1 /*
2 ** zstrformat.cpp
3 ** Routines for generic printf-style formatting.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2005-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** Portions of this file relating to printing floating point numbers
34 ** are covered by the following copyright:
35 **
36 **---------------------------------------------------------------------------
37 ** Copyright (c) 1990, 1993
38 ** The Regents of the University of California. All rights reserved.
39 **
40 ** This code is derived from software contributed to Berkeley by
41 ** Chris Torek.
42 **
43 ** Redistribution and use in source and binary forms, with or without
44 ** modification, are permitted provided that the following conditions
45 ** are met:
46 ** 1. Redistributions of source code must retain the above copyright
47 ** notice, this list of conditions and the following disclaimer.
48 ** 2. Redistributions in binary form must reproduce the above copyright
49 ** notice, this list of conditions and the following disclaimer in the
50 ** documentation and/or other materials provided with the distribution.
51 ** 4. Neither the name of the University nor the names of its contributors
52 ** may be used to endorse or promote products derived from this software
53 ** without specific prior written permission.
54 **
55 ** THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 ** ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 ** SUCH DAMAGE.
66 **
67 **---------------------------------------------------------------------------
68 **
69 ** Even though the standard C library has a function to do printf-style
70 ** formatting in a generic way, there is no standard interface to this
71 ** function. So if you want to do some printf formatting that doesn't fit in
72 ** the context of the provided functions, you need to roll your own. Why is
73 ** that?
74 **
75 ** Maybe Microsoft wants you to write a better one yourself? When used as
76 ** part of a sprintf replacement, this function is significantly faster than
77 ** Microsoft's offering. When used as part of a fprintf replacement, this
78 ** function turns out to be slower, but that's probably because the CRT's
79 ** fprintf can interact with the FILE object on a low level for better
80 ** perfomance. If you sprintf into a buffer and then fwrite that buffer, this
81 ** routine wins again, though the difference isn't great.
82 */
83
84 #include <limits.h>
85 #include <string.h>
86 #include <stddef.h>
87 #include <stdlib.h>
88 #include <math.h>
89 #include <stdlib.h>
90 #include <locale.h>
91
92 #include "zstring.h"
93 #include "gdtoa.h"
94
95 #ifndef _MSC_VER
96 #include <stdint.h>
97 #else
98 typedef unsigned __int64 uint64_t;
99 typedef signed __int64 int64_t;
100 #endif
101
102 /*
103 * MAXEXPDIG is the maximum number of decimal digits needed to store a
104 * floating point exponent in the largest supported format. It should
105 * be ceil(log10(LDBL_MAX_10_EXP)) or, if hexadecimal floating point
106 * conversions are supported, ceil(log10(LDBL_MAX_EXP)). But since it
107 * is presently never greater than 5 in practice, we fudge it.
108 */
109 #define MAXEXPDIG 6
110 #if LDBL_MAX_EXP > 999999
111 #error "floating point buffers too small"
112 #endif
113
114 #define DEFPREC 6
115
116 static const char hexits[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
117 static const char HEXits[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
118 static const char spaces[16] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
119 static const char zeroes[17] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','.'};
120 static const char dotchar = '.';
121
122 namespace StringFormat
123 {
124 static int writepad (OutputFunc output, void *outputData, const char *pad, int padsize, int spaceToFill);
125 static int printandpad (OutputFunc output, void *outputData, const char *p, const char *ep, int len, const char *with, int padsize);
126 static int exponent (char *p0, int exp, int fmtch);
127
Worker(OutputFunc output,void * outputData,const char * fmt,...)128 int Worker (OutputFunc output, void *outputData, const char *fmt, ...)
129 {
130 va_list arglist;
131 int len;
132
133 va_start (arglist, fmt);
134 len = VWorker (output, outputData, fmt, arglist);
135 va_end (arglist);
136 return len;
137 }
138
VWorker(OutputFunc output,void * outputData,const char * fmt,va_list arglist)139 int VWorker (OutputFunc output, void *outputData, const char *fmt, va_list arglist)
140 {
141 const char *c;
142 const char *base;
143 int len = 0;
144 int width;
145 int precision;
146 int flags;
147
148 base = c = fmt;
149 for (;;)
150 {
151 while (*c && *c != '%')
152 {
153 ++c;
154 }
155 if (*c == '\0')
156 {
157 return len + output (outputData, base, int(c - base));
158 }
159
160 if (c - base > 0)
161 {
162 len += output (outputData, base, int(c - base));
163 }
164 c++;
165
166 // Gather the flags, if any
167 for (flags = 0;; ++c)
168 {
169 if (*c == '-')
170 {
171 flags |= F_MINUS; // bit 0
172 }
173 else if (*c == '+')
174 {
175 flags |= F_PLUS; // bit 1
176 }
177 else if (*c == '0')
178 {
179 flags |= F_ZERO; // bit 2
180 }
181 else if (*c == ' ')
182 {
183 flags |= F_BLANK; // bit 3
184 }
185 else if (*c == '#')
186 {
187 flags |= F_HASH; // bit 4
188 }
189 else
190 {
191 break;
192 }
193 }
194
195 width = precision = -1;
196
197 // Read the width, if any
198 if (*c == '*')
199 {
200 ++c;
201 width = va_arg (arglist, int);
202 if (width < 0)
203 { // Negative width means minus flag and positive width
204 flags |= F_MINUS;
205 width = -width;
206 }
207 }
208 else if (*c >= '0' && *c <= '9')
209 {
210 width = *c++ - '0';
211 while (*c >= '0' && *c <= '9')
212 {
213 width = width * 10 + *c++ - '0';
214 }
215 }
216
217 // If 0 and - both appear, 0 is ignored.
218 // If the blank and + both appear, the blank is ignored.
219 flags &= ~((flags & 3) << 2);
220
221 // Read the precision, if any
222 if (*c == '.')
223 {
224 precision = 0;
225 if (*++c == '*')
226 {
227 ++c;
228 precision = va_arg (arglist, int);
229 }
230 else if (*c >= '0' && *c <= '9')
231 {
232 precision = *c++ - '0';
233 while (*c >= '0' && *c <= '9')
234 {
235 precision = precision * 10 + *c++ - '0';
236 }
237 }
238 }
239
240 // Read the size prefix, if any
241 if (*c == 'h')
242 {
243 if (*++c == 'h')
244 {
245 flags |= F_HALFHALF;
246 ++c;
247 }
248 else
249 {
250 flags |= F_HALF;
251 }
252 }
253 else if (*c == 'l')
254 {
255 if (*++c == 'l')
256 {
257 flags |= F_LONGLONG;
258 ++c;
259 }
260 else
261 {
262 flags |= F_LONG;
263 }
264 }
265 else if (*c == 'I')
266 {
267 if (*++c == '6')
268 {
269 if (*++c == '4')
270 {
271 flags |= F_LONGLONG;
272 ++c;
273 }
274 }
275 else
276 {
277 flags |= F_BIGI;
278 }
279 }
280 else if (*c == 't')
281 {
282 flags |= F_PTRDIFF;
283 ++c;
284 }
285 else if (*c == 'z')
286 {
287 flags |= F_SIZE;
288 ++c;
289 }
290
291 base = c+1;
292
293 // Now that that's all out of the way, we should be pointing at the type specifier
294 {
295 char prefix[3];
296 int prefixlen;
297 char hexprefix = '\0';
298 char sign = '\0';
299 int postprefixzeros = 0;
300 int size = flags & 0xF000;
301 char buffer[80], *ibuff;
302 const char *obuff = 0;
303 char type = *c++;
304 int bufflen = 0;
305 int outlen = 0;
306 unsigned int intarg = 0;
307 uint64_t int64arg = 0;
308 const void *voidparg;
309 const char *charparg;
310 double dblarg;
311 const char *xits = hexits;
312 int inlen = len;
313 /*
314 * We can decompose the printed representation of floating
315 * point numbers into several parts, some of which may be empty:
316 *
317 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
318 * A B ---C--- D E F
319 *
320 * A: 'sign' holds this value if present; '\0' otherwise
321 * B: hexprefix holds the 'x' or 'X'; '\0' if not hexadecimal
322 * C: obuff points to the string MMMNNN. Leading and trailing
323 * zeros are not in the string and must be added.
324 * D: expchar holds this character; '\0' if no exponent, e.g. %f
325 * F: at least two digits for decimal, at least one digit for hex
326 */
327 const char *decimal_point = ".";/* locale specific decimal point */
328 int signflag; /* true if float is negative */
329 int expt; /* integer value of exponent */
330 char expchar = 'e'; /* exponent character: [eEpP\0] */
331 char *dtoaend; /* pointer to end of converted digits */
332 int expsize = 0; /* character count for expstr */
333 int ndig = 0; /* actual number of digits returned by dtoa */
334 char expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */
335 char *dtoaresult = NULL; /* buffer allocated by dtoa */
336
337 // Using a bunch of if/else if statements is faster than a switch, because a switch generates
338 // a jump table. A jump table means a possible data cache miss and a hefty penalty while the
339 // cache line is loaded.
340
341 if (type == 'x' || type == 'X' ||
342 type == 'p' ||
343 type == 'd' || type == 'u' || type == 'i' ||
344 type == 'o' ||
345 type == 'B')
346 {
347 if (type == 'X' || type == 'p')
348 {
349 xits = HEXits;
350 }
351 if (type == 'p')
352 {
353 type = 'X';
354 voidparg = va_arg (arglist, void *);
355 if (sizeof(void*) == sizeof(int))
356 {
357 intarg = (unsigned int)(size_t)voidparg;
358 precision = 8;
359 size = 0;
360 }
361 else
362 {
363 int64arg = (uint64_t)(size_t)voidparg;
364 precision = 16;
365 size = F_LONGLONG;
366 }
367 }
368 else
369 {
370 if (size == 0)
371 {
372 intarg = va_arg (arglist, int);
373 }
374 else if (size == F_HALFHALF)
375 {
376 intarg = va_arg (arglist, int);
377 intarg = (signed char)intarg;
378 }
379 else if (size == F_HALF)
380 {
381 intarg = va_arg (arglist, int);
382 intarg = (short)intarg;
383 }
384 else if (size == F_LONG)
385 {
386 if (sizeof(long) == sizeof(int)) intarg = va_arg (arglist, int);
387 else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
388 }
389 else if (size == F_BIGI)
390 {
391 if (sizeof(void*) == sizeof(int)) intarg = va_arg (arglist, int);
392 else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
393 }
394 else if (size == F_LONGLONG)
395 {
396 int64arg = va_arg (arglist, int64_t);
397 }
398 else if (size == F_PTRDIFF)
399 {
400 if (sizeof(ptrdiff_t) == sizeof(int)) intarg = va_arg (arglist, int);
401 else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
402 }
403 else if (size == F_SIZE)
404 {
405 if (sizeof(size_t) == sizeof(int)) intarg = va_arg (arglist, int);
406 else { int64arg = va_arg (arglist, int64_t); size = F_LONGLONG; }
407 }
408 else
409 {
410 intarg = va_arg (arglist, int);
411 }
412 }
413
414 if (precision < 0) precision = 1;
415
416 ibuff = &buffer[sizeof(buffer)];
417
418 if (size == F_LONGLONG)
419 {
420 if (int64arg == 0)
421 {
422 flags |= F_ZEROVALUE;
423 }
424 else
425 {
426 if (type == 'o')
427 { // Octal: Dump digits until it fits in an unsigned int
428 while (int64arg > UINT_MAX)
429 {
430 *--ibuff = char(int64arg & 7) + '0'; int64arg >>= 3;
431 }
432 intarg = int(int64arg);
433 }
434 else if (type == 'x' || type == 'X')
435 { // Hexadecimal: Dump digits until it fits in an unsigned int
436 while (int64arg > UINT_MAX)
437 {
438 *--ibuff = xits[int64arg & 15]; int64arg >>= 4;
439 }
440 intarg = int(int64arg);
441 }
442 else if (type == 'B')
443 { // Binary: Dump digits until it fits in an unsigned int
444 while (int64arg > UINT_MAX)
445 {
446 *--ibuff = char(int64arg & 1) + '0'; int64arg >>= 1;
447 }
448 intarg = int(int64arg);
449 }
450 else
451 {
452 if (type != 'u')
453 {
454 // If a signed number is negative, set the negative flag and make it positive.
455 int64_t sint64arg = (int64_t)int64arg;
456 if (sint64arg < 0)
457 {
458 flags |= F_NEGATIVE;
459 sint64arg = -sint64arg;
460 int64arg = sint64arg;
461 }
462 flags |= F_SIGNED;
463 type = 'u';
464 }
465 // If an unsigned int64 is too big to fit in an unsigned int, dump out
466 // digits until it is sufficiently small.
467 while (int64arg > INT_MAX)
468 {
469 *--ibuff = char(int64arg % 10) + '0'; int64arg /= 10;
470 }
471 intarg = (unsigned int)(int64arg);
472 }
473 }
474 }
475 else
476 {
477 if (intarg == 0)
478 {
479 flags |= F_ZEROVALUE;
480 }
481 else if (type == 'i' || type == 'd')
482 { // If a signed int is negative, set the negative flag and make it positive.
483 signed int sintarg = (signed int)intarg;
484 if (sintarg < 0)
485 {
486 flags |= F_NEGATIVE;
487 sintarg = -sintarg;
488 intarg = sintarg;
489 }
490 flags |= F_SIGNED;
491 type = 'u';
492 }
493 }
494 if (flags & F_ZEROVALUE)
495 {
496 if (precision != 0)
497 {
498 *--ibuff = '0';
499 }
500 }
501 else if (type == 'u')
502 { // Decimal
503 int i;
504
505 // Unsigned division is typically slower than signed division.
506 // Do it at most once.
507 if (intarg > INT_MAX)
508 {
509 *--ibuff = char(intarg % 10) + '0'; intarg /= 10;
510 }
511 i = (int)intarg;
512 while (i != 0)
513 {
514 *--ibuff = char(i % 10) + '0'; i /= 10;
515 }
516 }
517 else if (type == 'o')
518 { // Octal
519 while (intarg != 0)
520 {
521 *--ibuff = char(intarg & 7) + '0'; intarg >>= 3;
522 }
523 }
524 else if (type == 'B')
525 { // Binary
526 while (intarg != 0)
527 {
528 *--ibuff = char(intarg & 1) + '0'; intarg >>= 1;
529 }
530 }
531 else
532 { // Hexadecimal
533 while (intarg != 0)
534 {
535 *--ibuff = xits[intarg & 15]; intarg >>= 4;
536 }
537 }
538 // Check for prefix (only for non-decimal, which are always unsigned)
539 if ((flags & (F_HASH|F_ZEROVALUE)) == F_HASH)
540 {
541 if (type == 'o')
542 {
543 if (bufflen >= precision)
544 {
545 sign = '0';
546 }
547 }
548 else if (type == 'x' || type == 'X')
549 {
550 hexprefix = type;
551 }
552 else if (type == 'B')
553 {
554 hexprefix = '!';
555 }
556 }
557 bufflen = (int)(ptrdiff_t)(&buffer[sizeof(buffer)] - ibuff);
558 obuff = ibuff;
559 if (precision >= 0)
560 {
561 postprefixzeros = precision - bufflen;
562 if (postprefixzeros < 0) postprefixzeros = 0;
563 // flags &= ~F_ZERO;
564 }
565 }
566 else if (type == 'c')
567 {
568 intarg = va_arg (arglist, int);
569 buffer[0] = char(intarg);
570 bufflen = 1;
571 obuff = buffer;
572 }
573 else if (type == 's')
574 {
575 charparg = va_arg (arglist, const char *);
576 if (charparg == NULL)
577 {
578 obuff = "(null)";
579 bufflen = 6;
580 }
581 else
582 {
583 obuff = charparg;
584 if (precision < 0)
585 {
586 bufflen = (int)strlen (charparg);
587 }
588 else
589 {
590 for (bufflen = 0; bufflen < precision && charparg[bufflen] != '\0'; ++bufflen)
591 { /* empty */ }
592 }
593 }
594 }
595 else if (type == '%')
596 { // Just print a '%': Output it with the next stage.
597 base--;
598 continue;
599 }
600 else if (type == 'n')
601 {
602 if (size == F_HALFHALF)
603 {
604 *va_arg (arglist, char *) = (char)inlen;
605 }
606 else if (size == F_HALF)
607 {
608 *va_arg (arglist, short *) = (short)inlen;
609 }
610 else if (size == F_LONG)
611 {
612 *va_arg (arglist, long *) = inlen;
613 }
614 else if (size == F_LONGLONG)
615 {
616 *va_arg (arglist, int64_t *) = inlen;
617 }
618 else if (size == F_BIGI)
619 {
620 *va_arg (arglist, ptrdiff_t *) = inlen;
621 }
622 else
623 {
624 *va_arg (arglist, int *) = inlen;
625 }
626 }
627 else if (type == 'f' || type == 'F')
628 {
629 expchar = '\0';
630 goto fp_begin;
631 }
632 else if (type == 'g' || type == 'G')
633 {
634 expchar = type - ('g' - 'e');
635 if (precision == 0)
636 {
637 precision = 1;
638 }
639 goto fp_begin;
640 }
641 #if 0
642 // The hdtoa function provided with FreeBSD uses a hexadecimal FP constant.
643 // Microsoft's compiler does not support these, so I would need to hack it
644 // together with ints instead. It's very do-able, but until I actually have
645 // some reason to print hex FP numbers, I won't bother.
646 else if (type == 'a' || type == 'A')
647 {
648 if (type == 'A')
649 {
650 xits = HEXits;
651 hexprefix = 'X';
652 expchar = 'P';
653 }
654 else
655 {
656 hexprefix = 'x';
657 expchar = 'p';
658 }
659 if (precision >= 0)
660 {
661 precision++;
662 }
663 dblarg = va_arg(arglist, double);
664 dtoaresult = obuff = hdtoa(dblarg, xits, precision, &expt, &signflag, &dtoaend);
665 if (precision < 0)
666 {
667 precision = (int)(dtoaend - obuff);
668 }
669 if (expt == INT_MAX)
670 {
671 hexprefix = '\0';
672 }
673 goto fp_common;
674 }
675 #endif
676 else if (type == 'e' || type == 'E')
677 {
678 expchar = type;
679 if (precision < 0) // account for digit before decpt
680 {
681 precision = DEFPREC + 1;
682 }
683 else
684 {
685 precision++;
686 }
687 fp_begin:
688 if (precision < 0)
689 {
690 precision = DEFPREC;
691 }
692 dblarg = va_arg(arglist, double);
693 obuff = dtoaresult = dtoa(dblarg, expchar ? 2 : 3, precision, &expt, &signflag, &dtoaend);
694 //fp_common:
695 #ifdef __ANDROID__
696 decimal_point = ".";
697 #else
698 decimal_point = localeconv()->decimal_point;
699 #endif
700 flags |= F_SIGNED;
701 if (signflag)
702 {
703 flags |= F_NEGATIVE;
704 }
705 if (expt == INT_MAX) // inf or nan
706 {
707 if (*obuff == 'N')
708 {
709 obuff = (type >= 'a') ? "nan" : "NAN";
710 flags &= ~F_SIGNED;
711 }
712 else
713 {
714 obuff = (type >= 'a') ? "inf" : "INF";
715 }
716 bufflen = 3;
717 flags &= ~F_ZERO;
718 }
719 else
720 {
721 flags |= F_FPT;
722 ndig = (int)(dtoaend - obuff);
723 if (type == 'g' || type == 'G')
724 {
725 if (expt > -4 && expt <= precision)
726 { // Make %[gG] smell like %[fF].
727 expchar = '\0';
728 if (flags & F_HASH)
729 {
730 precision -= expt;
731 }
732 else
733 {
734 precision = ndig - expt;
735 }
736 if (precision < 0)
737 {
738 precision = 0;
739 }
740 }
741 else
742 { // Make %[gG] smell like %[eE], but trim trailing zeroes if no # flag.
743 if (!(flags & F_HASH))
744 {
745 precision = ndig;
746 }
747 }
748 }
749 if (expchar)
750 {
751 expsize = exponent(expstr, expt - 1, expchar);
752 bufflen = expsize + precision;
753 if (precision > 1 || (flags & F_HASH))
754 {
755 ++bufflen;
756 }
757 }
758 else
759 { // space for digits before decimal point
760 if (expt > 0)
761 {
762 bufflen = expt;
763 }
764 else // "0"
765 {
766 bufflen = 1;
767 }
768 // space for decimal pt and following digits
769 if (precision != 0 || (flags & F_HASH))
770 {
771 bufflen += precision + 1;
772 }
773 }
774 }
775 }
776
777 // Check for sign prefix (only for signed numbers)
778 if (flags & F_SIGNED)
779 {
780 if (flags & F_NEGATIVE)
781 {
782 sign = '-';
783 }
784 else if (flags & F_PLUS)
785 {
786 sign = '+';
787 }
788 else if (flags & F_BLANK)
789 {
790 sign = ' ';
791 }
792 }
793
794 // Construct complete prefix from sign and hex prefix character
795 prefixlen = 0;
796 if (sign != '\0')
797 {
798 prefix[0] = sign;
799 prefixlen = 1;
800 }
801 if (hexprefix != '\0')
802 {
803 prefix[prefixlen] = '0';
804 prefix[prefixlen + 1] = hexprefix;
805 prefixlen += 2;
806 }
807
808 // Pad the output to the field width, if needed
809 int fieldlen = prefixlen + postprefixzeros + bufflen;
810 const char *pad = (flags & F_ZERO) ? zeroes : spaces;
811
812 // If the output is right aligned and zero-padded, then the prefix must come before the padding.
813 if ((flags & (F_ZERO|F_MINUS)) == F_ZERO && prefixlen > 0)
814 {
815 outlen += output (outputData, prefix, prefixlen);
816 prefixlen = 0;
817 }
818 if (!(flags & F_MINUS) && fieldlen < width)
819 { // Field is right-justified, so padding comes first
820 outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
821 width = -1;
822 }
823
824 // Output field: Prefix, post-prefix zeros, buffer text
825 if (prefixlen > 0)
826 {
827 outlen += output (outputData, prefix, prefixlen);
828 }
829 outlen += writepad (output, outputData, zeroes, sizeof(spaces), postprefixzeros);
830 if (!(flags & F_FPT))
831 {
832 if (bufflen > 0)
833 {
834 outlen += output (outputData, obuff, bufflen);
835 }
836 }
837 else
838 {
839 if (expchar == '\0') // %[fF] or sufficiently short %[gG]
840 {
841 if (expt <= 0)
842 {
843 outlen += output (outputData, zeroes, 1);
844 if (precision != 0 || (flags & F_HASH))
845 {
846 outlen += output (outputData, decimal_point, 1);
847 }
848 outlen += writepad (output, outputData, zeroes, sizeof(zeroes), -expt);
849 // already handled initial 0's
850 precision += expt;
851 }
852 else
853 {
854 outlen += printandpad (output, outputData, obuff, dtoaend, expt, zeroes, sizeof(zeroes));
855 obuff += expt;
856 if (precision || (flags & F_HASH))
857 {
858 outlen += output (outputData, decimal_point, 1);
859 }
860 }
861 outlen += printandpad (output, outputData, obuff, dtoaend, precision, zeroes, sizeof(zeroes));
862 }
863 else // %[eE] or sufficiently long %[gG]
864 {
865 if (precision > 1 || (flags & F_HASH))
866 {
867 buffer[0] = *obuff++;
868 buffer[1] = *decimal_point;
869 outlen += output (outputData, buffer, 2);
870 outlen += output (outputData, obuff, ndig - 1);
871 outlen += writepad (output, outputData, zeroes, sizeof(zeroes), precision - ndig);
872 }
873 else // XeYY
874 {
875 outlen += output (outputData, obuff, 1);
876 }
877 outlen += output (outputData, expstr, expsize);
878 }
879 }
880
881 if ((flags & F_MINUS) && fieldlen < width)
882 { // Field is left-justified, so padding comes last
883 outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
884 }
885 len += outlen;
886 if (dtoaresult != NULL)
887 {
888 freedtoa(dtoaresult);
889 dtoaresult = NULL;
890 }
891 }
892 }
893 }
894
writepad(OutputFunc output,void * outputData,const char * pad,int padsize,int spaceToFill)895 static int writepad (OutputFunc output, void *outputData, const char *pad, int padsize, int spaceToFill)
896 {
897 int outlen = 0;
898 while (spaceToFill > 0)
899 {
900 int count = spaceToFill > padsize ? padsize : spaceToFill;
901 outlen += output (outputData, pad, count);
902 spaceToFill -= count;
903 }
904 return outlen;
905 }
906
printandpad(OutputFunc output,void * outputData,const char * p,const char * ep,int len,const char * with,int padsize)907 static int printandpad (OutputFunc output, void *outputData, const char *p, const char *ep, int len, const char *with, int padsize)
908 {
909 int outlen = 0;
910 int n2 = (int)(ep - p);
911 if (n2 > len)
912 {
913 n2 = len;
914 }
915 if (n2 > 0)
916 {
917 outlen = output (outputData, p, n2);
918 }
919 return outlen + writepad (output, outputData, with, padsize, len - (n2 > 0 ? n2 : 0));
920 }
921
exponent(char * p0,int exp,int fmtch)922 static int exponent (char *p0, int exp, int fmtch)
923 {
924 char *p, *t;
925 char expbuf[MAXEXPDIG];
926
927 p = p0;
928 *p++ = fmtch;
929 if (exp < 0)
930 {
931 exp = -exp;
932 *p++ = '-';
933 }
934 else
935 {
936 *p++ = '+';
937 }
938 t = expbuf + MAXEXPDIG;
939 if (exp > 9)
940 {
941 do
942 {
943 *--t = '0' + (exp % 10);
944 }
945 while ((exp /= 10) > 9);
946 *--t = '0' + exp;
947 for(; t < expbuf + MAXEXPDIG; *p++ = *t++)
948 { }
949 }
950 else
951 {
952 // Exponents for decimal floating point conversions
953 // (%[eEgG]) must be at least two characters long,
954 // whereas exponents for hexadecimal conversions can
955 // be only one character long.
956 if (fmtch == 'e' || fmtch == 'E')
957 {
958 *p++ = '0';
959 }
960 *p++ = '0' + exp;
961 }
962 return (int)(p - p0);
963 }
964 };
965
966 //========================================================================//
967 // snprintf / vsnprintf imitations
968
969 #if defined(__GNUC__) && !defined(__ANDROID__)
970 #define GCCPRINTF(stri,firstargi) __attribute__((format(printf,stri,firstargi)))
971 #define GCCFORMAT(stri) __attribute__((format(printf,stri,0)))
972 #define GCCNOWARN __attribute__((unused))
973 #else
974 #define GCCPRINTF(a,b)
975 #define GCCFORMAT(a)
976 #define GCCNOWARN
977 #endif
978
979 struct snprintf_state
980 {
981 char *buffer;
982 size_t maxlen;
983 size_t curlen;
984 int ideallen;
985 };
986
myvsnprintf_helper(void * data,const char * cstr,int cstr_len)987 static int myvsnprintf_helper(void *data, const char *cstr, int cstr_len)
988 {
989 snprintf_state *state = (snprintf_state *)data;
990
991 if (INT_MAX - cstr_len < state->ideallen)
992 {
993 state->ideallen = INT_MAX;
994 }
995 else
996 {
997 state->ideallen += cstr_len;
998 }
999 if (state->curlen + cstr_len > state->maxlen)
1000 {
1001 cstr_len = (int)(state->maxlen - state->curlen);
1002 }
1003 if (cstr_len > 0)
1004 {
1005 memcpy(state->buffer + state->curlen, cstr, cstr_len);
1006 state->curlen += cstr_len;
1007 }
1008 return cstr_len;
1009 }
1010
1011 extern "C"
1012 {
1013
1014 // Unlike the MS CRT function snprintf, this one always writes a terminating
1015 // null character to the buffer. It also returns the full length of the string
1016 // that would have been output if the buffer had been large enough. In other
1017 // words, it follows BSD/Linux rules and not MS rules.
myvsnprintf(char * buffer,size_t count,const char * format,va_list argptr)1018 int myvsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
1019 {
1020 size_t originalcount = count;
1021 if (count != 0)
1022 {
1023 count--;
1024 }
1025 if (count > INT_MAX)
1026 { // This is probably an error. Output nothing.
1027 originalcount = 0;
1028 count = 0;
1029 }
1030 snprintf_state state = { buffer, count, 0, 0 };
1031 StringFormat::VWorker(myvsnprintf_helper, &state, format, argptr);
1032 if (originalcount > 0)
1033 {
1034 buffer[state.curlen] = '\0';
1035 }
1036 return state.ideallen;
1037 }
1038
mysnprintf(char * buffer,size_t count,const char * format,...)1039 int mysnprintf(char *buffer, size_t count, const char *format, ...)
1040 {
1041 va_list argptr;
1042 va_start(argptr, format);
1043 int len = myvsnprintf(buffer, count, format, argptr);
1044 va_end(argptr);
1045 return len;
1046 }
1047
1048 }
1049