2 * NOTE: If you change this file, please merge it into rsync, samba, etc.
6 * Copyright Patrick Powell 1995
7 * This code is based on code written by Patrick Powell (papowell@astart.com)
8 * It may be used for any purpose as long as this notice remains intact
9 * on all source code distributions
12 /**************************************************************
14 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
15 * A bombproof version of doprnt (dopr) included.
16 * Sigh. This sort of thing is always nasty do deal with. Note that
17 * the version here does not include floating point...
19 * snprintf() is used instead of sprintf() as it does limit checks
20 * for string length. This covers a nasty loophole.
22 * The other functions are there to prevent NULL pointers from
23 * causing nasty effects.
26 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
27 * This was ugly. It is still ugly. I opted out of floating point
28 * numbers, but the formatter understands just about everything
29 * from the normal C string format, at least as far as I can tell from
30 * the Solaris 2.5 printf(3S) man page.
32 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
33 * Ok, added some minimal floating point support, which means this
34 * probably requires libm on most operating systems. Don't yet
35 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
36 * was pretty badly broken, it just wasn't being exercised in ways
37 * which showed it, so that's been fixed. Also, formatted the code
38 * to mutt conventions, and removed dead code left over from the
39 * original. Also, there is now a builtin-test, just compile with:
40 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
41 * and run snprintf for results.
43 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
44 * The PGP code was using unsigned hexadecimal formats.
45 * Unfortunately, unsigned formats simply didn't work.
47 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
48 * The original code assumed that both snprintf() and vsnprintf() were
49 * missing. Some systems only have snprintf() but not vsnprintf(), so
50 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
52 * Andrew Tridgell (tridge@samba.org) Oct 1998
53 * fixed handling of %.0f
54 * added test for HAVE_LONG_DOUBLE
56 * tridge@samba.org, idra@samba.org, April 2001
57 * got rid of fcvt code (twas buggy and made testing harder)
60 * date: 2002/12/19 19:56:31; author: herb; state: Exp; lines: +2 -0
61 * actually print args for %g and %e
63 * date: 2002/06/03 13:37:52; author: jmcd; state: Exp; lines: +8 -0
64 * Since includes.h isn't included here, VA_COPY has to be defined here. I don't
65 * see any include file that is guaranteed to be here, so I'm defining it
66 * locally. Fixes AIX and Solaris builds.
68 * date: 2002/06/03 03:07:24; author: tridge; state: Exp; lines: +5 -13
69 * put the ifdef for HAVE_VA_COPY in one place rather than in lots of
72 * date: 2002/05/17 14:51:22; author: jmcd; state: Exp; lines: +21 -4
73 * Fix usage of va_list passed as an arg. Use __va_copy before using it
76 * date: 2002/04/16 22:38:04; author: idra; state: Exp; lines: +20 -14
77 * Fix incorrect zpadlen handling in fmtfp.
78 * Thanks to Ollie Oldham <ollie.oldham@metro-optix.com> for spotting it.
79 * few mods to make it easier to compile the tests.
80 * added the "Ollie" test to the floating point ones.
82 * Martin Pool (mbp@samba.org) April 2003
83 * Remove NO_CONFIG_H so that the test case can be built within a source
84 * tree with less trouble.
85 * Remove unnecessary SAFE_FREE() definition.
87 * Martin Pool (mbp@samba.org) May 2003
88 * Put in a prototype for dummy_snprintf() to quiet compiler warnings.
90 * Move #endif to make sure VA_COPY, LDOUBLE, etc are defined even
91 * if the C library has some snprintf functions already.
93 * Darren Tucker (dtucker@zip.com.au) 2005
94 * Fix bug allowing read overruns of the source string with "%.*s"
95 * Usually harmless unless the read runs outside the process' allocation
96 * (eg if your malloc does guard pages) in which case it will segfault.
97 * From OpenSSH. Also added test for same.
99 * Simo Sorce (idra@samba.org) Jan 2006
101 * Add support for position independent parameters
102 * fix fmtstr now it conforms to sprintf wrt min.max
104 **************************************************************/
107 #include "system/locale.h"
109 #ifdef TEST_SNPRINTF /* need math library headers for testing */
111 /* In test mode, we pretend that this system doesn't have any snprintf
112 * functions, regardless of what config.h says. */
113 # undef HAVE_SNPRINTF
114 # undef HAVE_VSNPRINTF
115 # undef HAVE_C99_VSNPRINTF
116 # undef HAVE_ASPRINTF
117 # undef HAVE_VASPRINTF
119 #endif /* TEST_SNPRINTF */
121 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
122 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
124 /* make the compiler happy with an empty file */
125 void dummy_snprintf(void);
126 void dummy_snprintf(void) {}
127 #endif /* HAVE_SNPRINTF, etc */
129 /* yes this really must be a ||. Don't muck with this (tridge) */
130 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
132 #ifdef HAVE_LONG_DOUBLE
133 #define LDOUBLE long double
135 #define LDOUBLE double
138 #ifdef HAVE_LONG_LONG
139 #define LLONG long long
146 #define VA_COPY(dest, src) va_copy(dest, src)
148 #ifdef HAVE___VA_COPY
149 #define VA_COPY(dest, src) __va_copy(dest, src)
151 #define VA_COPY(dest, src) (dest) = (src)
156 * dopr(): poor man's version of doprintf
159 /* format read states */
160 #define DP_S_DEFAULT 0
169 /* format flags - Bits */
170 #define DP_F_MINUS (1 << 0)
171 #define DP_F_PLUS (1 << 1)
172 #define DP_F_SPACE (1 << 2)
173 #define DP_F_NUM (1 << 3)
174 #define DP_F_ZERO (1 << 4)
175 #define DP_F_UP (1 << 5)
176 #define DP_F_UNSIGNED (1 << 6)
178 /* Conversion Flags */
182 #define DP_C_LDOUBLE 4
187 #define CNK_FMT_STR 0
199 #define char_to_int(p) ((p)- '0')
201 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
205 int type
; /* chunk type */
206 int num
; /* parameter number */
217 struct pr_chunk
*min_star
;
218 struct pr_chunk
*max_star
;
219 struct pr_chunk
*next
;
223 struct pr_chunk
**chunks
;
227 static int dopr(char *buffer
, size_t maxlen
, const char *format
,
229 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
230 char *value
, int flags
, int min
, int max
);
231 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
232 LLONG value
, int base
, int min
, int max
, int flags
);
233 static void fmtfp(char *buffer
, size_t *currlen
, size_t maxlen
,
234 LDOUBLE fvalue
, int min
, int max
, int flags
);
235 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
);
236 static struct pr_chunk
*new_chunk(void);
237 static int add_cnk_list_entry(struct pr_chunk_x
**list
,
238 int max_num
, struct pr_chunk
*chunk
);
240 static int dopr(char *buffer
, size_t maxlen
, const char *format
, va_list args_in
)
250 struct pr_chunk
*chunks
= NULL
;
251 struct pr_chunk
*cnk
= NULL
;
252 struct pr_chunk_x
*clist
= NULL
;
256 VA_COPY(args
, args_in
);
258 state
= DP_S_DEFAULT
;
267 /* retrieve the string structure as chunks */
268 while (state
!= DP_S_DONE
) {
276 cnk
->next
= new_chunk();
282 if (!chunks
) chunks
= cnk
;
288 cnk
->type
= CNK_FMT_STR
;
289 cnk
->start
= format
- base
-1;
290 while ((ch
!= '\0') && (ch
!= '%')) ch
= *format
++;
291 cnk
->len
= format
- base
- cnk
->start
-1;
297 cnk
->flags
|= DP_F_MINUS
;
301 cnk
->flags
|= DP_F_PLUS
;
305 cnk
->flags
|= DP_F_SPACE
;
309 cnk
->flags
|= DP_F_NUM
;
313 cnk
->flags
|= DP_F_ZERO
;
317 /* internationalization not supported yet */
326 if (isdigit((unsigned char)ch
)) {
327 cnk
->min
= 10 * cnk
->min
+ char_to_int (ch
);
329 } else if (ch
== '$') {
330 if (!pfirst
&& !pflag
) {
331 /* parameters must be all positioned or none */
338 if (cnk
->min
== 0) /* what ?? */
343 } else if (ch
== '*') {
344 if (pfirst
) pfirst
= 0;
345 cnk
->min_star
= new_chunk();
346 if (!cnk
->min_star
) /* out of memory :-( */
348 cnk
->min_star
->type
= CNK_INT
;
352 if (!isdigit((unsigned char)ch
)) {
353 /* parameters must be all positioned or none */
356 for (num
= 0; isdigit((unsigned char)ch
); ch
= *format
++) {
357 num
= 10 * num
+ char_to_int(ch
);
359 cnk
->min_star
->num
= num
;
360 if (ch
!= '$') /* what ?? */
363 cnk
->min_star
->num
= ++pnum
;
365 max_pos
= add_cnk_list_entry(&clist
, max_pos
, cnk
->min_star
);
366 if (max_pos
== 0) /* out of memory :-( */
371 if (pfirst
) pfirst
= 0;
384 if (isdigit((unsigned char)ch
)) {
387 cnk
->max
= 10 * cnk
->max
+ char_to_int (ch
);
389 } else if (ch
== '$') {
390 if (!pfirst
&& !pflag
) {
391 /* parameters must be all positioned or none */
394 if (cnk
->max
<= 0) /* what ?? */
399 } else if (ch
== '*') {
400 cnk
->max_star
= new_chunk();
401 if (!cnk
->max_star
) /* out of memory :-( */
403 cnk
->max_star
->type
= CNK_INT
;
407 if (!isdigit((unsigned char)ch
)) {
408 /* parameters must be all positioned or none */
411 for (num
= 0; isdigit((unsigned char)ch
); ch
= *format
++) {
412 num
= 10 * num
+ char_to_int(ch
);
414 cnk
->max_star
->num
= num
;
415 if (ch
!= '$') /* what ?? */
418 cnk
->max_star
->num
= ++pnum
;
420 max_pos
= add_cnk_list_entry(&clist
, max_pos
, cnk
->max_star
);
421 if (max_pos
== 0) /* out of memory :-( */
433 cnk
->cflags
= DP_C_SHORT
;
436 cnk
->cflags
= DP_C_CHAR
;
441 cnk
->cflags
= DP_C_LONG
;
443 if (ch
== 'l') { /* It's a long long */
444 cnk
->cflags
= DP_C_LLONG
;
449 cnk
->cflags
= DP_C_LLONG
;
453 cnk
->cflags
= DP_C_LDOUBLE
;
457 cnk
->cflags
= DP_C_SIZET
;
466 if (cnk
->num
== 0) cnk
->num
= ++pnum
;
467 max_pos
= add_cnk_list_entry(&clist
, max_pos
, cnk
);
468 if (max_pos
== 0) /* out of memory :-( */
477 cnk
->type
= CNK_OCTAL
;
478 cnk
->flags
|= DP_F_UNSIGNED
;
481 cnk
->type
= CNK_UINT
;
482 cnk
->flags
|= DP_F_UNSIGNED
;
485 cnk
->flags
|= DP_F_UP
;
488 cnk
->flags
|= DP_F_UNSIGNED
;
491 /* hex float not supported yet */
495 cnk
->flags
|= DP_F_UP
;
497 /* hex float not supported yet */
501 cnk
->type
= CNK_FLOAT
;
504 cnk
->type
= CNK_CHAR
;
507 cnk
->type
= CNK_STRING
;
511 cnk
->flags
|= DP_F_UNSIGNED
;
517 cnk
->type
= CNK_PRCNT
;
520 /* Unknown, bail out*/
524 state
= DP_S_DEFAULT
;
530 break; /* some picky compilers need this */
534 /* retrieve the format arguments */
535 for (pnum
= 0; pnum
< max_pos
; pnum
++) {
538 if (clist
[pnum
].num
== 0) {
539 /* ignoring a parameter should not be permitted
540 * all parameters must be matched at least once
541 * BUT seem some system ignore this rule ...
542 * at least my glibc based system does --SSS
544 #ifdef DEBUG_SNPRINTF
545 printf("parameter at position %d not used\n", pnum
+1);
547 /* eat the parameter */
551 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
552 if (clist
[pnum
].chunks
[0]->type
!= clist
[pnum
].chunks
[i
]->type
) {
554 * all the references to a parameter
555 * must be of the same type
560 cnk
= clist
[pnum
].chunks
[0];
563 if (cnk
->cflags
== DP_C_SHORT
)
564 cnk
->value
= va_arg (args
, int);
565 else if (cnk
->cflags
== DP_C_LONG
)
566 cnk
->value
= va_arg (args
, long int);
567 else if (cnk
->cflags
== DP_C_LLONG
)
568 cnk
->value
= va_arg (args
, LLONG
);
569 else if (cnk
->cflags
== DP_C_SIZET
)
570 cnk
->value
= va_arg (args
, ssize_t
);
572 cnk
->value
= va_arg (args
, int);
574 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
575 clist
[pnum
].chunks
[i
]->value
= cnk
->value
;
582 if (cnk
->cflags
== DP_C_SHORT
)
583 cnk
->value
= va_arg (args
, unsigned int);
584 else if (cnk
->cflags
== DP_C_LONG
)
585 cnk
->value
= (unsigned long int)va_arg (args
, unsigned long int);
586 else if (cnk
->cflags
== DP_C_LLONG
)
587 cnk
->value
= (LLONG
)va_arg (args
, unsigned LLONG
);
588 else if (cnk
->cflags
== DP_C_SIZET
)
589 cnk
->value
= (size_t)va_arg (args
, size_t);
591 cnk
->value
= (unsigned int)va_arg (args
, unsigned int);
593 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
594 clist
[pnum
].chunks
[i
]->value
= cnk
->value
;
599 if (cnk
->cflags
== DP_C_LDOUBLE
)
600 cnk
->fvalue
= va_arg (args
, LDOUBLE
);
602 cnk
->fvalue
= va_arg (args
, double);
604 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
605 clist
[pnum
].chunks
[i
]->fvalue
= cnk
->fvalue
;
610 cnk
->value
= va_arg (args
, int);
612 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
613 clist
[pnum
].chunks
[i
]->value
= cnk
->value
;
618 cnk
->strvalue
= va_arg (args
, char *);
619 if (!cnk
->strvalue
) cnk
->strvalue
= "(NULL)";
621 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
622 clist
[pnum
].chunks
[i
]->strvalue
= cnk
->strvalue
;
627 cnk
->strvalue
= va_arg (args
, void *);
628 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
629 clist
[pnum
].chunks
[i
]->strvalue
= cnk
->strvalue
;
634 if (cnk
->cflags
== DP_C_CHAR
)
635 cnk
->pnum
= va_arg (args
, char *);
636 else if (cnk
->cflags
== DP_C_SHORT
)
637 cnk
->pnum
= va_arg (args
, short int *);
638 else if (cnk
->cflags
== DP_C_LONG
)
639 cnk
->pnum
= va_arg (args
, long int *);
640 else if (cnk
->cflags
== DP_C_LLONG
)
641 cnk
->pnum
= va_arg (args
, LLONG
*);
642 else if (cnk
->cflags
== DP_C_SIZET
)
643 cnk
->pnum
= va_arg (args
, ssize_t
*);
645 cnk
->pnum
= va_arg (args
, int *);
647 for (i
= 1; i
< clist
[pnum
].num
; i
++) {
648 clist
[pnum
].chunks
[i
]->pnum
= cnk
->pnum
;
660 /* print out the actual string from chunks */
666 if (cnk
->min_star
) min
= cnk
->min_star
->value
;
668 if (cnk
->max_star
) max
= cnk
->max_star
->value
;
674 if (maxlen
!= 0 && maxlen
> currlen
) {
675 if (maxlen
> (currlen
+ cnk
->len
)) len
= cnk
->len
;
676 else len
= maxlen
- currlen
;
678 memcpy(&(buffer
[currlen
]), &(base
[cnk
->start
]), len
);
686 fmtint (buffer
, &currlen
, maxlen
, cnk
->value
, 10, min
, max
, cnk
->flags
);
690 fmtint (buffer
, &currlen
, maxlen
, cnk
->value
, 8, min
, max
, cnk
->flags
);
694 fmtint (buffer
, &currlen
, maxlen
, cnk
->value
, 16, min
, max
, cnk
->flags
);
698 fmtfp (buffer
, &currlen
, maxlen
, cnk
->fvalue
, min
, max
, cnk
->flags
);
702 dopr_outch (buffer
, &currlen
, maxlen
, cnk
->value
);
707 max
= strlen(cnk
->strvalue
);
709 fmtstr (buffer
, &currlen
, maxlen
, cnk
->strvalue
, cnk
->flags
, min
, max
);
713 fmtint (buffer
, &currlen
, maxlen
, (long)(cnk
->strvalue
), 16, min
, max
, cnk
->flags
);
717 if (cnk
->cflags
== DP_C_CHAR
)
718 *((char *)(cnk
->pnum
)) = (char)currlen
;
719 else if (cnk
->cflags
== DP_C_SHORT
)
720 *((short int *)(cnk
->pnum
)) = (short int)currlen
;
721 else if (cnk
->cflags
== DP_C_LONG
)
722 *((long int *)(cnk
->pnum
)) = (long int)currlen
;
723 else if (cnk
->cflags
== DP_C_LLONG
)
724 *((LLONG
*)(cnk
->pnum
)) = (LLONG
)currlen
;
725 else if (cnk
->cflags
== DP_C_SIZET
)
726 *((ssize_t
*)(cnk
->pnum
)) = (ssize_t
)currlen
;
728 *((int *)(cnk
->pnum
)) = (int)currlen
;
732 dopr_outch (buffer
, &currlen
, maxlen
, '%');
742 if (currlen
< maxlen
- 1)
743 buffer
[currlen
] = '\0';
745 buffer
[maxlen
- 1] = '\0';
754 if (chunks
->min_star
) free(chunks
->min_star
);
755 if (chunks
->max_star
) free(chunks
->max_star
);
760 for (pnum
= 0; pnum
< max_pos
; pnum
++) {
761 if (clist
[pnum
].chunks
) free(clist
[pnum
].chunks
);
768 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
769 char *value
, int flags
, int min
, int max
)
771 int padlen
, strln
; /* amount to pad */
774 #ifdef DEBUG_SNPRINTF
775 printf("fmtstr min=%d max=%d s=[%s]\n", min
, max
, value
);
781 for (strln
= 0; strln
< max
&& value
[strln
]; ++strln
); /* strlen */
782 padlen
= min
- strln
;
785 if (flags
& DP_F_MINUS
)
786 padlen
= -padlen
; /* Left Justify */
789 dopr_outch (buffer
, currlen
, maxlen
, ' ');
792 while (*value
&& (cnt
< max
)) {
793 dopr_outch (buffer
, currlen
, maxlen
, *value
++);
797 dopr_outch (buffer
, currlen
, maxlen
, ' ');
802 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
804 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
805 LLONG value
, int base
, int min
, int max
, int flags
)
808 unsigned LLONG uvalue
;
809 char convert
[22+1]; /* 64-bit value in octal: 22 digits + \0 */
811 int spadlen
= 0; /* amount to space pad */
812 int zpadlen
= 0; /* amount to zero pad */
820 if(!(flags
& DP_F_UNSIGNED
)) {
825 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
827 else if (flags
& DP_F_SPACE
)
832 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
836 (caps
? "0123456789ABCDEF":"0123456789abcdef")
837 [uvalue
% (unsigned)base
];
838 uvalue
= (uvalue
/ (unsigned)base
);
839 } while(uvalue
&& (place
< sizeof(convert
)));
840 if (place
== sizeof(convert
)) place
--;
843 zpadlen
= max
- place
;
844 spadlen
= min
- MAX (max
, place
) - (signvalue
? 1 : 0);
845 if (zpadlen
< 0) zpadlen
= 0;
846 if (spadlen
< 0) spadlen
= 0;
847 if (flags
& DP_F_ZERO
) {
848 zpadlen
= MAX(zpadlen
, spadlen
);
851 if (flags
& DP_F_MINUS
)
852 spadlen
= -spadlen
; /* Left Justifty */
854 #ifdef DEBUG_SNPRINTF
855 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
856 zpadlen
, spadlen
, min
, max
, place
);
860 while (spadlen
> 0) {
861 dopr_outch (buffer
, currlen
, maxlen
, ' ');
867 dopr_outch (buffer
, currlen
, maxlen
, signvalue
);
871 while (zpadlen
> 0) {
872 dopr_outch (buffer
, currlen
, maxlen
, '0');
879 dopr_outch (buffer
, currlen
, maxlen
, convert
[--place
]);
881 /* Left Justified spaces */
882 while (spadlen
< 0) {
883 dopr_outch (buffer
, currlen
, maxlen
, ' ');
888 static LDOUBLE
abs_val(LDOUBLE value
)
890 LDOUBLE result
= value
;
898 static LDOUBLE
POW10(int exp
)
910 static LLONG
ROUND(LDOUBLE value
)
914 intpart
= (LLONG
)value
;
915 value
= value
- intpart
;
916 if (value
>= 0.5) intpart
++;
921 /* a replacement for modf that doesn't need the math library. Should
922 be portable, but slow */
923 static double my_modf(double x0
, double *iptr
)
930 for (i
=0;i
<100;i
++) {
932 if (l
<= (x
+1) && l
>= (x
-1)) break;
938 /* yikes! the number is beyond what we can handle. What do we do? */
947 ret
= my_modf(x0
-l
*f
, &i2
);
957 static void fmtfp (char *buffer
, size_t *currlen
, size_t maxlen
,
958 LDOUBLE fvalue
, int min
, int max
, int flags
)
966 int padlen
= 0; /* amount to pad */
975 * AIX manpage says the default is 0, but Solaris says the default
976 * is 6, and sprintf on AIX defaults to 6
981 ufvalue
= abs_val (fvalue
);
986 if (flags
& DP_F_PLUS
) { /* Do a sign (+/i) */
989 if (flags
& DP_F_SPACE
)
995 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
999 if (max
== 0) ufvalue
+= 0.5; /* if max = 0 we must round */
1003 * Sorry, we only support 9 digits past the decimal because of our
1009 /* We "cheat" by converting the fractional part to integer by
1010 * multiplying by a factor of 10
1014 my_modf(temp
, &intpart
);
1016 fracpart
= ROUND((POW10(max
)) * (ufvalue
- intpart
));
1018 if (fracpart
>= POW10(max
)) {
1020 fracpart
-= POW10(max
);
1024 /* Convert integer part */
1027 my_modf(temp
, &intpart
);
1028 idx
= (int) ((temp
-intpart
+0.05)* 10.0);
1029 /* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
1030 /* printf ("%llf, %f, %x\n", temp, intpart, idx); */
1031 iconvert
[iplace
++] =
1032 (caps
? "0123456789ABCDEF":"0123456789abcdef")[idx
];
1033 } while (intpart
&& (iplace
< 311));
1034 if (iplace
== 311) iplace
--;
1035 iconvert
[iplace
] = 0;
1037 /* Convert fractional part */
1041 temp
= fracpart
*0.1;
1042 my_modf(temp
, &fracpart
);
1043 idx
= (int) ((temp
-fracpart
+0.05)* 10.0);
1044 /* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
1045 /* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
1046 fconvert
[fplace
++] =
1047 (caps
? "0123456789ABCDEF":"0123456789abcdef")[idx
];
1048 } while(fracpart
&& (fplace
< 311));
1049 if (fplace
== 311) fplace
--;
1051 fconvert
[fplace
] = 0;
1053 /* -1 for decimal point, another -1 if we are printing a sign */
1054 padlen
= min
- iplace
- max
- 1 - ((signvalue
) ? 1 : 0);
1055 zpadlen
= max
- fplace
;
1056 if (zpadlen
< 0) zpadlen
= 0;
1059 if (flags
& DP_F_MINUS
)
1060 padlen
= -padlen
; /* Left Justifty */
1062 if ((flags
& DP_F_ZERO
) && (padlen
> 0)) {
1064 dopr_outch (buffer
, currlen
, maxlen
, signvalue
);
1068 while (padlen
> 0) {
1069 dopr_outch (buffer
, currlen
, maxlen
, '0');
1073 while (padlen
> 0) {
1074 dopr_outch (buffer
, currlen
, maxlen
, ' ');
1078 dopr_outch (buffer
, currlen
, maxlen
, signvalue
);
1081 dopr_outch (buffer
, currlen
, maxlen
, iconvert
[--iplace
]);
1083 #ifdef DEBUG_SNPRINTF
1084 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace
, zpadlen
);
1088 * Decimal point. This should probably use locale to find the correct
1089 * char to print out.
1092 dopr_outch (buffer
, currlen
, maxlen
, '.');
1094 while (zpadlen
> 0) {
1095 dopr_outch (buffer
, currlen
, maxlen
, '0');
1100 dopr_outch (buffer
, currlen
, maxlen
, fconvert
[--fplace
]);
1103 while (padlen
< 0) {
1104 dopr_outch (buffer
, currlen
, maxlen
, ' ');
1109 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
)
1111 if (*currlen
< maxlen
) {
1112 buffer
[(*currlen
)] = c
;
1117 static struct pr_chunk
*new_chunk(void) {
1118 struct pr_chunk
*new_c
= (struct pr_chunk
*)malloc(sizeof(struct pr_chunk
));
1126 new_c
->min_star
= NULL
;
1128 new_c
->max_star
= NULL
;
1135 new_c
->strvalue
= NULL
;
1142 static int add_cnk_list_entry(struct pr_chunk_x
**list
,
1143 int max_num
, struct pr_chunk
*chunk
) {
1144 struct pr_chunk_x
*l
;
1145 struct pr_chunk
**c
;
1150 if (chunk
->num
> max_num
) {
1153 if (*list
== NULL
) {
1154 l
= (struct pr_chunk_x
*)malloc(sizeof(struct pr_chunk_x
) * max
);
1157 l
= (struct pr_chunk_x
*)realloc(*list
, sizeof(struct pr_chunk_x
) * max
);
1161 for (i
= 0; i
< max
; i
++) {
1162 if ((*list
)[i
].chunks
) free((*list
)[i
].chunks
);
1166 for (i
= pos
; i
< max
; i
++) {
1176 cnum
= l
[i
].num
+ 1;
1177 if (l
[i
].chunks
== NULL
) {
1178 c
= (struct pr_chunk
**)malloc(sizeof(struct pr_chunk
*) * cnum
);
1180 c
= (struct pr_chunk
**)realloc(l
[i
].chunks
, sizeof(struct pr_chunk
*) * cnum
);
1183 for (i
= 0; i
< max
; i
++) {
1184 if (l
[i
].chunks
) free(l
[i
].chunks
);
1188 c
[l
[i
].num
] = chunk
;
1196 int rep_vsnprintf (char *str
, size_t count
, const char *fmt
, va_list args
)
1198 return dopr(str
, count
, fmt
, args
);
1202 /* yes this really must be a ||. Don't muck with this (tridge)
1204 * The logic for these two is that we need our own definition if the
1205 * OS *either* has no definition of *sprintf, or if it does have one
1206 * that doesn't work properly according to the autoconf test.
1208 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1209 int rep_snprintf(char *str
,size_t count
,const char *fmt
,...)
1215 ret
= vsnprintf(str
, count
, fmt
, ap
);
1221 #ifndef HAVE_C99_VSNPRINTF
1222 int rep_printf(const char *fmt
, ...)
1230 ret
= vasprintf(&s
, fmt
, ap
);
1234 fwrite(s
, 1, strlen(s
), stdout
);
1242 #ifndef HAVE_C99_VSNPRINTF
1243 int rep_fprintf(FILE *stream
, const char *fmt
, ...)
1251 ret
= vasprintf(&s
, fmt
, ap
);
1255 fwrite(s
, 1, strlen(s
), stream
);
1265 #if !defined(HAVE_VASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1266 int rep_vasprintf(char **ptr
, const char *format
, va_list ap
)
1272 ret
= vsnprintf(NULL
, 0, format
, ap2
);
1274 if (ret
< 0) return ret
;
1276 (*ptr
) = (char *)malloc(ret
+1);
1277 if (!*ptr
) return -1;
1280 ret
= vsnprintf(*ptr
, ret
+1, format
, ap2
);
1287 #if !defined(HAVE_ASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1288 int rep_asprintf(char **ptr
, const char *format
, ...)
1294 va_start(ap
, format
);
1295 ret
= vasprintf(ptr
, format
, ap
);
1302 #ifdef TEST_SNPRINTF
1304 int sprintf(char *str
,const char *fmt
,...);
1305 int printf(const char *fmt
,...);
1331 double fp_nums
[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 203.9, 0.96, 0.996,
1332 0.9996, 1.996, 4.136, 5.030201, 0.00205,
1347 long int_nums
[] = { -1, 134, 91340, 341, 0203, 1234567890, 0};
1363 char *str_vals
[] = {"hello", "a", "", "a longer string", NULL
};
1364 #ifdef HAVE_LONG_LONG
1369 LLONG ll_nums
[] = { 134, 91340, 341, 0203, 1234567890, 128006186140000000LL, 0};
1380 size_t ss_nums
[] = {134, 91340, 123456789, 0203, 1234567890, 0};
1382 printf ("Testing snprintf format codes against system sprintf...\n");
1384 for (x
= 0; fp_fmt
[x
] ; x
++) {
1385 for (y
= 0; fp_nums
[y
] != 0 ; y
++) {
1386 buf1
[0] = buf2
[0] = '\0';
1387 l1
= snprintf(buf1
, sizeof(buf1
), fp_fmt
[x
], fp_nums
[y
]);
1388 l2
= sprintf (buf2
, fp_fmt
[x
], fp_nums
[y
]);
1389 buf1
[1023] = buf2
[1023] = '\0';
1390 if (strcmp (buf1
, buf2
) || (l1
!= l2
)) {
1391 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1392 fp_fmt
[x
], l1
, buf1
, l2
, buf2
);
1399 for (x
= 0; int_fmt
[x
] ; x
++) {
1400 for (y
= 0; int_nums
[y
] != 0 ; y
++) {
1401 buf1
[0] = buf2
[0] = '\0';
1402 l1
= snprintf(buf1
, sizeof(buf1
), int_fmt
[x
], int_nums
[y
]);
1403 l2
= sprintf (buf2
, int_fmt
[x
], int_nums
[y
]);
1404 buf1
[1023] = buf2
[1023] = '\0';
1405 if (strcmp (buf1
, buf2
) || (l1
!= l2
)) {
1406 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1407 int_fmt
[x
], l1
, buf1
, l2
, buf2
);
1414 for (x
= 0; str_fmt
[x
] ; x
++) {
1415 for (y
= 0; str_vals
[y
] != 0 ; y
++) {
1416 buf1
[0] = buf2
[0] = '\0';
1417 l1
= snprintf(buf1
, sizeof(buf1
), str_fmt
[x
], str_vals
[y
]);
1418 l2
= sprintf (buf2
, str_fmt
[x
], str_vals
[y
]);
1419 buf1
[1023] = buf2
[1023] = '\0';
1420 if (strcmp (buf1
, buf2
) || (l1
!= l2
)) {
1421 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1422 str_fmt
[x
], l1
, buf1
, l2
, buf2
);
1429 #ifdef HAVE_LONG_LONG
1430 for (x
= 0; ll_fmt
[x
] ; x
++) {
1431 for (y
= 0; ll_nums
[y
] != 0 ; y
++) {
1432 buf1
[0] = buf2
[0] = '\0';
1433 l1
= snprintf(buf1
, sizeof(buf1
), ll_fmt
[x
], ll_nums
[y
]);
1434 l2
= sprintf (buf2
, ll_fmt
[x
], ll_nums
[y
]);
1435 buf1
[1023] = buf2
[1023] = '\0';
1436 if (strcmp (buf1
, buf2
) || (l1
!= l2
)) {
1437 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1438 ll_fmt
[x
], l1
, buf1
, l2
, buf2
);
1448 buf1
[0] = buf2
[0] = '\0';
1449 if ((buf3
= malloc(BUFSZ
)) == NULL
) {
1453 memset(buf3
, 'a', BUFSZ
);
1454 snprintf(buf1
, sizeof(buf1
), "%.*s", 1, buf3
);
1456 if (strcmp(buf1
, "a") != 0) {
1457 printf("length limit buf1 '%s' expected 'a'\n", buf1
);
1462 buf1
[0] = buf2
[0] = '\0';
1463 l1
= snprintf(buf1
, sizeof(buf1
), "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1464 l2
= sprintf(buf2
, "%4$*1$d %2$s %3$*1$.*1$f", 3, "pos test", 12.3456, 9);
1465 buf1
[1023] = buf2
[1023] = '\0';
1466 if (strcmp(buf1
, buf2
) || (l1
!= l2
)) {
1467 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1468 "%4$*1$d %2$s %3$*1$.*1$f", l1
, buf1
, l2
, buf2
);
1472 buf1
[0] = buf2
[0] = '\0';
1473 l1
= snprintf(buf1
, sizeof(buf1
), "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1474 l2
= sprintf(buf2
, "%4$*4$d %2$s %3$*4$.*4$f", 3, "pos test", 12.3456, 9);
1475 buf1
[1023] = buf2
[1023] = '\0';
1476 if (strcmp(buf1
, buf2
)) {
1477 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1478 "%4$*1$d %2$s %3$*1$.*1$f", l1
, buf1
, l2
, buf2
);
1482 for (x
= 0; ss_fmt
[x
] ; x
++) {
1483 for (y
= 0; ss_nums
[y
] != 0 ; y
++) {
1484 buf1
[0] = buf2
[0] = '\0';
1485 l1
= snprintf(buf1
, sizeof(buf1
), ss_fmt
[x
], ss_nums
[y
]);
1486 l2
= sprintf (buf2
, ss_fmt
[x
], ss_nums
[y
]);
1487 buf1
[1023] = buf2
[1023] = '\0';
1488 if (strcmp (buf1
, buf2
) || (l1
!= l2
)) {
1489 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1490 ss_fmt
[x
], l1
, buf1
, l2
, buf2
);
1497 buf1
[0] = buf2
[0] = '\0';
1498 l1
= snprintf(buf1
, sizeof(buf1
), "%lld", (LLONG
)1234567890);
1499 l2
= sprintf(buf2
, "%lld", (LLONG
)1234567890);
1500 buf1
[1023] = buf2
[1023] = '\0';
1501 if (strcmp(buf1
, buf2
)) {
1502 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1503 "%lld", l1
, buf1
, l2
, buf2
);
1507 buf1
[0] = buf2
[0] = '\0';
1508 l1
= snprintf(buf1
, sizeof(buf1
), "%Lf", (LDOUBLE
)890.1234567890123);
1509 l2
= sprintf(buf2
, "%Lf", (LDOUBLE
)890.1234567890123);
1510 buf1
[1023] = buf2
[1023] = '\0';
1511 if (strcmp(buf1
, buf2
)) {
1512 printf("snprintf doesn't match Format: %s\n\tsnprintf(%d) = [%s]\n\t sprintf(%d) = [%s]\n",
1513 "%Lf", l1
, buf1
, l2
, buf2
);
1517 printf ("%d tests failed out of %d.\n", fail
, num
);
1519 printf("seeing how many digits we support\n");
1521 double v0
= 0.12345678901234567890123456789012345678901;
1522 for (x
=0; x
<100; x
++) {
1523 double p
= pow(10, x
);
1525 snprintf(buf1
, sizeof(buf1
), "%1.1f", r
);
1526 sprintf(buf2
, "%1.1f", r
);
1527 if (strcmp(buf1
, buf2
)) {
1528 printf("we seem to support %d digits\n", x
-1);
1536 #endif /* TEST_SNPRINTF */