detect if the compiler supports -fno-builtin and -fno-builtin-vsnprintf. (NicJA)
[AROS.git] / compiler / fmtprintf / fmtprintf.c
blob3338b43ce8941018c35719f8f2f94fa4c33bca75
1 /*
2 Copyright © 2018, The AROS Development Team. All rights reserved.
3 $Id:$
5 Common code block to format a string like printf().
6 */
8 /*
9 * This code is used by debug functions during early startup.
10 * Please keep it self-contained, at least when compiled with -DSTDC_STATIC.
13 size_t outcount=0;
15 while (*format)
17 if (*format == '%')
19 static const char flagc[] = { '#', '0', '-', ' ', '+' };
20 size_t width=0, preci=ULONG_MAX, flags=0; /* Specifications */
21 char type, subtype = 'i';
22 #ifdef AROS_HAVE_LONG_LONG
23 char lltype=0;
24 #endif
25 char buffer1[2]; /* Signs and that like */
26 char buffer[REQUIREDBUFFER]; /* The body */
27 char *buffer2 = buffer; /* So we can set this to any other strings */
28 size_t size1 = 0, size2 = 0; /* How many chars in buffer? */
29 const char *ptr = format + 1; /* pointer to format string */
30 size_t i, pad; /* Some temporary variables */
31 #if defined(FULL_SPECIFIERS)
32 union { /* floating point arguments %[aAeEfFgG] */
33 double dbl;
34 long double ldbl;
35 } fparg;
36 #endif
37 do /* read flags */
38 for (i = 0; i < sizeof(flagc); i++)
39 if (flagc[i] == *ptr)
41 flags |= 1<<i;
42 ptr++;
43 break;
45 while (i < sizeof(flagc));
47 if (*ptr == '*') /* read width from arguments */
49 signed int a;
50 ptr++;
51 a = va_arg(args, signed int);
52 if (a < 0)
54 flags |= LALIGNFLAG;
55 width = -a;
57 else
58 width = a;
59 }else
60 while (isdigit(*ptr))
61 width = width * 10 + (*ptr++ - '0');
63 if (*ptr == '.')
65 ptr++;
66 if (*ptr == '*') /* read precision from arguments */
68 signed int a;
69 ptr++;
70 a = va_arg(args, signed int);
71 if (a >= 0)
72 preci = a;
73 }else
75 preci = 0;
76 while (isdigit(*ptr))
77 preci = preci * 10 + (*ptr++ - '0');
81 if (*ptr == 'h' || *ptr == 'l' || *ptr == 'L' || *ptr == 'z')
82 subtype=*ptr++;
84 if (*ptr == 'l' || *ptr == 'q')
86 #ifdef AROS_HAVE_LONG_LONG
87 lltype = 1;
88 subtype = 'l';
89 #endif
90 ptr++;
93 type = *ptr++;
95 switch(type)
97 case 'd':
98 case 'i':
99 case 'o':
100 case 'p':
101 case 'u':
102 case 'x':
103 case 'X':
105 #ifdef AROS_HAVE_LONG_LONG
106 unsigned long long llv = 0;
107 #endif
108 unsigned long v = 0;
109 int base = 10;
111 if (type=='p') /* This is written as 0x08lx (or 0x016lx on 64 bits) */
113 subtype = 'l';
114 type = 'x';
115 if (!width)
116 width = sizeof(void *) * 2;
117 flags |= ZEROPADFLAG;
120 if (type=='d' || type=='i') /* These are signed */
122 signed long v2;
124 if (subtype=='l')
126 #ifdef AROS_HAVE_LONG_LONG
127 if (lltype)
129 signed long long llv2;
131 llv2 = va_arg(args, signed long long);
132 if (llv2 < 0)
134 llv = - llv2;
135 v2 = -1; /* Assign a dummy value to v2 in order to process sign below */
137 else
139 llv = llv2;
140 v2 = llv2 ? 1 : 0;
143 else
144 #endif
145 v2=va_arg(args, signed long);
147 else if (subtype == 'z')
148 v2 = va_arg(args, size_t);
149 else
150 v2 = va_arg(args, signed int);
152 if (v2 < 0)
154 buffer1[size1++] = '-';
155 v = -v2;
157 else
159 if (flags & SIGNFLAG)
160 buffer1[size1++] = '+';
161 else if (flags & BLANKFLAG)
162 buffer1[size1++] = ' ';
163 v = v2;
166 else /* These are unsigned */
168 if (subtype=='l')
170 #ifdef AROS_HAVE_LONG_LONG
171 if (lltype)
172 llv = va_arg(args, unsigned long long);
173 else
174 #endif
175 v = va_arg(args, unsigned long);
177 else if (subtype == 'z')
178 v = va_arg(args, size_t);
179 else
180 v = va_arg(args, unsigned int);
182 if (flags & ALTERNATEFLAG)
184 if (type == 'o' && preci && v)
185 buffer1[size1++] = '0';
186 if ((type == 'x' || type == 'X') && v)
188 buffer1[size1++] = '0';
189 buffer1[size1++] = type;
194 buffer2 = &buffer[sizeof(buffer)]; /* Calculate body string */
196 #ifdef AROS_HAVE_LONG_LONG
198 * For long long type we have actual value in llv.
199 * For long we have actual value in v.
200 * This avoids slow 64-bit operations on 32-bit processors
201 * when not needed.
203 if (lltype)
204 size2 = format_longlong(buffer2, type, base, llv);
205 else
206 #endif
207 size2 = format_long(buffer2, type, base, v);
208 /* Position to the beginning of the string */
209 buffer2 -= size2;
211 if (preci == ULONG_MAX) /* default */
212 preci = 0;
213 else
214 flags &= ~ZEROPADFLAG;
215 break;
218 case 'c':
219 if (subtype=='l')
221 #ifdef AROS_HAVE_LONG_LONG
222 if (lltype)
223 *buffer2 = va_arg(args, long long);
224 else
225 #endif
226 *buffer2 = va_arg(args, long);
228 else
229 *buffer2 = va_arg(args, int);
231 size2 = 1;
232 preci = 0;
233 break;
235 case 's':
236 buffer2 = va_arg(args, char *);
237 if (!buffer2)
238 buffer2 = "(null)";
239 size2 = FMTPRINTF_STRLEN(buffer2);
240 size2 = size2 <= preci ? size2 : preci;
241 preci = 0;
242 break;
244 case 'b':
245 buffer2 = BADDR(va_arg(args, BPTR));
246 if (buffer2)
247 #if AROS_FAST_BSTR
248 size2 = FMTPRINTF_STRLEN(buffer2);
249 #else
250 size2 = *(unsigned char *)buffer2++;
251 #endif
252 else
254 buffer2 = "(null)";
255 size2 = 6;
258 size2 = size2 <= preci ? size2 : preci;
259 preci = 0;
260 break;
262 #ifdef FULL_SPECIFIERS
263 case 'a':
264 case 'A':
265 case 'f':
266 case 'F':
267 case 'e':
268 case 'E':
269 case 'g':
270 case 'G':
272 char killzeros=0, sign=0; /* some flags */
273 int ex1, ex2; /* Some temporary variables */
274 size_t size, dnum, dreq;
275 char *udstr = NULL;
277 if (subtype == 'L')
279 flags |= LDBLFLAG;
280 fparg.ldbl = va_arg(args, long double);
281 } else {
282 flags &= ~LDBLFLAG;
283 fparg.dbl = va_arg(args, double);
286 if (isinf(fparg.dbl))
288 if (fparg.dbl>0)
289 udstr = "+inf";
290 else
291 udstr = "-inf";
292 } else if (isnan(fparg.dbl))
293 udstr = "NaN";
295 if (udstr != NULL)
297 size2 = FMTPRINTF_STRLEN(udstr);
298 preci = 0;
299 buffer2 = udstr;
300 break; }
302 if (preci == ULONG_MAX) /* old default */
303 preci = 6; /* new default */
305 if (((subtype != 'L') && (fparg.dbl < 0.0)) || ((subtype == 'L') && (fparg.ldbl < 0.0)))
307 sign = '-';
308 if (subtype == 'L')
309 fparg.ldbl = -fparg.ldbl;
310 else
311 fparg.dbl = -fparg.dbl;
312 } else {
313 if (flags & SIGNFLAG)
314 sign = '+';
315 else if (flags & BLANKFLAG)
316 sign = ' ';
319 ex1 = 0;
320 if (tolower(type) != 'a')
322 if (subtype != 'L')
324 if (fparg.dbl != 0.0)
326 ex1 = log10(fparg.dbl);
327 if (fparg.dbl < 1.0)
328 fparg.dbl = fparg.dbl * pow(10,- --ex1); /* Caution: (int)log10(.5)!=-1 */
329 else
330 fparg.dbl=fparg.dbl / pow(10, ex1);
331 if (fparg.dbl < 1.0) /* adjust if we are too low (log10(.1)=-.999999999) */
333 fparg.dbl *= 10.0; /* luckily this cannot happen with FLT_MAX and FLT_MIN */
334 ex1--; /* The case too high (log(10.)=.999999999) is done later */
337 } else {
338 if (fparg.ldbl != 0.0)
340 ex1 = log10l(fparg.ldbl);
341 if (fparg.ldbl < 1.0)
342 fparg.ldbl= fparg.ldbl * powl(10,- --ex1);
343 else
344 fparg.ldbl=fparg.ldbl / powl(10, ex1);
345 if (fparg.ldbl < 1.0)
347 fparg.ldbl *= 10.0;
348 ex1--;
354 ex2 = preci;
355 if (tolower(type) == 'f')
356 ex2 += ex1;
357 if (tolower(type) == 'g')
358 ex2--;
359 if (subtype != 'L')
361 fparg.dbl += .5 / pow(10, ex2 < MINFLOATSIZE ? ex2 : MINFLOATSIZE); /* Round up */
362 if (fparg.dbl >= 10.0) /* Adjusts log10(10.)=.999999999 too */
364 fparg.dbl /= 10.0;
365 ex1++;
367 } else {
368 fparg.ldbl += .5 / powl(10, ex2 < MINFLOATSIZE ? ex2 : MINFLOATSIZE); /* Round up */
369 if (fparg.ldbl >= 10.0) /* Adjusts log10(10.)=.999999999 too */
371 fparg.ldbl /= 10.0;
372 ex1++;
376 if (tolower(type) == 'g') /* This changes to one of the other types */
378 if (ex1 < (signed long)preci && ex1 >= -4)
380 type = 'f';
381 preci -= ex1;
382 } else
383 type = type == 'g' ? 'e' : 'E';
384 preci--;
385 if (!(flags & ALTERNATEFLAG))
386 killzeros = 1; /* set flag to kill trailing zeros */
389 dreq = preci + 1; /* Calculate number of decimal places required */
390 if (type == 'f')
391 dreq += ex1; /* even more before the decimal point */
393 dnum = 0;
394 while (dnum < dreq && dnum < MINFLOATSIZE) /* Calculate all decimal places needed */
396 if (subtype != 'L')
398 buffer[dnum++] = (char)fparg.dbl + '0';
399 fparg.dbl = (fparg.dbl - (double)(char)fparg.dbl) * 10.0;
400 } else {
401 buffer[dnum++] = (char)fparg.ldbl + '0';
402 fparg.ldbl = (fparg.ldbl - (long double)(char)fparg.ldbl) * 10.0;
405 if (killzeros) /* Kill trailing zeros if possible */
406 while(preci && (dreq-- > dnum || buffer[dreq] == '0'))
407 preci--;
409 if (tolower(type) == 'f') /* Calculate actual size of string (without sign) */
411 size = preci + 1; /* numbers after decimal point + 1 before */
412 if (ex1 > 0)
413 size += ex1; /* numbers >= 10 */
414 if (preci || flags & ALTERNATEFLAG)
415 size++; /* 1 for decimal point */
416 }else
418 size = preci + 5; /* 1 for the number before the decimal point, and 4 for the exponent */
419 if (preci || flags & ALTERNATEFLAG)
420 size++;
421 if (ex1 > 99 || ex1 < -99)
422 size++; /* exponent needs an extra decimal place */
425 pad = size + (sign != 0);
426 pad = pad >= width ? 0 : width - pad;
428 if (sign && flags & ZEROPADFLAG)
429 FMTPRINTF_COUT(sign);
431 if (!(flags & LALIGNFLAG))
432 for (i = 0; i < pad; i++)
433 FMTPRINTF_COUT(flags & ZEROPADFLAG ? '0' : ' ');
435 if (sign && !(flags & ZEROPADFLAG))
436 FMTPRINTF_COUT(sign);
438 dreq = 0;
439 if (tolower(type) == 'a')
441 // TODO: Implement hexfloat literals
442 FMTPRINTF_COUT('0');
443 FMTPRINTF_COUT('x');
444 FMTPRINTF_COUT('0');
445 FMTPRINTF_COUT('.');
446 FMTPRINTF_COUT('0');
447 if (type=='A')
448 FMTPRINTF_COUT('P');
449 else
450 FMTPRINTF_COUT('p');
451 FMTPRINTF_COUT('0');
452 } else if (tolower(type) == 'f') {
453 if (ex1 < 0)
454 FMTPRINTF_COUT('0');
455 else
456 while(ex1 >= 0)
458 FMTPRINTF_COUT(dreq < dnum ? buffer[dreq++] : '0');
459 ex1--;
461 if (preci || flags & ALTERNATEFLAG)
463 FMTPRINTF_COUT(FMTPRINTF_DECIMALPOINT[0]);
464 while(preci--)
465 if (++ex1 < 0)
466 FMTPRINTF_COUT('0');
467 else
468 FMTPRINTF_COUT(dreq < dnum ? buffer[dreq++] : '0');
470 } else {
471 FMTPRINTF_COUT(buffer[dreq++]);
472 if (preci || flags & ALTERNATEFLAG)
474 FMTPRINTF_COUT(FMTPRINTF_DECIMALPOINT[0]);
475 while(preci--)
476 FMTPRINTF_COUT(dreq < dnum ? buffer[dreq++] : '0');
478 FMTPRINTF_COUT(type);
479 if (ex1 < 0)
481 FMTPRINTF_COUT('-');
482 ex1 = -ex1;
484 else
485 FMTPRINTF_COUT('+');
486 if (ex1 > 99)
487 FMTPRINTF_COUT(ex1 / 100 + '0');
488 FMTPRINTF_COUT(ex1 / 10 % 10 + '0');
489 FMTPRINTF_COUT(ex1 % 10 + '0');
492 if (flags & LALIGNFLAG)
493 for (i = 0; i < pad; i++)
494 FMTPRINTF_COUT(' ');
496 width = preci = 0; /* Everything already done */
497 break;
499 #endif
500 case '%':
501 buffer2 = "%";
502 size2 = 1;
503 preci = 0;
504 break;
505 case 'n':
506 *va_arg(args, int *) = outcount;
507 width = preci = 0;
508 break;
509 default:
510 if (!type)
511 ptr--; /* We've gone too far - step one back */
512 buffer2 = (char *)format;
513 size2 = ptr - format;
514 width = preci = 0;
515 break;
517 pad = size1 + (size2 >= preci ? size2 : preci); /* Calculate the number of characters */
518 pad = pad >= width ? 0 : width - pad; /* and the number of resulting pad bytes */
520 if (flags & ZEROPADFLAG) /* print sign and that like */
521 for (i = 0; i < size1; i++)
522 FMTPRINTF_COUT(buffer1[i]);
524 if (!(flags & LALIGNFLAG)) /* Pad left */
525 for (i = 0; i < pad; i++)
526 FMTPRINTF_COUT(flags & ZEROPADFLAG ? '0' : ' ');
528 if (!(flags & ZEROPADFLAG)) /* print sign if not zero padded */
529 for (i = 0; i < size1; i++)
530 FMTPRINTF_COUT(buffer1[i]);
532 for (i = size2; i < preci; i++) /* extend to precision */
533 FMTPRINTF_COUT('0');
535 for (i = 0; i < size2; i++) /* print body */
536 FMTPRINTF_COUT(buffer2[i]);
538 if (flags & LALIGNFLAG) /* Pad right */
539 for (i = 0; i < pad; i++)
540 FMTPRINTF_COUT(' ');
542 format = ptr;
544 else
545 FMTPRINTF_COUT(*format++);