Added setting position while duplicating file handles with FMF_WRITE mode. It's neede...
[cake.git] / compiler / clib / __vcformat.c
blob1fc7c1e43908b5192414a54f9d5f7d00fa14fe22
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
5 Function to format a string like printf().
6 */
8 #define __vcformat __vcformat
10 /* Original source from libnix */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #ifndef AROS_NO_LIMITS_H
16 # include <limits.h>
17 #else
18 # define ULONG_MAX 4294967295UL
19 #endif
20 #include <ctype.h>
21 #include "__math.h"
22 #include <math.h>
23 #include <float.h>
25 #include <aros/config.h>
27 /* Prevent 'missing math function' problem on AROSfA */
28 #if !(AROS_FLAVOUR & AROS_FLAVOUR_NATIVE)
29 #define FULL_SPECIFIERS
30 #endif
32 #ifdef AROS_NOFPU
33 # ifdef FULL_SPECIFIERS
34 # undef FULL_SPECIFIERS
35 # endif
36 #endif
38 #ifndef BITSPERBYTE
39 # define BITSPERBYTE 8
40 #endif
42 #if (__WORDSIZE != 64) && defined(AROSC_STATIC)
43 #undef AROS_HAVE_LONG_LONG
44 #endif
46 /* a little macro to make life easier */
47 #define OUT(c) do \
48 { if((*outc)((c),data)==EOF) \
49 return outcount; \
50 outcount++; \
51 }while(0)
53 #define MINFLOATSIZE (DBL_DIG+1) /* Why not 1 more - it's 97% reliable */
54 #define MININTSIZE (sizeof(unsigned long)*BITSPERBYTE/3+1)
55 #define MINPOINTSIZE (sizeof(void *)*BITSPERBYTE/4+1)
56 #define REQUIREDBUFFER (MININTSIZE>MINPOINTSIZE? \
57 (MININTSIZE>MINFLOATSIZE?MININTSIZE:MINFLOATSIZE): \
58 (MINPOINTSIZE>MINFLOATSIZE?MINPOINTSIZE:MINFLOATSIZE))
60 #define ALTERNATEFLAG 1 /* '#' is set */
61 #define ZEROPADFLAG 2 /* '0' is set */
62 #define LALIGNFLAG 4 /* '-' is set */
63 #define BLANKFLAG 8 /* ' ' is set */
64 #define SIGNFLAG 16 /* '+' is set */
66 const unsigned char *const __decimalpoint = ".";
68 /*****************************************************************************
70 NAME */
72 int __vcformat (
74 /* SYNOPSIS */
75 void * data,
76 int (* outc)(int, void *),
77 const char * format,
78 va_list args)
80 /* FUNCTION
81 Format a list of arguments and call a function for each char
82 to print.
84 INPUTS
85 data - This is passed to the usercallback outc
86 outc - Call this function for every character that should be
87 emitted. The function should return EOF on error and
88 > 0 otherwise.
89 format - A printf() format string.
90 args - A list of arguments for the format string.
92 RESULT
93 The number of characters written.
95 NOTES
97 EXAMPLE
99 BUGS
101 SEE ALSO
103 INTERNALS
105 ******************************************************************************/
107 size_t outcount=0;
109 while(*format)
111 if(*format=='%')
113 static const char flagc[]=
114 { '#','0','-',' ','+' };
115 static const char lowertabel[]=
116 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
117 static const char uppertabel[]=
118 { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
119 size_t width=0,preci=ULONG_MAX,flags=0; /* Specifications */
120 char type,subtype='i';
121 #ifdef AROS_HAVE_LONG_LONG
122 char lltype=0;
123 #endif
124 char buffer1[2]; /* Signs and that like */
125 char buffer[REQUIREDBUFFER]; /* The body */
126 char *buffer2=buffer; /* So we can set this to any other strings */
127 size_t size1=0,size2=0; /* How many chars in buffer? */
128 const char *ptr=format+1; /* pointer to format string */
129 size_t i,pad; /* Some temporary variables */
131 do /* read flags */
132 for(i=0;i<sizeof(flagc);i++)
133 if(flagc[i]==*ptr)
134 { flags|=1<<i;
135 ptr++;
136 break; }
137 while(i<sizeof(flagc));
139 if(*ptr=='*') /* read width from arguments */
140 { signed int a;
141 ptr++;
142 a=va_arg(args,signed int);
143 if(a<0)
144 { flags|=LALIGNFLAG;
145 width=-a; }
146 else
147 width=a;
148 }else
149 while(isdigit(*ptr))
150 width=width*10+(*ptr++-'0');
152 if(*ptr=='.')
153 { ptr++;
154 if(*ptr=='*') /* read precision from arguments */
155 { signed int a;
156 ptr++;
157 a=va_arg(args,signed int);
158 if(a>=0)
159 preci=a;
160 }else
161 { preci=0;
162 while(isdigit(*ptr))
163 preci=preci*10+(*ptr++-'0');
167 if(*ptr=='h'||*ptr=='l'||*ptr=='L')
168 subtype=*ptr++;
170 #ifdef AROS_HAVE_LONG_LONG
171 if(*ptr=='l')
173 lltype=1;
174 ptr++;
176 #endif
178 type=*ptr++;
180 switch(type)
181 { case 'd':
182 case 'i':
183 case 'o':
184 case 'p':
185 case 'u':
186 case 'x':
187 case 'X':
189 #ifdef AROS_HAVE_LONG_LONG
190 unsigned long long v;
191 #else
192 unsigned long v;
193 #endif
194 const char *tabel;
195 int base;
197 if(type=='p')
198 { subtype='l'; /* This is written as %#lx */
199 type='x';
200 flags|=ALTERNATEFLAG; }
202 if(type=='d'||type=='i') /* These are signed */
204 #ifdef AROS_HAVE_LONG_LONG
205 signed long long v2;
206 #else
207 signed long v2;
208 #endif
209 if(subtype=='l')
210 #ifdef AROS_HAVE_LONG_LONG
211 { if(lltype)
212 v2=va_arg(args,signed long long);
213 else
214 v2=va_arg(args,signed long); }
215 #else
216 v2=va_arg(args,signed long);
217 #endif
218 else
219 v2=va_arg(args,signed int);
220 if(v2<0)
221 { buffer1[size1++]='-';
222 v=-v2;
223 }else
224 { if(flags&SIGNFLAG)
225 buffer1[size1++]='+';
226 else if(flags&BLANKFLAG)
227 buffer1[size1++]=' ';
228 v=v2; }
229 }else /* These are unsigned */
230 { if(subtype=='l')
231 #ifdef AROS_HAVE_LONG_LONG
232 { if(lltype)
233 v=va_arg(args,unsigned long long);
234 else
235 v=va_arg(args,unsigned long); }
236 #else
237 v=va_arg(args,unsigned long);
238 #endif
239 else
240 v=va_arg(args,unsigned int);
241 if(flags&ALTERNATEFLAG)
242 { if(type=='o'&&(preci&&v))
243 buffer1[size1++]='0';
244 if((type=='x'||type=='X')&&v)
245 { buffer1[size1++]='0';
246 buffer1[size1++]=type; }
250 buffer2=&buffer[sizeof(buffer)]; /* Calculate body string */
251 base=type=='x'||type=='X'?16:(type=='o'?8:10);
252 tabel=type!='X'?lowertabel:uppertabel;
254 { *--buffer2=tabel[v%base];
255 v=v/base;
256 size2++;
257 }while(v);
258 if(preci==ULONG_MAX) /* default */
259 preci=0;
260 else
261 flags&=~ZEROPADFLAG;
262 break;
264 case 'c':
265 if(subtype=='l')
266 #ifdef AROS_HAVE_LONG_LONG
267 { if(lltype)
268 buffer2=va_arg(args,long long);
269 else
270 buffer2=va_arg(args,long); }
271 #else
272 *buffer2=va_arg(args,long);
273 #endif
274 else
275 *buffer2=va_arg(args,int);
276 size2=1;
277 preci=0;
278 break;
279 case 's':
280 buffer2=va_arg(args,char *);
281 if (!buffer2)
282 buffer2="(null)";
283 size2=strlen(buffer2);
284 size2=size2<=preci?size2:preci;
285 preci=0;
286 break;
287 #ifdef FULL_SPECIFIERS
288 case 'f':
289 case 'e':
290 case 'E':
291 case 'g':
292 case 'G':
293 { double v;
294 char killzeros=0,sign=0; /* some flags */
295 int ex1,ex2; /* Some temporary variables */
296 size_t size,dnum,dreq;
297 char *udstr=NULL;
299 v=va_arg(args,double);
301 if(isinf(v))
302 { if(v>0)
303 udstr="+inf";
304 else
305 udstr="-inf";
306 }else if(isnan(v))
307 udstr="NaN";
309 if(udstr!=NULL)
310 { size2=strlen(udstr);
311 preci=0;
312 buffer2=udstr;
313 break; }
315 if(preci==ULONG_MAX) /* old default */
316 preci=6; /* new default */
318 if(v<0.0)
319 { sign='-';
320 v=-v;
321 }else
322 { if(flags&SIGNFLAG)
323 sign='+';
324 else if(flags&BLANKFLAG)
325 sign=' ';
328 ex1=0;
329 if(v!=0.0)
330 { ex1=log10(v);
331 if(v<1.0)
332 v=v*pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
333 else
334 v=v/pow(10,ex1);
335 if(v<1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
336 { v*=10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
337 ex1--; } /* The case too high (log(10.)=.999999999) is done later */
340 ex2=preci;
341 if(type=='f')
342 ex2+=ex1;
343 if(tolower(type)=='g')
344 ex2--;
345 v+=.5/pow(10,ex2<MINFLOATSIZE?ex2:MINFLOATSIZE); /* Round up */
347 if(v>=10.0) /* Adjusts log10(10.)=.999999999 too */
348 { v/=10.0;
349 ex1++; }
351 if(tolower(type)=='g') /* This changes to one of the other types */
352 { if(ex1<(signed long)preci&&ex1>=-4)
353 { type='f';
354 preci-=ex1;
355 }else
356 type=type=='g'?'e':'E';
357 preci--;
358 if(!(flags&ALTERNATEFLAG))
359 killzeros=1; /* set flag to kill trailing zeros */
362 dreq=preci+1; /* Calculate number of decimal places required */
363 if(type=='f')
364 dreq+=ex1; /* even more before the decimal point */
366 dnum=0;
367 while(dnum<dreq&&dnum<MINFLOATSIZE) /* Calculate all decimal places needed */
368 { buffer[dnum++]=(char)v+'0';
369 v=(v-(double)(char)v)*10.0; }
371 if(killzeros) /* Kill trailing zeros if possible */
372 while(preci&&(dreq-->dnum||buffer[dreq]=='0'))
373 preci--;
375 if(type=='f')/* Calculate actual size of string (without sign) */
376 { size=preci+1; /* numbers after decimal point + 1 before */
377 if(ex1>0)
378 size+=ex1; /* numbers >= 10 */
379 if(preci||flags&ALTERNATEFLAG)
380 size++; /* 1 for decimal point */
381 }else
382 { size=preci+5; /* 1 for the number before the decimal point, and 4 for the exponent */
383 if(preci||flags&ALTERNATEFLAG)
384 size++;
385 if(ex1>99||ex1<-99)
386 size++; /* exponent needs an extra decimal place */
389 pad=size+(sign!=0);
390 pad=pad>=width?0:width-pad;
392 if(sign&&flags&ZEROPADFLAG)
393 OUT(sign);
395 if(!(flags&LALIGNFLAG))
396 for(i=0;i<pad;i++)
397 OUT(flags&ZEROPADFLAG?'0':' ');
399 if(sign&&!(flags&ZEROPADFLAG))
400 OUT(sign);
402 dreq=0;
403 if(type=='f')
404 { if(ex1<0)
405 OUT('0');
406 else
407 while(ex1>=0)
408 { OUT(dreq<dnum?buffer[dreq++]:'0');
409 ex1--; }
410 if(preci||flags&ALTERNATEFLAG)
411 { OUT(__decimalpoint[0]);
412 while(preci--)
413 if(++ex1<0)
414 OUT('0');
415 else
416 OUT(dreq<dnum?buffer[dreq++]:'0');
418 }else
419 { OUT(buffer[dreq++]);
420 if(preci||flags&ALTERNATEFLAG)
421 { OUT(__decimalpoint[0]);
422 while(preci--)
423 OUT(dreq<dnum?buffer[dreq++]:'0');
425 OUT(type);
426 if(ex1<0)
427 { OUT('-');
428 ex1=-ex1; }
429 else
430 OUT('+');
431 if(ex1>99)
432 OUT(ex1/100+'0');
433 OUT(ex1/10%10+'0');
434 OUT(ex1%10+'0');
437 if(flags&LALIGNFLAG)
438 for(i=0;i<pad;i++)
439 OUT(' ');
441 width=preci=0; /* Everything already done */
442 break;
444 #endif
445 case '%':
446 buffer2="%";
447 size2=1;
448 preci=0;
449 break;
450 case 'n':
451 *va_arg(args,int *)=outcount;
452 width=preci=0;
453 break;
454 default:
455 if(!type)
456 ptr--; /* We've gone too far - step one back */
457 buffer2=(char *)format;
458 size2=ptr-format;
459 width=preci=0;
460 break;
462 pad=size1+(size2>=preci?size2:preci); /* Calculate the number of characters */
463 pad=pad>=width?0:width-pad; /* and the number of resulting pad bytes */
465 if(flags&ZEROPADFLAG) /* print sign and that like */
466 for(i=0;i<size1;i++)
467 OUT(buffer1[i]);
469 if(!(flags&LALIGNFLAG)) /* Pad left */
470 for(i=0;i<pad;i++)
471 OUT(flags&ZEROPADFLAG?'0':' ');
473 if(!(flags&ZEROPADFLAG)) /* print sign if not zero padded */
474 for(i=0;i<size1;i++)
475 OUT(buffer1[i]);
477 for(i=size2;i<preci;i++) /* extend to precision */
478 OUT('0');
480 for(i=0;i<size2;i++) /* print body */
481 OUT(buffer2[i]);
483 if(flags&LALIGNFLAG) /* Pad right */
484 for(i=0;i<pad;i++)
485 OUT(' ');
487 format=ptr;
489 else
490 OUT(*format++);
492 return outcount;