1 /*
2  * snprintf.c
3  * ripped from rsync sources by pts@inf.bme.hu at Thu Mar  7 18:16:00 CET 2002
4  * ripped from reTCP sources by pts@fazekas.hu at Tue Jun 11 14:47:01 CEST 2002
5  *
6  * Why does this .c file rock?
7  *
8  * -- minimal dependencies: only <stdarg.h> is included
9  * -- minimal dependencies: not even -lm is required
10  * -- can print floating point numbers
11  * -- can print `long long' and `long double'
12  * -- C99 semantics (NULL arg for vsnprintf OK, always returns the length
13  *    that would have been printed)
14  * -- provides all vsnprintf(), snprintf(), vasprintf(), asprintf()
15  */
16 
17 /**** pts: sam2p-specific defines ****/
18 #include "snprintf.h"
19 /* #include <stdarg.h> -- from snprintf.h */
20 #define P_CONST PTS_const
21 #ifdef NULL  /* as on Mac OS/X 10.5.7 <stdlib.h> */
22 #  undef NULL
23 #endif
24 #define NULL ((void*)0)
25 #ifdef __cplusplus
26 #  define malloc ::operator new
27 #else
28 #  include <stdlib.h> /* malloc() */
29 #endif
30 #define size_t slen_t /* normally defined in <sys/types.h> */
31 #define sizeret_t slen_t /* normally: int, unsigned */
32 #undef  HAVE_LONG_DOUBLE
33 #undef  HAVE_LONG_LONG
34 #undef  HAVE_C99_VSNPRINTF /* force local implementation */
35 #undef  HAVE_ASPRINTF
36 #undef  HAVE_VASPRINTF
37 #undef  TEST_SNPRINTF
38 #undef  DEBUG_SNPRINTF
39 #define NEED_SPRINTF 1
40 #define vsnprintf fixup_vsnprintf
41 #define snprintf  fixup_snprintf
42 #define vasprintf fixup_vasprintf
43 #define asprintf  fixup_asprintf
44 #define sprintf   fixup_sprintf
45 typedef slen_t ret_t;
46 
47 /* vvv repeat prototypes in snprintf.h */
48 EXTERN_C sizeret_t fixup_vsnprintf(char *str, size_t count, P_CONST char *fmt, va_list args);
49 EXTERN_C sizeret_t fixup_snprintf(char *str,size_t count,P_CONST char *fmt,...);
50 EXTERN_C sizeret_t fixup_vasprintf(char **ptr, P_CONST char *format, va_list ap);
51 EXTERN_C sizeret_t fixup_asprintf(char **ptr, P_CONST char *format, ...);
52 EXTERN_C sizeret_t fixup_sprintf(char *ptr, P_CONST char *format, ...);
53 
54 /*
55  * Copyright Patrick Powell 1995
56  * This code is based on code written by Patrick Powell (papowell@astart.com)
57  * It may be used for any purpose as long as this notice remains intact
58  * on all source code distributions
59  */
60 
61 /**************************************************************
62  * Original:
63  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
64  * A bombproof version of doprnt (dopr) included.
65  * Sigh.  This sort of thing is always nasty do deal with.  Note that
66  * the version here does not include floating point...
67  *
68  * snprintf() is used instead of sprintf() as it does limit checks
69  * for string length.  This covers a nasty loophole.
70  *
71  * The other functions are there to prevent NULL pointers from
72  * causing nast effects.
73  *
74  * More Recently:
75  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
76  *  This was ugly.  It is still ugly.  I opted out of floating point
77  *  numbers, but the formatter understands just about everything
78  *  from the normal C string format, at least as far as I can tell from
79  *  the Solaris 2.5 printf(3S) man page.
80  *
81  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
82  *    Ok, added some minimal floating point support, which means this
83  *    probably requires libm on most operating systems.  Don't yet
84  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
85  *    was pretty badly broken, it just wasn't being exercised in ways
86  *    which showed it, so that's been fixed.  Also, formated the code
87  *    to mutt conventions, and removed dead code left over from the
88  *    original.  Also, there is now a builtin-test, just compile with:
89  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
90  *    and run snprintf for results.
91  *
92  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
93  *    The PGP code was using unsigned hexadecimal formats.
94  *    Unfortunately, unsigned formats simply didn't work.
95  *
96  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
97  *    The original code assumed that both snprintf() and vsnprintf() were
98  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
99  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
100  *
101  *  Andrew Tridgell (tridge@samba.org) Oct 1998
102  *    fixed handling of %.0f
103  *    added test for HAVE_LONG_DOUBLE
104  *
105  * tridge@samba.org, idra@samba.org, April 2001
106  *    got rid of fcvt code (twas buggy and made testing harder)
107  *    added C99 semantics
108  *
109  **************************************************************/
110 
111 
112 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
113 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
114 #include <stdio.h>
115  /* make the compiler happy with an empty file */
dummy_snprintf(void)116  void dummy_snprintf(void) {}
117 #else
118 
119 #if HAVE_LONG_DOUBLE && NEED_LONG_DOUBLE
120 #define LDOUBLE long double
121 #else
122 #define LDOUBLE double
123 #endif
124 
125 #if HAVE_LONG_LONG && NEED_LONG_LONG
126 #define LLONG long long
127 #else
128 #define LLONG long
129 #endif
130 
131 static size_t dopr(char *buffer, size_t maxlen, P_CONST char *format,
132 		   va_list args);
133 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
134 		    char *value, int flags, int min, int max);
135 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
136 		    long value, int base, int min, int max, int flags);
137 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
138 		   LDOUBLE fvalue, int min, int max, int flags);
139 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
140 
141 /*
142  * dopr(): poor man's version of doprintf
143  */
144 
145 /* format read states */
146 #define DP_S_DEFAULT 0
147 #define DP_S_FLAGS   1
148 #define DP_S_MIN     2
149 #define DP_S_DOT     3
150 #define DP_S_MAX     4
151 #define DP_S_MOD     5
152 #define DP_S_CONV    6
153 #define DP_S_DONE    7
154 
155 /* format flags - Bits */
156 #define DP_F_MINUS 	(1 << 0)
157 #define DP_F_PLUS  	(1 << 1)
158 #define DP_F_SPACE 	(1 << 2)
159 #define DP_F_NUM   	(1 << 3)
160 #define DP_F_ZERO  	(1 << 4)
161 #define DP_F_UP    	(1 << 5)
162 #define DP_F_UNSIGNED 	(1 << 6)
163 
164 /* Conversion Flags */
165 #define DP_C_SHORT   1
166 #define DP_C_LONG    2
167 #define DP_C_LDOUBLE 3
168 #define DP_C_LLONG   4
169 
170 #define char_to_int(p) ((p)- '0')
171 #ifndef MAX
172 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
173 #endif
174 
175 /**** pts ****/
176 #undef  isdigit
177 #define isdigit(c) ((unsigned char)((c)-'0')<=(unsigned char)('9'-'0'))
178 
dopr(char * buffer,size_t maxlen,P_CONST char * format,va_list args)179 static size_t dopr(char *buffer, size_t maxlen, P_CONST char *format, va_list args)
180 {
181 	char ch;
182 	LLONG value;
183 	LDOUBLE fvalue;
184 	char *strvalue;
185 	int min;
186 	int max;
187 	int state;
188 	int flags;
189 	int cflags;
190 	size_t currlen;
191 
192 	state = DP_S_DEFAULT;
193 	currlen = flags = cflags = min = 0;
194 	max = -1;
195 	ch = *format++;
196 
197 	while (state != DP_S_DONE) {
198 		if (ch == '\0')
199 			state = DP_S_DONE;
200 
201 		switch(state) {
202 		case DP_S_DEFAULT:
203 			if (ch == '%')
204 				state = DP_S_FLAGS;
205 			else
206 				dopr_outch (buffer, &currlen, maxlen, ch);
207 			ch = *format++;
208 			break;
209 		case DP_S_FLAGS:
210 			switch (ch) {
211 			case '-':
212 				flags |= DP_F_MINUS;
213 				ch = *format++;
214 				break;
215 			case '+':
216 				flags |= DP_F_PLUS;
217 				ch = *format++;
218 				break;
219 			case ' ':
220 				flags |= DP_F_SPACE;
221 				ch = *format++;
222 				break;
223 			case '#':
224 				flags |= DP_F_NUM;
225 				ch = *format++;
226 				break;
227 			case '0':
228 				flags |= DP_F_ZERO;
229 				ch = *format++;
230 				break;
231 			default:
232 				state = DP_S_MIN;
233 				break;
234 			}
235 			break;
236 		case DP_S_MIN:
237 			if (isdigit((unsigned char)ch)) {
238 				min = 10*min + char_to_int (ch);
239 				ch = *format++;
240 			} else if (ch == '*') {
241 				min = va_arg (args, int);
242 				ch = *format++;
243 				state = DP_S_DOT;
244 			} else {
245 				state = DP_S_DOT;
246 			}
247 			break;
248 		case DP_S_DOT:
249 			if (ch == '.') {
250 				state = DP_S_MAX;
251 				ch = *format++;
252 			} else {
253 				state = DP_S_MOD;
254 			}
255 			break;
256 		case DP_S_MAX:
257 			if (isdigit((unsigned char)ch)) {
258 				if (max < 0)
259 					max = 0;
260 				max = 10*max + char_to_int (ch);
261 				ch = *format++;
262 			} else if (ch == '*') {
263 				max = va_arg (args, int);
264 				ch = *format++;
265 				state = DP_S_MOD;
266 			} else {
267 				state = DP_S_MOD;
268 			}
269 			break;
270 		case DP_S_MOD:
271 			switch (ch) {
272 			case 'h':
273 				cflags = DP_C_SHORT;
274 				ch = *format++;
275 				break;
276 			case 'l':
277 				cflags = DP_C_LONG;
278 				ch = *format++;
279 				if (ch == 'l') {	/* It's a long long */
280 					cflags = DP_C_LLONG;
281 					ch = *format++;
282 				}
283 				break;
284 			case 'L':
285 				cflags = DP_C_LDOUBLE;
286 				ch = *format++;
287 				break;
288 			default:
289 				break;
290 			}
291 			state = DP_S_CONV;
292 			break;
293 		case DP_S_CONV:
294 			switch (ch) {
295 			case 'd':
296 			case 'i':
297 				if (cflags == DP_C_SHORT)
298 					value = va_arg (args, int);
299 				else if (cflags == DP_C_LONG)
300 					value = va_arg (args, long int);
301 				else if (cflags == DP_C_LLONG)
302 					value = va_arg (args, LLONG);
303 				else
304 					value = va_arg (args, int);
305 				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
306 				break;
307 			case 'o':
308 				flags |= DP_F_UNSIGNED;
309 				if (cflags == DP_C_SHORT)
310 					value = va_arg (args, unsigned int);
311 				else if (cflags == DP_C_LONG)
312 					value = (long)va_arg (args, unsigned long int);
313 				else if (cflags == DP_C_LLONG)
314 					value = (long)va_arg (args, unsigned LLONG);
315 				else
316 					value = (long)va_arg (args, unsigned int);
317 				fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
318 				break;
319 			case 'u':
320 				flags |= DP_F_UNSIGNED;
321 				if (cflags == DP_C_SHORT)
322 					value = va_arg (args, unsigned int);
323 				else if (cflags == DP_C_LONG)
324 					value = (long)va_arg (args, unsigned long int);
325 				else if (cflags == DP_C_LLONG)
326 					value = (LLONG)va_arg (args, unsigned LLONG);
327 				else
328 					value = (long)va_arg (args, unsigned int);
329 				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
330 				break;
331 			case 'X':
332 				flags |= DP_F_UP;
333 			case 'x':
334 				flags |= DP_F_UNSIGNED;
335 				if (cflags == DP_C_SHORT)
336 					value = va_arg (args, unsigned int);
337 				else if (cflags == DP_C_LONG)
338 					value = (long)va_arg (args, unsigned long int);
339 				else if (cflags == DP_C_LLONG)
340 					value = (LLONG)va_arg (args, unsigned LLONG);
341 				else
342 					value = (long)va_arg (args, unsigned int);
343 				fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
344 				break;
345 			case 'f':
346 				if (cflags == DP_C_LDOUBLE)
347 					fvalue = va_arg (args, LDOUBLE);
348 				else
349 					fvalue = va_arg (args, double);
350 				/* um, floating point? */
351 				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
352 				break;
353 			case 'E':
354 				flags |= DP_F_UP;
355 			case 'e':
356 				if (cflags == DP_C_LDOUBLE)
357 					fvalue = va_arg (args, LDOUBLE);
358 				else
359 					fvalue = va_arg (args, double);
360 				break;
361 			case 'G':
362 				flags |= DP_F_UP;
363 			case 'g':
364 				if (cflags == DP_C_LDOUBLE)
365 					fvalue = va_arg (args, LDOUBLE);
366 				else
367 					fvalue = va_arg (args, double);
368 				break;
369 			case 'c':
370 				dopr_outch (buffer, &currlen, maxlen, (char)va_arg (args, int));
371 				break;
372 			case 's':
373 				strvalue = va_arg (args, char *);
374 				if (max == -1) {
375 					/**** pts ****/
376 					for (max = 0; strvalue[max]; ++max); /* strlen */
377 				}
378 				if (min > 0 && max >= 0 && min > max) max = min;
379 				fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
380 				break;
381 			case 'p':
382 				strvalue = (char*)(va_arg (args, void *));
383 				fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
384 				break;
385 			case 'n':
386 				if (cflags == DP_C_SHORT) {
387 					short int *num;
388 					num = va_arg (args, short int *);
389 					*num = currlen;
390 				} else if (cflags == DP_C_LONG) {
391 					long int *num;
392 					num = va_arg (args, long int *);
393 					*num = (long int)currlen;
394 				} else if (cflags == DP_C_LLONG) {
395 					LLONG *num;
396 					num = va_arg (args, LLONG *);
397 					*num = (LLONG)currlen;
398 				} else {
399 					int *num;
400 					num = va_arg (args, int *);
401 					*num = currlen;
402 				}
403 				break;
404 			case '%':
405 				dopr_outch (buffer, &currlen, maxlen, ch);
406 				break;
407 			case 'w':
408 				/* not supported yet, treat as next char */
409 				ch = *format++;
410 				break;
411 			default:
412 				/* Unknown, skip */
413 				break;
414 			}
415 			ch = *format++;
416 			state = DP_S_DEFAULT;
417 			flags = cflags = min = 0;
418 			max = -1;
419 			break;
420 		case DP_S_DONE:
421 			break;
422 		default:
423 			/* hmm? */
424 			break; /* some picky compilers need this */
425 		}
426 	}
427 	if (maxlen != 0) {
428 		if (currlen < maxlen - 1)
429 			buffer[currlen] = '\0';
430 		else if (maxlen > 0)
431 			buffer[maxlen - 1] = '\0';
432 	}
433 
434 	return currlen;
435 }
436 
fmtstr(char * buffer,size_t * currlen,size_t maxlen,char * value,int flags,int min,int max)437 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
438                    char *value, int flags, int min, int max)
439 {
440 	int padlen, strln;     /* amount to pad */
441 	int cnt = 0;
442 
443 #ifdef DEBUG_SNPRINTF
444 	printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
445 #endif
446 	if (value == 0) {
447 		value = (char*)"<NULL>";
448 	}
449 
450 	for (strln = 0; value[strln]; ++strln); /* strlen */
451 	padlen = min - strln;
452 	if (padlen < 0)
453 		padlen = 0;
454 	if (flags & DP_F_MINUS)
455 		padlen = -padlen; /* Left Justify */
456 
457 	while ((padlen > 0) && (cnt < max)) {
458 		dopr_outch (buffer, currlen, maxlen, ' ');
459 		--padlen;
460 		++cnt;
461 	}
462 	while (*value && (cnt < max)) {
463 		dopr_outch (buffer, currlen, maxlen, *value++);
464 		++cnt;
465 	}
466 	while ((padlen < 0) && (cnt < max)) {
467 		dopr_outch (buffer, currlen, maxlen, ' ');
468 		++padlen;
469 		++cnt;
470 	}
471 }
472 
473 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
474 
fmtint(char * buffer,size_t * currlen,size_t maxlen,long value,int base,int min,int max,int flags)475 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
476 		    long value, int base, int min, int max, int flags)
477 {
478 	int signvalue = 0;
479 	unsigned long uvalue;
480 	char convert[20];
481 	int place = 0;
482 	int spadlen = 0; /* amount to space pad */
483 	int zpadlen = 0; /* amount to zero pad */
484 	int caps = 0;
485 
486 	if (max < 0)
487 		max = 0;
488 
489 	uvalue = value;
490 
491 	if(!(flags & DP_F_UNSIGNED)) {
492 		if( value < 0 ) {
493 			signvalue = '-';
494 			uvalue = -value;
495 		} else {
496 			if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
497 				signvalue = '+';
498 			else if (flags & DP_F_SPACE)
499 				signvalue = ' ';
500 		}
501 	}
502 
503 	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
504 
505 	do {
506 		convert[place++] =
507 			(caps? "0123456789ABCDEF":"0123456789abcdef")
508 			[uvalue % (unsigned)base  ];
509 		uvalue = (uvalue / (unsigned)base );
510 	} while(uvalue && (place < 20));
511 	if (place == 20) place--;
512 	convert[place] = 0;
513 
514 	zpadlen = max - place;
515 	spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
516 	if (zpadlen < 0) zpadlen = 0;
517 	if (spadlen < 0) spadlen = 0;
518 	if (flags & DP_F_ZERO) {
519 		zpadlen = MAX(zpadlen, spadlen);
520 		spadlen = 0;
521 	}
522 	if (flags & DP_F_MINUS)
523 		spadlen = -spadlen; /* Left Justifty */
524 
525 #ifdef DEBUG_SNPRINTF
526 	printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
527 	       zpadlen, spadlen, min, max, place);
528 #endif
529 
530 	/* Spaces */
531 	while (spadlen > 0) {
532 		dopr_outch (buffer, currlen, maxlen, ' ');
533 		--spadlen;
534 	}
535 
536 	/* Sign */
537 	if (signvalue)
538 		dopr_outch (buffer, currlen, maxlen, (char)signvalue); /* pacify VC6.0 */
539 
540 	/* Zeros */
541 	if (zpadlen > 0) {
542 		while (zpadlen > 0) {
543 			dopr_outch (buffer, currlen, maxlen, '0');
544 			--zpadlen;
545 		}
546 	}
547 
548 	/* Digits */
549 	while (place > 0)
550 		dopr_outch (buffer, currlen, maxlen, convert[--place]);
551 
552 	/* Left Justified spaces */
553 	while (spadlen < 0) {
554 		dopr_outch (buffer, currlen, maxlen, ' ');
555 		++spadlen;
556 	}
557 }
558 
abs_val(LDOUBLE value)559 static LDOUBLE abs_val(LDOUBLE value)
560 {
561 	LDOUBLE result = value;
562 
563 	if (value < 0)
564 		result = -value;
565 
566 	return result;
567 }
568 
POW10(int exp)569 static LDOUBLE POW10(int exp)
570 {
571 	LDOUBLE result = 1;
572 
573 	while (exp) {
574 		result *= 10;
575 		exp--;
576 	}
577 
578 	return result;
579 }
580 
ROUND(LDOUBLE value)581 static LLONG ROUND(LDOUBLE value)
582 {
583 	LLONG intpart;
584 
585 	intpart = (LLONG)value;
586 	value = value - intpart;
587 	if (value >= 0.5) intpart++;
588 
589 	return intpart;
590 }
591 
592 /* a replacement for modf that doesn't need the math library. Should
593    be portable, but slow */
my_modf(double x0,double * iptr)594 static double my_modf(double x0, double *iptr)
595 {
596 	int i;
597 	long l=0;
598 	double x = x0;
599 	double f = 1.0;
600 
601 	for (i=0;i<100;i++) {
602 		l = (long)x;
603 		if (l <= (x+1) && l >= (x-1)) break;
604 		x *= 0.1;
605 		f *= 10.0;
606 	}
607 
608 	if (i == 100) {
609 		/* yikes! the number is beyond what we can handle. What do we do? */
610 		(*iptr) = 0;
611 		return 0;
612 	}
613 
614 	if (i != 0) {
615 		double i2;
616 		double ret;
617 
618 		ret = my_modf(x0-l*f, &i2);
619 		(*iptr) = l*f + i2;
620 		return ret;
621 	}
622 
623 	(*iptr) = l;
624 	return x - (*iptr);
625 }
626 
627 
fmtfp(char * buffer,size_t * currlen,size_t maxlen,LDOUBLE fvalue,int min,int max,int flags)628 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
629 		   LDOUBLE fvalue, int min, int max, int flags)
630 {
631 	int signvalue = 0;
632 	double ufvalue;
633 	char iconvert[311];
634 	char fconvert[311];
635 	int iplace = 0;
636 	int fplace = 0;
637 	int padlen = 0; /* amount to pad */
638 	int zpadlen = 0;
639 	int caps = 0;
640 	int index;
641 	double intpart;
642 	double fracpart;
643 	double temp;
644 
645 	/*
646 	 * AIX manpage says the default is 0, but Solaris says the default
647 	 * is 6, and sprintf on AIX defaults to 6
648 	 */
649 	if (max < 0)
650 		max = 6;
651 
652 	ufvalue = abs_val (fvalue);
653 
654 	if (fvalue < 0) {
655 		signvalue = '-';
656 	} else {
657 		if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
658 			signvalue = '+';
659 		} else {
660 			if (flags & DP_F_SPACE)
661 				signvalue = ' ';
662 		}
663 	}
664 
665 #if 0
666 	if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
667 #endif
668 
669 #if 0
670 	 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
671 #endif
672 
673 	/*
674 	 * Sorry, we only support 16 digits past the decimal because of our
675 	 * conversion method
676 	 */
677 	if (max > 16)
678 		max = 16;
679 
680 	/* We "cheat" by converting the fractional part to integer by
681 	 * multiplying by a factor of 10
682 	 */
683 
684 	temp = ufvalue;
685 	my_modf(temp, &intpart);
686 
687 	fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
688 
689 	if (fracpart >= POW10(max)) {
690 		intpart++;
691 		fracpart -= POW10(max);
692 	}
693 
694 
695 	/* Convert integer part */
696 	do {
697 		temp = intpart;
698 		my_modf(intpart*0.1, &intpart);
699 		temp = temp*0.1;
700 		index = (int) ((temp -intpart +0.05)* 10.0);
701 		/* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
702 		/* printf ("%llf, %f, %x\n", temp, intpart, index); */
703 		iconvert[iplace++] =
704 			(caps? "0123456789ABCDEF":"0123456789abcdef")[index];
705 	} while (intpart && (iplace < 311));
706 	if (iplace == 311) iplace--;
707 	iconvert[iplace] = 0;
708 
709 	/* Convert fractional part */
710 	if (fracpart)
711 	{
712 		do {
713 			temp = fracpart;
714 			my_modf(fracpart*0.1, &fracpart);
715 			temp = temp*0.1;
716 			index = (int) ((temp -fracpart +0.05)* 10.0);
717 			/* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
718 			/* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
719 			fconvert[fplace++] =
720 			(caps? "0123456789ABCDEF":"0123456789abcdef")[index];
721 		} while(fracpart && (fplace < 311));
722 		if (fplace == 311) fplace--;
723 	}
724 	fconvert[fplace] = 0;
725 
726 	/* -1 for decimal point, another -1 if we are printing a sign */
727 	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
728 	zpadlen = max - fplace;
729 	if (zpadlen < 0) zpadlen = 0;
730 	if (padlen < 0)
731 		padlen = 0;
732 	if (flags & DP_F_MINUS)
733 		padlen = -padlen; /* Left Justifty */
734 
735 	if ((flags & DP_F_ZERO) && (padlen > 0)) {
736 		if (signvalue) {
737 			dopr_outch (buffer, currlen, maxlen, (char)signvalue);
738 			--padlen;
739 			signvalue = 0;
740 		}
741 		while (padlen > 0) {
742 			dopr_outch (buffer, currlen, maxlen, '0');
743 			--padlen;
744 		}
745 	}
746 	while (padlen > 0) {
747 		dopr_outch (buffer, currlen, maxlen, ' ');
748 		--padlen;
749 	}
750 	if (signvalue)
751 		dopr_outch (buffer, currlen, maxlen, (char)signvalue);
752 
753 	while (iplace > 0)
754 		dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
755 
756 #ifdef DEBUG_SNPRINTF
757 	printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
758 #endif
759 
760 	/*
761 	 * Decimal point.  This should probably use locale to find the correct
762 	 * char to print out.
763 	 */
764 	if (max > 0) {
765 		dopr_outch (buffer, currlen, maxlen, '.');
766 
767 		while (fplace > 0)
768 			dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
769 	}
770 
771 	while (zpadlen > 0) {
772 		dopr_outch (buffer, currlen, maxlen, '0');
773 		--zpadlen;
774 	}
775 
776 	while (padlen < 0) {
777 		dopr_outch (buffer, currlen, maxlen, ' ');
778 		++padlen;
779 	}
780 }
781 
dopr_outch(char * buffer,size_t * currlen,size_t maxlen,char c)782 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
783 {
784 	if (*currlen < maxlen) {
785 		buffer[(*currlen)] = c;
786 	}
787 	(*currlen)++;
788 }
789 
790 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
vsnprintf(char * str,size_t count,P_CONST char * fmt,va_list args)791  sizeret_t vsnprintf (char *str, size_t count, P_CONST char *fmt, va_list args)
792 {
793 	return dopr(str, count, fmt, args);
794 }
795 #endif
796 
797 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
snprintf(char * str,size_t count,P_CONST char * fmt,...)798  sizeret_t snprintf(char *str,size_t count,P_CONST char *fmt,...)
799 {
800 	size_t ret;
801 	va_list ap;
802 
803 	PTS_va_start(ap, fmt);
804 	ret = vsnprintf(str, count, fmt, ap);
805 	va_end(ap);
806 	return ret;
807 }
808 #endif
809 
810 #endif
811 
812 #ifndef HAVE_VASPRINTF
vasprintf(char ** ptr,P_CONST char * format,va_list ap)813  sizeret_t vasprintf(char **ptr, P_CONST char *format, va_list ap)
814 {
815 	size_t ret;
816 
817 	ret = vsnprintf((char*)NULL, 0, format, ap);
818 	if (ret+1 <= 1) return ret; /* pts: bit of old unsigned trick... */
819 
820 	if (NULLP==(*ptr = (char *)malloc(ret+1))) return (sizeret_t)-1;
821 	ret = vsnprintf(*ptr, ret+1, format, ap);
822 
823 	return ret;
824 }
825 #endif
826 
827 
828 #ifndef HAVE_ASPRINTF
asprintf(char ** ptr,P_CONST char * format,...)829  sizeret_t asprintf(char **ptr, P_CONST char *format, ...)
830 {
831 	va_list ap;
832 	sizeret_t ret;
833 
834 	PTS_va_start(ap, format);
835 	ret = vasprintf(ptr, format, ap);
836 	va_end(ap);
837 
838 	return ret;
839 }
840 #endif
841 
842 /**** pts ****/
843 #ifdef NEED_SPRINTF
sprintf(char * ptr,P_CONST char * format,...)844  sizeret_t sprintf(char *ptr, P_CONST char *format, ...)
845 {
846 	va_list ap;
847 	sizeret_t ret;
848 
849 	PTS_va_start(ap, format);
850 #if 0
851 	ret = vsnprintf(NULL, 0, format, ap);
852 	if (ret+1 <= 1) return ret;
853 	ret = vsnprintf(ptr, ret, format, ap);
854 #else
855 	ret = vsnprintf(ptr, (slen_t)-1, format, ap);
856 #endif
857 	va_end(ap);
858 
859 	return ret;
860 }
861 #endif
862 
863 #ifdef TEST_SNPRINTF
864 
865  sizeret_t sprintf(char *str,P_CONST char *fmt,...);
866 
main(void)867  int main (void)
868 {
869 	char buf1[1024];
870 	char buf2[1024];
871 	char *fp_fmt[] = {
872 		"%1.1f",
873 		"%-1.5f",
874 		"%1.5f",
875 		"%123.9f",
876 		"%10.5f",
877 		"% 10.5f",
878 		"%+22.9f",
879 		"%+4.9f",
880 		"%01.3f",
881 		"%4f",
882 		"%3.1f",
883 		"%3.2f",
884 		"%.0f",
885 		"%f",
886 		"-16.16f",
887 		NULL
888 	};
889 	double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
890 			     0.9996, 1.996, 4.136,  0};
891 	char *int_fmt[] = {
892 		"%-1.5d",
893 		"%1.5d",
894 		"%123.9d",
895 		"%5.5d",
896 		"%10.5d",
897 		"% 10.5d",
898 		"%+22.33d",
899 		"%01.3d",
900 		"%4d",
901 		"%d",
902 		NULL
903 	};
904 	long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
905 	char *str_fmt[] = {
906 		"10.5s",
907 		"5.10s",
908 		"10.1s",
909 		"0.10s",
910 		"10.0s",
911 		"1.10s",
912 		"%s",
913 		"%.1s",
914 		"%.10s",
915 		"%10s",
916 		NULL
917 	};
918 	char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
919 	int x, y;
920 	int fail = 0;
921 	int num = 0;
922 
923 	printf ("Testing snprintf format codes against system sprintf...\n");
924 
925 	for (x = 0; fp_fmt[x] ; x++) {
926 		for (y = 0; fp_nums[y] != 0 ; y++) {
927 			int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
928 			int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
929 			sprintf (buf2, fp_fmt[x], fp_nums[y]);
930 			if (strcmp (buf1, buf2)) {
931 				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
932 				       fp_fmt[x], buf1, buf2);
933 				fail++;
934 			}
935 			if (l1 != l2) {
936 				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
937 				fail++;
938 			}
939 			num++;
940 		}
941 	}
942 
943 	for (x = 0; int_fmt[x] ; x++) {
944 		for (y = 0; int_nums[y] != 0 ; y++) {
945 			int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
946 			int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
947 			sprintf (buf2, int_fmt[x], int_nums[y]);
948 			if (strcmp (buf1, buf2)) {
949 				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
950 				       int_fmt[x], buf1, buf2);
951 				fail++;
952 			}
953 			if (l1 != l2) {
954 				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
955 				fail++;
956 			}
957 			num++;
958 		}
959 	}
960 
961 	for (x = 0; str_fmt[x] ; x++) {
962 		for (y = 0; str_vals[y] != 0 ; y++) {
963 			int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
964 			int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
965 			sprintf (buf2, str_fmt[x], str_vals[y]);
966 			if (strcmp (buf1, buf2)) {
967 				printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
968 				       str_fmt[x], buf1, buf2);
969 				fail++;
970 			}
971 			if (l1 != l2) {
972 				printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
973 				fail++;
974 			}
975 			num++;
976 		}
977 	}
978 
979 	printf ("%d tests failed out of %d.\n", fail, num);
980 
981 	printf("seeing how many digits we support\n");
982 	{
983 		double v0 = 0.12345678901234567890123456789012345678901;
984 		for (x=0; x<100; x++) {
985 			snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x));
986 			sprintf(buf2,                "%1.1f", v0*pow(10, x));
987 			if (strcmp(buf1, buf2)) {
988 				printf("we seem to support %d digits\n", x-1);
989 				break;
990 			}
991 		}
992 	}
993 
994 	return 0;
995 }
996 #endif /* SNPRINTF_TEST */
997