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 			decimal_point = localeconv()->decimal_point;
696 			flags |= F_SIGNED;
697 			if (signflag)
698 			{
699 				flags |= F_NEGATIVE;
700 			}
701 			if (expt == INT_MAX)	// inf or nan
702 			{
703 				if (*obuff == 'N')
704 				{
705 					obuff = (type >= 'a') ? "nan" : "NAN";
706 					flags &= ~F_SIGNED;
707 				}
708 				else
709 				{
710 					obuff = (type >= 'a') ? "inf" : "INF";
711 				}
712 				bufflen = 3;
713 				flags &= ~F_ZERO;
714 			}
715 			else
716 			{
717 				flags |= F_FPT;
718 				ndig = (int)(dtoaend - obuff);
719 				if (type == 'g' || type == 'G')
720 				{
721 					if (expt > -4 && expt <= precision)
722 					{ // Make %[gG] smell like %[fF].
723 						expchar = '\0';
724 						if (flags & F_HASH)
725 						{
726 							precision -= expt;
727 						}
728 						else
729 						{
730 							precision = ndig - expt;
731 						}
732 						if (precision < 0)
733 						{
734 							precision = 0;
735 						}
736 					}
737 					else
738 					{ // Make %[gG] smell like %[eE], but trim trailing zeroes if no # flag.
739 						if (!(flags & F_HASH))
740 						{
741 							precision = ndig;
742 						}
743 					}
744 				}
745 				if (expchar)
746 				{
747 					expsize = exponent(expstr, expt - 1, expchar);
748 					bufflen = expsize + precision;
749 					if (precision > 1 || (flags & F_HASH))
750 					{
751 						++bufflen;
752 					}
753 				}
754 				else
755 				{ // space for digits before decimal point
756 					if (expt > 0)
757 					{
758 						bufflen = expt;
759 					}
760 					else	// "0"
761 					{
762 						bufflen = 1;
763 					}
764 					// space for decimal pt and following digits
765 					if (precision != 0 || (flags & F_HASH))
766 					{
767 						bufflen += precision + 1;
768 					}
769 				}
770 			}
771 		}
772 
773 		// Check for sign prefix (only for signed numbers)
774 		if (flags & F_SIGNED)
775 		{
776 			if (flags & F_NEGATIVE)
777 			{
778 				sign = '-';
779 			}
780 			else if (flags & F_PLUS)
781 			{
782 				sign = '+';
783 			}
784 			else if (flags & F_BLANK)
785 			{
786 				sign = ' ';
787 			}
788 		}
789 
790 		// Construct complete prefix from sign and hex prefix character
791 		prefixlen = 0;
792 		if (sign != '\0')
793 		{
794 			prefix[0] = sign;
795 			prefixlen = 1;
796 		}
797 		if (hexprefix != '\0')
798 		{
799 			prefix[prefixlen] = '0';
800 			prefix[prefixlen + 1] = hexprefix;
801 			prefixlen += 2;
802 		}
803 
804 		// Pad the output to the field width, if needed
805 		int fieldlen = prefixlen + postprefixzeros + bufflen;
806 		const char *pad = (flags & F_ZERO) ? zeroes : spaces;
807 
808 		// If the output is right aligned and zero-padded, then the prefix must come before the padding.
809 		if ((flags & (F_ZERO|F_MINUS)) == F_ZERO && prefixlen > 0)
810 		{
811 			outlen += output (outputData, prefix, prefixlen);
812 			prefixlen = 0;
813 		}
814         if (!(flags & F_MINUS) && fieldlen < width)
815 		{ // Field is right-justified, so padding comes first
816 			outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
817 			width = -1;
818 		}
819 
820 		// Output field: Prefix, post-prefix zeros, buffer text
821 		if (prefixlen > 0)
822 		{
823 			outlen += output (outputData, prefix, prefixlen);
824 		}
825 		outlen += writepad (output, outputData, zeroes, sizeof(spaces), postprefixzeros);
826 		if (!(flags & F_FPT))
827 		{
828 			if (bufflen > 0)
829 			{
830 				outlen += output (outputData, obuff, bufflen);
831 			}
832 		}
833 		else
834 		{
835 			if (expchar == '\0')	// %[fF] or sufficiently short %[gG]
836 			{
837 				if (expt <= 0)
838 				{
839 					outlen += output (outputData, zeroes, 1);
840 					if (precision != 0 || (flags & F_HASH))
841 					{
842 						outlen += output (outputData, decimal_point, 1);
843 					}
844 					outlen += writepad (output, outputData, zeroes, sizeof(zeroes), -expt);
845 					// already handled initial 0's
846 					precision += expt;
847 				}
848 				else
849 				{
850 					outlen += printandpad (output, outputData, obuff, dtoaend, expt, zeroes, sizeof(zeroes));
851 					obuff += expt;
852 					if (precision || (flags & F_HASH))
853 					{
854 						outlen += output (outputData, decimal_point, 1);
855 					}
856 				}
857 				outlen += printandpad (output, outputData, obuff, dtoaend, precision, zeroes, sizeof(zeroes));
858 			}
859 			else						// %[eE] or sufficiently long %[gG]
860 			{
861 				if (precision > 1 || (flags & F_HASH))
862 				{
863 					buffer[0] = *obuff++;
864 					buffer[1] = *decimal_point;
865 					outlen += output (outputData, buffer, 2);
866 					outlen += output (outputData, obuff, ndig - 1);
867 					outlen += writepad (output, outputData, zeroes, sizeof(zeroes), precision - ndig);
868 				}
869 				else		// XeYY
870 				{
871 					outlen += output (outputData, obuff, 1);
872 				}
873 				outlen += output (outputData, expstr, expsize);
874 			}
875 		}
876 
877         if ((flags & F_MINUS) && fieldlen < width)
878 		{ // Field is left-justified, so padding comes last
879 			outlen += writepad (output, outputData, pad, sizeof(spaces), width - fieldlen);
880 		}
881 		len += outlen;
882 		if (dtoaresult != NULL)
883 		{
884 			freedtoa(dtoaresult);
885 			dtoaresult = NULL;
886 		}
887 	}
888 	}
889 	}
890 
writepad(OutputFunc output,void * outputData,const char * pad,int padsize,int spaceToFill)891 	static int writepad (OutputFunc output, void *outputData, const char *pad, int padsize, int spaceToFill)
892 	{
893 		int outlen = 0;
894 		while (spaceToFill > 0)
895 		{
896 			int count = spaceToFill > padsize ? padsize : spaceToFill;
897 			outlen += output (outputData, pad, count);
898 			spaceToFill -= count;
899 		}
900 		return outlen;
901 	}
902 
printandpad(OutputFunc output,void * outputData,const char * p,const char * ep,int len,const char * with,int padsize)903 	static int printandpad (OutputFunc output, void *outputData, const char *p, const char *ep, int len, const char *with, int padsize)
904 	{
905 		int outlen = 0;
906 		int n2 = (int)(ep - p);
907 		if (n2 > len)
908 		{
909 			n2 = len;
910 		}
911 		if (n2 > 0)
912 		{
913 			outlen = output (outputData, p, n2);
914 		}
915 		return outlen + writepad (output, outputData, with, padsize, len - (n2 > 0 ? n2 : 0));
916 	}
917 
exponent(char * p0,int exp,int fmtch)918 	static int exponent (char *p0, int exp, int fmtch)
919 	{
920 		char *p, *t;
921 		char expbuf[MAXEXPDIG];
922 
923 		p = p0;
924 		*p++ = fmtch;
925 		if (exp < 0)
926 		{
927 			exp = -exp;
928 			*p++ = '-';
929 		}
930 		else
931 		{
932 			*p++ = '+';
933 		}
934 		t = expbuf + MAXEXPDIG;
935 		if (exp > 9)
936 		{
937 			do
938 			{
939 				*--t = '0' + (exp % 10);
940 			}
941 			while ((exp /= 10) > 9);
942 			*--t = '0' + exp;
943 			for(; t < expbuf + MAXEXPDIG; *p++ = *t++)
944 			{ }
945 		}
946 		else
947 		{
948 			// Exponents for decimal floating point conversions
949 			// (%[eEgG]) must be at least two characters long,
950 			// whereas exponents for hexadecimal conversions can
951 			// be only one character long.
952 			if (fmtch == 'e' || fmtch == 'E')
953 			{
954 				*p++ = '0';
955 			}
956 			*p++ = '0' + exp;
957 		}
958 		return (int)(p - p0);
959 	}
960 };
961 
962 //========================================================================//
963 // snprintf / vsnprintf imitations
964 
965 #ifdef __GNUC__
966 #define GCCPRINTF(stri,firstargi)	__attribute__((format(printf,stri,firstargi)))
967 #define GCCFORMAT(stri)				__attribute__((format(printf,stri,0)))
968 #define GCCNOWARN					__attribute__((unused))
969 #else
970 #define GCCPRINTF(a,b)
971 #define GCCFORMAT(a)
972 #define GCCNOWARN
973 #endif
974 
975 struct snprintf_state
976 {
977 	char *buffer;
978 	size_t maxlen;
979 	size_t curlen;
980 	int ideallen;
981 };
982 
myvsnprintf_helper(void * data,const char * cstr,int cstr_len)983 static int myvsnprintf_helper(void *data, const char *cstr, int cstr_len)
984 {
985 	snprintf_state *state = (snprintf_state *)data;
986 
987 	if (INT_MAX - cstr_len < state->ideallen)
988 	{
989 		state->ideallen = INT_MAX;
990 	}
991 	else
992 	{
993 		state->ideallen += cstr_len;
994 	}
995 	if (state->curlen + cstr_len > state->maxlen)
996 	{
997 		cstr_len = (int)(state->maxlen - state->curlen);
998 	}
999 	if (cstr_len > 0)
1000 	{
1001 		memcpy(state->buffer + state->curlen, cstr, cstr_len);
1002 		state->curlen += cstr_len;
1003 	}
1004 	return cstr_len;
1005 }
1006 
1007 extern "C"
1008 {
1009 
1010 // Unlike the MS CRT function snprintf, this one always writes a terminating
1011 // null character to the buffer. It also returns the full length of the string
1012 // that would have been output if the buffer had been large enough. In other
1013 // words, it follows BSD/Linux rules and not MS rules.
myvsnprintf(char * buffer,size_t count,const char * format,va_list argptr)1014 int myvsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
1015 {
1016 	size_t originalcount = count;
1017 	if (count != 0)
1018 	{
1019 		count--;
1020 	}
1021 	if (count > INT_MAX)
1022 	{ // This is probably an error. Output nothing.
1023 		originalcount = 0;
1024 		count = 0;
1025 	}
1026 	snprintf_state state = { buffer, count, 0, 0 };
1027 	StringFormat::VWorker(myvsnprintf_helper, &state, format, argptr);
1028 	if (originalcount > 0)
1029 	{
1030 		buffer[state.curlen] = '\0';
1031 	}
1032 	return state.ideallen;
1033 }
1034 
mysnprintf(char * buffer,size_t count,const char * format,...)1035 int mysnprintf(char *buffer, size_t count, const char *format, ...)
1036 {
1037 	va_list argptr;
1038 	va_start(argptr, format);
1039 	int len = myvsnprintf(buffer, count, format, argptr);
1040 	va_end(argptr);
1041 	return len;
1042 }
1043 
1044 }
1045