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