1 /* Own portable snprintf() implementation */
3 /* These sources aren't the officially distributed version, they are modified
4 * by us (ELinks coders) and some other third-party hackers. See ELinks
5 * ChangeLog for details about changes we made here, comments bellow may give
6 * you some indices as well. --pasky */
9 * Copyright Patrick Powell 1995
10 * This code is based on code written by Patrick Powell (papowell@astart.com)
11 * It may be used for any purpose as long as this notice remains intact
12 * on all source code distributions
15 /**************************************************************
17 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
18 * A bombproof version of doprnt (dopr) included.
19 * Sigh. This sort of thing is always nasty do deal with. Note that
20 * the version here does not include floating point...
22 * snprintf() is used instead of sprintf() as it does limit checks
23 * for string length. This covers a nasty loophole.
25 * The other functions are there to prevent NULL pointers from
26 * causing nast effects.
29 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
30 * This was ugly. It is still ugly. I opted out of floating point
31 * numbers, but the formatter understands just about everything
32 * from the normal C string format, at least as far as I can tell from
33 * the Solaris 2.5 printf(3S) man page.
35 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
36 * Ok, added some minimal floating point support, which means this
37 * probably requires libm on most operating systems. Don't yet
38 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
39 * was pretty badly broken, it just wasn't being exercised in ways
40 * which showed it, so that's been fixed. Also, formated the code
41 * to mutt conventions, and removed dead code left over from the
42 * original. Also, there is now a builtin-test, just compile with:
43 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
44 * and run snprintf for results.
46 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
47 * The PGP code was using unsigned hexadecimal formats.
48 * Unfortunately, unsigned formats simply didn't work.
50 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
51 * The original code assumed that both snprintf() and vsnprintf() were
52 * missing. Some systems only have snprintf() but not vsnprintf(), so
53 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
57 * Andrew Tridgell (tridge@samba.org) Oct 1998
58 * fixed handling of %.0f
59 * added test for HAVE_LONG_DOUBLE
61 * tridge@samba.org, idra@samba.org, April 2001
62 * got rid of fcvt code (twas buggy and made testing harder)
65 * === About ELinks changes see ELinks' ChangeLog.
67 **************************************************************/
69 #ifdef HAVE_CONFIG_H /* for some tests */
77 #include <sys/types.h>
81 #include "util/snprintf.h"
84 /* #define TEST_SNPRINTF */ /* For compiling a standlone test binary. */
87 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
89 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
91 /* make the compiler happy with an empty file */
93 void dummy_snprintf(void);
94 void dummy_snprintf(void) {}
96 #else /* defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) */
98 /* TODO: Don't do all this if we just need to get own snprintf() wrapper to
99 * system-provided vsnprintf(). (What an unlikely case, isn't it?) */
101 #ifdef HAVE_LONG_DOUBLE
102 #define LDOUBLE long double
104 #define LDOUBLE double
107 #ifdef HAVE_LONG_LONG
108 #define LLONG long long
113 static size_t dopr(char *buffer
, size_t maxlen
, const char *format
,
115 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
116 char *value
, int flags
, int min
, int max
);
117 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
118 long value
, int base
, int min
, int max
, int flags
);
119 static void fmtfp(char *buffer
, size_t *currlen
, size_t maxlen
,
120 LDOUBLE fvalue
, int min
, int max
, int flags
);
121 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
);
124 * dopr(): poor man's version of doprintf
127 /* format read states */
128 #define DP_S_DEFAULT 0
137 /* format flags - Bits */
138 #define DP_F_MINUS (1 << 0)
139 #define DP_F_PLUS (1 << 1)
140 #define DP_F_SPACE (1 << 2)
141 #define DP_F_NUM (1 << 3)
142 #define DP_F_ZERO (1 << 4)
143 #define DP_F_UP (1 << 5)
144 #define DP_F_UNSIGNED (1 << 6)
146 /* Conversion Flags */
149 #define DP_C_LDOUBLE 3
152 #define char_to_int(p) ((p)- '0')
154 static char hexnumbers
[] = "0123456789abcdef";
155 static char HEXnumbers
[] = "0123456789ABCDEF";
158 dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
)
160 if (*currlen
< maxlen
)
161 buffer
[(*currlen
)] = c
;
167 dopr(char *buffer
, size_t maxlen
, const char *format
, va_list args_in
)
181 VA_COPY(args
, args_in
);
183 state
= DP_S_DEFAULT
;
185 flags
= cflags
= min
= 0;
189 while (state
!= DP_S_DONE
) {
198 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
229 if (isdigit((unsigned char) ch
)) {
230 min
= 10 * min
+ char_to_int(ch
);
232 } else if (ch
== '*') {
233 min
= va_arg(args
, int);
249 if (isdigit((unsigned char) ch
)) {
252 max
= 10 * max
+ char_to_int(ch
);
254 } else if (ch
== '*') {
255 max
= va_arg(args
, int);
271 if (ch
== 'l') { /* It's a long long */
277 cflags
= DP_C_LDOUBLE
;
289 if (cflags
== DP_C_SHORT
)
290 value
= va_arg(args
, int);
291 else if (cflags
== DP_C_LONG
)
292 value
= va_arg(args
, long);
293 else if (cflags
== DP_C_LLONG
)
294 value
= va_arg(args
, LLONG
);
296 value
= va_arg(args
, int);
297 fmtint(buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
300 flags
|= DP_F_UNSIGNED
;
301 if (cflags
== DP_C_SHORT
)
302 value
= va_arg(args
, unsigned int);
303 else if (cflags
== DP_C_LONG
)
304 value
= (long) va_arg(args
, unsigned long);
305 else if (cflags
== DP_C_LLONG
)
306 value
= (long) va_arg(args
, unsigned LLONG
);
308 value
= (long) va_arg(args
, unsigned int);
309 fmtint(buffer
, &currlen
, maxlen
, value
, 8, min
, max
, flags
);
312 flags
|= DP_F_UNSIGNED
;
313 if (cflags
== DP_C_SHORT
)
314 value
= va_arg(args
, unsigned int);
315 else if (cflags
== DP_C_LONG
)
316 value
= (long) va_arg(args
, unsigned long);
317 else if (cflags
== DP_C_LLONG
)
318 value
= (LLONG
) va_arg(args
, unsigned LLONG
);
320 value
= (long) va_arg(args
, unsigned int);
321 fmtint(buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
326 flags
|= DP_F_UNSIGNED
;
327 if (cflags
== DP_C_SHORT
)
328 value
= va_arg(args
, unsigned int);
329 else if (cflags
== DP_C_LONG
)
330 value
= (long) va_arg(args
, unsigned long);
331 else if (cflags
== DP_C_LLONG
)
332 value
= (LLONG
) va_arg(args
, unsigned LLONG
);
334 value
= (long) va_arg(args
, unsigned int);
335 fmtint(buffer
, &currlen
, maxlen
, value
, 16, min
, max
, flags
);
338 if (cflags
== DP_C_LDOUBLE
)
339 fvalue
= va_arg(args
, LDOUBLE
);
341 fvalue
= va_arg(args
, double);
342 /* um, floating point? */
343 fmtfp(buffer
, &currlen
, maxlen
, fvalue
, min
, max
, flags
);
348 if (cflags
== DP_C_LDOUBLE
)
349 fvalue
= va_arg(args
, LDOUBLE
);
351 fvalue
= va_arg(args
, double);
352 fmtfp(buffer
, &currlen
, maxlen
, fvalue
, min
, max
, flags
);
357 if (cflags
== DP_C_LDOUBLE
)
358 fvalue
= va_arg(args
, LDOUBLE
);
360 fvalue
= va_arg(args
, double);
361 fmtfp(buffer
, &currlen
, maxlen
, fvalue
, min
, max
, flags
);
364 dopr_outch(buffer
, &currlen
, maxlen
, va_arg(args
, int));
367 strvalue
= va_arg(args
, char *);
368 if (!strvalue
) strvalue
= "(NULL)";
370 max
= strlen(strvalue
);
372 if (min
> 0 && max
>= 0 && min
> max
) max
= min
;
373 fmtstr(buffer
, &currlen
, maxlen
, strvalue
, flags
, min
, max
);
376 strvalue
= va_arg(args
, void *);
377 fmtint(buffer
, &currlen
, maxlen
, (long) strvalue
, 16, min
, max
, flags
);
380 if (cflags
== DP_C_SHORT
) {
383 num
= va_arg(args
, short *);
385 } else if (cflags
== DP_C_LONG
) {
388 num
= va_arg(args
, long *);
389 *num
= (long) currlen
;
390 } else if (cflags
== DP_C_LLONG
) {
393 num
= va_arg(args
, LLONG
*);
394 *num
= (LLONG
) currlen
;
398 num
= va_arg(args
, int *);
403 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
406 /* not supported yet, treat as next char */
414 state
= DP_S_DEFAULT
;
415 flags
= cflags
= min
= 0;
422 break; /* some picky compilers need this */
426 if (currlen
< maxlen
- 1)
427 buffer
[currlen
] = '\0';
429 buffer
[maxlen
- 1] = '\0';
436 fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
437 char *value
, int flags
, int min
, int max
)
439 int padlen
, strln
; /* amount to pad */
442 #ifdef DEBUG_SNPRINTF
443 printf("fmtstr min=%d max=%d s=[%s]\n", min
, max
, value
);
445 if (value
== 0) value
= "<NULL>";
447 for (strln
= 0; value
[strln
]; ++strln
); /* strlen */
448 padlen
= min
- strln
;
451 if (flags
& DP_F_MINUS
)
452 padlen
= -padlen
; /* Left Justify */
454 while ((padlen
> 0) && (cnt
< max
)) {
455 dopr_outch(buffer
, currlen
, maxlen
, ' ');
459 while (*value
&& (cnt
< max
)) {
460 dopr_outch(buffer
, currlen
, maxlen
, *value
++);
463 while ((padlen
< 0) && (cnt
< max
)) {
464 dopr_outch(buffer
, currlen
, maxlen
, ' ');
470 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
472 fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
473 long value
, int base
, int min
, int max
, int flags
)
476 unsigned long uvalue
;
478 char *numbers
= (char *) &hexnumbers
;
480 int spadlen
= 0; /* amount to space pad */
481 int zpadlen
= 0; /* amount to zero pad */
488 if (!(flags
& DP_F_UNSIGNED
)) {
493 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
495 else if (flags
& DP_F_SPACE
)
500 if (flags
& DP_F_UP
) {
501 /* Should characters be upper case? */
502 numbers
= (char *) &HEXnumbers
;
506 convert
[place
++] = numbers
[uvalue
% (unsigned) base
];
507 uvalue
= (uvalue
/ (unsigned) base
);
508 } while (uvalue
&& (place
< 20));
509 if (place
== 20) place
--;
512 zpadlen
= max
- place
;
513 spadlen
= min
- MAX(max
, place
) - (signvalue
? 1 : 0);
514 if (zpadlen
< 0) zpadlen
= 0;
515 if (spadlen
< 0) spadlen
= 0;
516 if (flags
& DP_F_ZERO
) {
517 zpadlen
= MAX(zpadlen
, spadlen
);
520 if (flags
& DP_F_MINUS
)
521 spadlen
= -spadlen
; /* Left Justifty */
523 #ifdef DEBUG_SNPRINTF
524 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
525 zpadlen
, spadlen
, min
, max
, place
);
529 while (spadlen
> 0) {
530 dopr_outch(buffer
, currlen
, maxlen
, ' ');
536 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
539 while (zpadlen
> 0) {
540 dopr_outch(buffer
, currlen
, maxlen
, '0');
546 dopr_outch(buffer
, currlen
, maxlen
, convert
[--place
]);
548 /* Left Justified spaces */
549 while (spadlen
< 0) {
550 dopr_outch(buffer
, currlen
, maxlen
, ' ');
556 my_abs(LDOUBLE value
)
558 LDOUBLE result
= value
;
580 my_round(LDOUBLE value
)
584 intpart
= (LLONG
) value
;
585 value
= value
- intpart
;
586 if (value
>= 0.5) intpart
++;
591 /* a replacement for modf that doesn't need the math library. Should
592 be portable, but slow */
594 my_modf(double x0
, double *iptr
)
601 for (i
= 0; i
< 100; i
++) {
603 if (l
<= (x
+ 1) && l
>= (x
- 1)) break;
609 /* yikes! the number is beyond what we can handle. What do we do? */
618 ret
= my_modf(x0
- l
* f
, &i2
);
619 (*iptr
) = l
* f
+ i2
;
628 fmtfp(char *buffer
, size_t *currlen
, size_t maxlen
,
629 LDOUBLE fvalue
, int min
, int max
, int flags
)
635 char *numbers
= (char *) &hexnumbers
;
638 int padlen
= 0; /* amount to pad */
647 * AIX manpage says the default is 0, but Solaris says the default
648 * is 6, and sprintf on AIX defaults to 6
653 ufvalue
= my_abs(fvalue
);
658 if (flags
& DP_F_PLUS
) { /* Do a sign (+/i) */
661 if (flags
& DP_F_SPACE
)
667 if (flags
& DP_F_UP
) {
668 caps
= 1; /* Should characters be upper case? */
669 numbers
= (char *) &HEXnumbers
;
674 if (max
== 0) ufvalue
+= 0.5; /* if max = 0 we must round */
678 * Sorry, we only support 16 digits past the decimal because of our
686 /* We "cheat" by converting the fractional part to integer by
687 * multiplying by a factor of 10
691 my_modf(temp
, &intpart
);
693 fracpart
= my_round(pow
* (ufvalue
- intpart
));
695 if (fracpart
>= pow
) {
700 /* Convert integer part */
702 temp
= intpart
* 0.1;
703 my_modf(temp
, &intpart
);
704 index
= (int) ((temp
- intpart
+ 0.05) * 10.0);
706 index
= (int) (((double) (temp
*0.1) -intpart
+0.05) *10.0);
707 printf ("%llf, %f, %x\n", temp
, intpart
, index
);
709 iconvert
[iplace
++] = numbers
[index
];
710 } while (intpart
&& (iplace
< 311));
711 if (iplace
== 311) iplace
--;
712 iconvert
[iplace
] = 0;
714 /* Convert fractional part */
717 temp
= fracpart
* 0.1;
718 my_modf(temp
, &fracpart
);
719 index
= (int) ((temp
- fracpart
+ 0.05) * 10.0);
721 index
= (int) ((((temp
/10) -fracpart
) +0.05) *10);
722 printf ("%lf, %lf, %ld\n", temp
, fracpart
, index
);
724 fconvert
[fplace
++] = numbers
[index
];
725 } while (fracpart
&& (fplace
< 311));
726 if (fplace
== 311) fplace
--;
728 fconvert
[fplace
] = 0;
730 /* -1 for decimal point, another -1 if we are printing a sign */
731 padlen
= min
- iplace
- max
- 1 - ((signvalue
) ? 1 : 0);
732 zpadlen
= max
- fplace
;
733 if (zpadlen
< 0) zpadlen
= 0;
736 if (flags
& DP_F_MINUS
)
737 padlen
= -padlen
; /* Left Justifty */
739 if ((flags
& DP_F_ZERO
) && (padlen
> 0)) {
741 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
746 dopr_outch(buffer
, currlen
, maxlen
, '0');
751 dopr_outch(buffer
, currlen
, maxlen
, ' ');
755 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
758 dopr_outch(buffer
, currlen
, maxlen
, iconvert
[--iplace
]);
760 #ifdef DEBUG_SNPRINTF
761 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace
, zpadlen
);
765 * Decimal point. This should probably use locale to find the correct
769 dopr_outch(buffer
, currlen
, maxlen
, '.');
771 while (zpadlen
> 0) {
772 dopr_outch(buffer
, currlen
, maxlen
, '0');
777 dopr_outch(buffer
, currlen
, maxlen
, fconvert
[--fplace
]);
781 dopr_outch(buffer
, currlen
, maxlen
, ' ');
787 /* Yes the following preprocessor conditions really must be a ||. Don't muck
788 * with this. -- tridge
790 * The logic for these is that we need our own definition if the OS *either*
791 * has no definition of *sprintf, or if it does have one that doesn't work
792 * properly according to the autoconf test. -- mbp */
794 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
796 elinks_vsnprintf(char *str
, size_t count
, const char *fmt
, va_list args
)
798 return dopr(str
, count
, fmt
, args
);
802 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
804 elinks_snprintf(char *str
, size_t count
, const char *fmt
, ...)
810 /* snprintf.h makes this to expand to the right thing. */
811 ret
= vsnprintf(str
, count
, fmt
, ap
);
818 #endif /* defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) */
821 #ifndef HAVE_VASPRINTF
823 elinks_vasprintf(char **ptr
, const char *format
, va_list ap
)
830 ret
= vsnprintf(NULL
, 0, format
, ap2
);
831 if (ret
<= 0) return ret
;
833 /* Should we use mem_alloc() here instead of malloc() ??? --Zas */
834 /* No: This is an alternative to the system vasprintf.
835 * When the system provides vasprintf, it will use malloc(),
836 * which means that the string will need to be freed with free().
837 * If we did use mem_alloc here in our alternative to the system
838 * vasprintf, we would need to change all users of the memory
839 * allocated by vasprintf to take into account whether the system
840 * vasprintf (and therefore malloc) or our own vasprintf
841 * (and therefore mem_alloc) were used. -- Miciah */
842 (*ptr
) = (char *) malloc(ret
+ 1);
843 if (!*ptr
) return -1;
847 return vsnprintf(*ptr
, ret
+ 1, format
, ap2
);
852 #ifndef HAVE_ASPRINTF
854 elinks_asprintf(char **ptr
, const char *format
, ...)
860 va_start(ap
, format
);
861 ret
= vasprintf(ptr
, format
, ap
);
869 asprintfa(const char *fmt
, ...)
875 str
= vasprintfa(fmt
, ap
);
886 int sprintf(char *str
,const char *fmt
,...);
911 double fp_nums
[] = { 6442452944.1234, -1.5, 134.21, 91340.2,
912 341.1234, 0203.9, 0.96, 0.996,
913 0.9996, 1.996, 4.136, 5.030201, 0};
927 long int_nums
[] = { -1, 134, 91340, 341, 0203, 0};
941 char *str_vals
[] = {"hello", "a", "", "a longer string", NULL
};
946 printf ("Testing snprintf format codes against system sprintf...\n");
948 for (x
= 0; fp_fmt
[x
] ; x
++) {
949 for (y
= 0; fp_nums
[y
] != 0 ; y
++) {
950 int l1
= snprintf(NULL
, 0, fp_fmt
[x
], fp_nums
[y
]);
951 int l2
= snprintf(buf1
, sizeof(buf1
), fp_fmt
[x
], fp_nums
[y
]);
953 sprintf(buf2
, fp_fmt
[x
], fp_nums
[y
]);
954 if (strcmp(buf1
, buf2
)) {
955 printf("snprintf doesn't match Format: %s\n\t"
956 "snprintf = [%s]\n\t sprintf = [%s]\n",
957 fp_fmt
[x
], buf1
, buf2
);
961 printf("snprintf l1 != l2 (%d %d) %s\n", l1
, l2
, fp_fmt
[x
]);
968 for (x
= 0; int_fmt
[x
] ; x
++) {
969 for (y
= 0; int_nums
[y
] != 0 ; y
++) {
970 int l1
= snprintf(NULL
, 0, int_fmt
[x
], int_nums
[y
]);
971 int l2
= snprintf(buf1
, sizeof(buf1
), int_fmt
[x
], int_nums
[y
]);
973 sprintf(buf2
, int_fmt
[x
], int_nums
[y
]);
974 if (strcmp(buf1
, buf2
)) {
975 printf("snprintf doesn't match Format: %s\n\t"
976 "snprintf = [%s]\n\t sprintf = [%s]\n",
977 int_fmt
[x
], buf1
, buf2
);
981 printf("snprintf l1 != l2 (%d %d) %s\n", l1
, l2
, int_fmt
[x
]);
988 for (x
= 0; str_fmt
[x
] ; x
++) {
989 for (y
= 0; str_vals
[y
] != 0 ; y
++) {
990 int l1
= snprintf(NULL
, 0, str_fmt
[x
], str_vals
[y
]);
991 int l2
= snprintf(buf1
, sizeof(buf1
), str_fmt
[x
], str_vals
[y
]);
993 sprintf(buf2
, str_fmt
[x
], str_vals
[y
]);
994 if (strcmp(buf1
, buf2
)) {
995 printf("snprintf doesn't match Format: %s\n\t"
996 "snprintf = [%s]\n\t sprintf = [%s]\n",
997 str_fmt
[x
], buf1
, buf2
);
1001 printf("snprintf l1 != l2 (%d %d) %s\n", l1
, l2
, str_fmt
[x
]);
1008 printf ("%d tests failed out of %d.\n", fail
, num
);
1010 printf("seeing how many digits we support\n");
1012 double v0
= 0.12345678901234567890123456789012345678901;
1014 for ( x
= 0; x
< 100; x
++) {
1015 double p
= pow(10, x
);
1018 snprintf(buf1
, sizeof(buf1
), "%1.1f", r
);
1019 sprintf(buf2
,"%1.1f", r
);
1020 if (strcmp(buf1
, buf2
)) {
1021 printf("we seem to support %d digits\n", x
-1);
1029 #endif /* SNPRINTF_TEST */