2 * Copyright Patrick Powell 1995
3 * This code is based on code written by Patrick Powell (papowell@astart.com)
4 * It may be used for any purpose as long as this notice remains intact
5 * on all source code distributions
8 /**************************************************************
10 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
11 * A bombproof version of doprnt (dopr) included.
12 * Sigh. This sort of thing is always nasty do deal with. Note that
13 * the version here does not include floating point...
15 * snprintf() is used instead of sprintf() as it does limit checks
16 * for string length. This covers a nasty loophole.
18 * The other functions are there to prevent NULL pointers from
19 * causing nast effects.
22 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
23 * This was ugly. It is still ugly. I opted out of floating point
24 * numbers, but the formatter understands just about everything
25 * from the normal C string format, at least as far as I can tell from
26 * the Solaris 2.5 printf(3S) man page.
28 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
29 * Ok, added some minimal floating point support, which means this
30 * probably requires libm on most operating systems. Don't yet
31 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
32 * was pretty badly broken, it just wasn't being exercised in ways
33 * which showed it, so that's been fixed. Also, formated the code
34 * to mutt conventions, and removed dead code left over from the
35 * original. Also, there is now a builtin-test, just compile with:
36 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
37 * and run snprintf for results.
39 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
40 * The PGP code was using unsigned hexadecimal formats.
41 * Unfortunately, unsigned formats simply didn't work.
43 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
44 * The original code assumed that both snprintf() and vsnprintf() were
45 * missing. Some systems only have snprintf() but not vsnprintf(), so
46 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
48 * Andrew Tridgell (tridge@samba.org) Oct 1998
49 * fixed handling of %.0f
50 * added test for HAVE_LONG_DOUBLE
52 * tridge@samba.org, idra@samba.org, April 2001
53 * got rid of fcvt code (twas buggy and made testing harder)
57 * * ht_snprintf/ht_vsnprintf return number of characters actually
58 * written instead of the number of characters that would
60 * * added '%y' to allow object output using Object's toString() method.
61 * * added '%q[dioux]' for formatting qwords.
62 * * added '%b' for formatting in binary notation.
63 **************************************************************/
72 #include <sys/types.h>
78 #include "system/types.h"
86 #define MIN(a, b) ((a) < (b) ? (a) : (b))
89 #define MAX(a, b) ((a) > (b) ? (a) : (b))
92 #ifdef HAVE_LONG_DOUBLE
93 #define LDOUBLE long double
95 #define LDOUBLE double
99 #define LLONG long long
104 static size_t dopr(char *buffer
, size_t maxlen
, const char *format
,
106 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
107 char *value
, int flags
, int min
, int max
);
108 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
109 long value
, int base
, int min
, int max
, int flags
);
110 static void fmtqword(char *buffer
, size_t *currlen
, size_t maxlen
,
111 sint64 value
, int base
, int min
, int max
, int flags
);
112 static void fmtfp(char *buffer
, size_t *currlen
, size_t maxlen
,
113 LDOUBLE fvalue
, int min
, int max
, int flags
);
114 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
);
117 * dopr(): poor man's version of doprintf
120 /* format read states */
121 #define DP_S_DEFAULT 0
130 /* format flags - Bits */
131 #define DP_F_MINUS (1 << 0)
132 #define DP_F_PLUS (1 << 1)
133 #define DP_F_SPACE (1 << 2)
134 #define DP_F_NUM (1 << 3)
135 #define DP_F_ZERO (1 << 4)
136 #define DP_F_UP (1 << 5)
137 #define DP_F_UNSIGNED (1 << 6)
139 /* Conversion Flags */
142 #define DP_C_LDOUBLE 3
146 #define char_to_int(p) ((p)- '0')
148 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
151 static size_t dopr(char *buffer
, size_t maxlen
, const char *format
, va_list args
)
164 state
= DP_S_DEFAULT
;
165 currlen
= flags
= cflags
= min
= 0;
169 while (state
!= DP_S_DONE
) {
170 if (ch
== '\0') state
= DP_S_DONE
;
177 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
209 if (isdigit((unsigned char)ch
)) {
210 min
= 10*min
+ char_to_int (ch
);
212 } else if (ch
== '*') {
213 min
= va_arg (args
, int);
229 if (isdigit((unsigned char)ch
)) {
230 if (max
< 0) max
= 0;
231 max
= 10*max
+ char_to_int (ch
);
233 } else if (ch
== '*') {
234 max
= va_arg (args
, int);
250 if (ch
== 'l') { /* It's a long long */
256 cflags
= DP_C_LDOUBLE
;
271 flags
|= DP_F_UNSIGNED
;
272 if (cflags
== DP_C_SHORT
) {
273 value
= va_arg (args
, unsigned int);
274 } else if (cflags
== DP_C_LONG
) {
275 value
= (long)va_arg (args
, unsigned long int);
276 } else if (cflags
== DP_C_LLONG
) {
277 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
278 } else if (cflags
== DP_C_QWORD
) {
279 sint64
*q
= va_arg (args
, sint64
*);
280 fmtqword(buffer
, &currlen
, maxlen
, *q
, 2, min
, max
, flags
);
283 value
= (long)va_arg (args
, unsigned int);
285 fmtint (buffer
, &currlen
, maxlen
, value
, 2, min
, max
, flags
);
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 int);
293 } else if (cflags
== DP_C_LLONG
) {
294 value
= va_arg (args
, LLONG
);
295 } else if (cflags
== DP_C_QWORD
) {
296 sint64
*q
= va_arg (args
, sint64
*);
297 fmtqword(buffer
, &currlen
, maxlen
, *q
, 10, min
, max
, flags
);
300 value
= va_arg (args
, int);
302 fmtint (buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
305 flags
|= DP_F_UNSIGNED
;
306 if (cflags
== DP_C_SHORT
) {
307 value
= va_arg (args
, unsigned int);
308 } else if (cflags
== DP_C_LONG
) {
309 value
= (long)va_arg (args
, unsigned long int);
310 } else if (cflags
== DP_C_LLONG
) {
311 value
= (long)va_arg (args
, unsigned LLONG
);
312 } else if (cflags
== DP_C_QWORD
) {
313 sint64
*q
= va_arg (args
, sint64
*);
314 fmtqword(buffer
, &currlen
, maxlen
, *q
, 8, min
, max
, flags
);
317 value
= (long)va_arg (args
, unsigned int);
319 fmtint (buffer
, &currlen
, maxlen
, value
, 8, min
, max
, flags
);
322 flags
|= DP_F_UNSIGNED
;
323 if (cflags
== DP_C_SHORT
) {
324 value
= va_arg (args
, unsigned int);
325 } else if (cflags
== DP_C_LONG
) {
326 value
= (long)va_arg (args
, unsigned long int);
327 } else if (cflags
== DP_C_LLONG
) {
328 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
329 } else if (cflags
== DP_C_QWORD
) {
330 sint64
*q
= va_arg (args
, sint64
*);
331 fmtqword(buffer
, &currlen
, maxlen
, *q
, 10, min
, max
, flags
);
334 value
= (long)va_arg (args
, unsigned int);
336 fmtint (buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
341 flags
|= DP_F_UNSIGNED
;
342 if (cflags
== DP_C_SHORT
) {
343 value
= va_arg (args
, unsigned int);
344 } else if (cflags
== DP_C_LONG
) {
345 value
= (long)va_arg (args
, unsigned long int);
346 } else if (cflags
== DP_C_LLONG
) {
347 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
348 } else if (cflags
== DP_C_QWORD
) {
349 sint64
*q
= va_arg (args
, sint64
*);
350 fmtqword(buffer
, &currlen
, maxlen
, *q
, 16, min
, max
, flags
);
353 value
= (long)va_arg (args
, unsigned int);
355 fmtint (buffer
, &currlen
, maxlen
, value
, 16, min
, max
, flags
);
358 if (cflags
== DP_C_LDOUBLE
)
359 fvalue
= va_arg (args
, LDOUBLE
);
361 fvalue
= va_arg (args
, double);
362 /* um, floating point? */
363 fmtfp (buffer
, &currlen
, maxlen
, fvalue
, min
, max
, flags
);
368 if (cflags
== DP_C_LDOUBLE
)
369 fvalue
= va_arg (args
, LDOUBLE
);
371 fvalue
= va_arg (args
, double);
376 if (cflags
== DP_C_LDOUBLE
)
377 fvalue
= va_arg (args
, LDOUBLE
);
379 fvalue
= va_arg (args
, double);
382 dopr_outch(buffer
, &currlen
, maxlen
, va_arg (args
, int));
385 strvalue
= va_arg (args
, char *);
386 if (!strvalue
) strvalue
= "(null)";
388 max
= strlen(strvalue
);
390 if (min
> 0 && max
>= 0 && min
> max
) max
= min
;
391 fmtstr (buffer
, &currlen
, maxlen
, strvalue
, flags
, min
, max
);
394 strvalue
= va_arg (args
, char *);
395 fmtint (buffer
, &currlen
, maxlen
, (long) strvalue
, 16, min
, max
, flags
);
398 if (cflags
== DP_C_SHORT
) {
400 num
= va_arg (args
, short int *);
402 } else if (cflags
== DP_C_LONG
) {
404 num
= va_arg (args
, long int *);
405 *num
= (long int)currlen
;
406 } else if (cflags
== DP_C_LLONG
) {
408 num
= va_arg (args
, LLONG
*);
409 *num
= (LLONG
)currlen
;
412 num
= va_arg (args
, int *);
417 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
420 /* not supported yet, treat as next char */
425 Object
*obj
= va_arg (args
, Object
*);
427 currlen
+= obj
->toString(buffer
+currlen
, maxlen
- currlen
);
431 max
= strlen(strvalue
);
433 if (min
> 0 && max
>= 0 && min
> max
) max
= min
;
434 fmtstr (buffer
, &currlen
, maxlen
, strvalue
, flags
, min
, max
);
443 state
= DP_S_DEFAULT
;
444 flags
= cflags
= min
= 0;
451 break; /* some picky compilers need this */
455 if (currlen
< maxlen
- 1)
456 buffer
[currlen
] = '\0';
458 buffer
[maxlen
- 1] = '\0';
464 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
465 char *value
, int flags
, int min
, int max
)
467 int padlen
, strln
; /* amount to pad */
470 #ifdef DEBUG_SNPRINTF
471 printf("fmtstr min=%d max=%d s=[%s]\n", min
, max
, value
);
474 for (strln
= 0; value
[strln
]; ++strln
); /* strlen */
475 padlen
= min
- strln
;
478 if (flags
& DP_F_MINUS
)
479 padlen
= -padlen
; /* Left Justify */
481 while ((padlen
> 0) && (cnt
< max
)) {
482 dopr_outch(buffer
, currlen
, maxlen
, ' ');
486 while (*value
&& (cnt
< max
)) {
487 dopr_outch(buffer
, currlen
, maxlen
, *value
++);
490 while ((padlen
< 0) && (cnt
< max
)) {
491 dopr_outch(buffer
, currlen
, maxlen
, ' ');
497 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
499 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
500 long value
, int base
, int min
, int max
, int flags
)
502 #define MAX_CONVERT_PLACES 40
504 unsigned long uvalue
;
505 char convert
[MAX_CONVERT_PLACES
];
507 int spadlen
= 0; /* amount to space pad */
508 int zpadlen
= 0; /* amount to zero pad */
516 if (!(flags
& DP_F_UNSIGNED
)) {
521 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
523 else if (flags
& DP_F_SPACE
)
528 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
532 (caps
? "0123456789ABCDEF":"0123456789abcdef")[uvalue
% (unsigned)base
];
533 uvalue
= (uvalue
/ (unsigned)base
);
534 } while (uvalue
&& (place
< MAX_CONVERT_PLACES
));
535 if (place
== MAX_CONVERT_PLACES
) place
--;
538 zpadlen
= max
- place
;
539 spadlen
= min
- MAX (max
, place
) - (signvalue
? 1 : 0);
540 if (zpadlen
< 0) zpadlen
= 0;
541 if (spadlen
< 0) spadlen
= 0;
542 if (flags
& DP_F_ZERO
) {
543 zpadlen
= MAX(zpadlen
, spadlen
);
546 if (flags
& DP_F_MINUS
)
547 spadlen
= -spadlen
; /* Left Justifty */
549 #ifdef DEBUG_SNPRINTF
550 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
551 zpadlen
, spadlen
, min
, max
, place
);
555 while (spadlen
> 0) {
556 dopr_outch(buffer
, currlen
, maxlen
, ' ');
561 if (signvalue
) dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
565 while (zpadlen
> 0) {
566 dopr_outch(buffer
, currlen
, maxlen
, '0');
572 while (place
> 0) dopr_outch(buffer
, currlen
, maxlen
, convert
[--place
]);
574 /* Left Justified spaces */
575 while (spadlen
< 0) {
576 dopr_outch(buffer
, currlen
, maxlen
, ' ');
581 static void fmtqword(char *buffer
, size_t *currlen
, size_t maxlen
,
582 sint64 value
, int base
, int min
, int max
, int flags
)
584 #undef MAX_CONVERT_PLACES
585 #define MAX_CONVERT_PLACES 80
588 char convert
[MAX_CONVERT_PLACES
];
590 int spadlen
= 0; /* amount to space pad */
591 int zpadlen
= 0; /* amount to zero pad */
594 if (max
< 0) max
= 0;
598 if (!(flags
& DP_F_UNSIGNED
)) {
603 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
605 else if (flags
& DP_F_SPACE
)
610 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
613 uint64 uv
= uvalue
% (uint64
)base
;
615 (caps
? "0123456789ABCDEF":"0123456789abcdef")[uv
];
616 uvalue
= (uvalue
/ (uint64
)base
);
617 } while ((uvalue
!= 0) && (place
< MAX_CONVERT_PLACES
));
618 if (place
== MAX_CONVERT_PLACES
) place
--;
621 zpadlen
= max
- place
;
622 spadlen
= min
- MAX (max
, place
) - (signvalue
? 1 : 0);
623 if (zpadlen
< 0) zpadlen
= 0;
624 if (spadlen
< 0) spadlen
= 0;
625 if (flags
& DP_F_ZERO
) {
626 zpadlen
= MAX(zpadlen
, spadlen
);
629 if (flags
& DP_F_MINUS
) spadlen
= -spadlen
; /* Left Justifty */
631 #ifdef DEBUG_SNPRINTF
632 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
633 zpadlen
, spadlen
, min
, max
, place
);
637 while (spadlen
> 0) {
638 dopr_outch(buffer
, currlen
, maxlen
, ' ');
643 if (signvalue
) dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
647 while (zpadlen
> 0) {
648 dopr_outch(buffer
, currlen
, maxlen
, '0');
654 while (place
> 0) dopr_outch(buffer
, currlen
, maxlen
, convert
[--place
]);
656 /* Left Justified spaces */
657 while (spadlen
< 0) {
658 dopr_outch(buffer
, currlen
, maxlen
, ' ');
663 static LDOUBLE
abs_val(LDOUBLE value
)
665 return (value
< 0) ? -value
: value
;
668 static LDOUBLE
POW10(int exp
)
680 static LLONG
ROUND(LDOUBLE value
)
684 intpart
= (LLONG
)value
;
685 value
= value
- intpart
;
686 if (value
>= 0.5) intpart
++;
691 /* a replacement for modf that doesn't need the math library. Should
692 be portable, but slow */
693 static double my_modf(double x0
, double *iptr
)
700 for (i
=0;i
<100;i
++) {
702 if (l
<= (x
+1) && l
>= (x
-1)) break;
708 /* yikes! the number is beyond what we can handle. What do we do? */
717 ret
= my_modf(x0
-l
*f
, &i2
);
727 static void fmtfp (char *buffer
, size_t *currlen
, size_t maxlen
,
728 LDOUBLE fvalue
, int min
, int max
, int flags
)
736 int padlen
= 0; /* amount to pad */
745 * AIX manpage says the default is 0, but Solaris says the default
746 * is 6, and sprintf on AIX defaults to 6
751 ufvalue
= abs_val (fvalue
);
756 if (flags
& DP_F_PLUS
) { /* Do a sign (+/i) */
759 if (flags
& DP_F_SPACE
)
765 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
769 if (max
== 0) ufvalue
+= 0.5; /* if max = 0 we must round */
773 * Sorry, we only support 16 digits past the decimal because of our
779 /* We "cheat" by converting the fractional part to integer by
780 * multiplying by a factor of 10
784 my_modf(temp
, &intpart
);
786 fracpart
= ROUND((POW10(max
)) * (ufvalue
- intpart
));
788 if (fracpart
>= POW10(max
)) {
790 fracpart
-= POW10(max
);
794 /* Convert integer part */
797 my_modf(intpart
*0.1, &intpart
);
799 index
= (int) ((temp
-intpart
+0.05)* 10.0);
800 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
801 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
803 (caps
? "0123456789ABCDEF":"0123456789abcdef")[index
];
804 } while (intpart
&& (iplace
< 311));
805 if (iplace
== 311) iplace
--;
806 iconvert
[iplace
] = 0;
808 /* Convert fractional part */
813 my_modf(fracpart
*0.1, &fracpart
);
815 index
= (int) ((temp
-fracpart
+0.05)* 10.0);
816 /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
817 /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
819 (caps
? "0123456789ABCDEF":"0123456789abcdef")[index
];
820 } while(fracpart
&& (fplace
< 311));
821 if (fplace
== 311) fplace
--;
823 fconvert
[fplace
] = 0;
825 /* -1 for decimal point, another -1 if we are printing a sign */
826 padlen
= min
- iplace
- max
- 1 - ((signvalue
) ? 1 : 0);
827 zpadlen
= max
- fplace
;
828 if (zpadlen
< 0) zpadlen
= 0;
831 if (flags
& DP_F_MINUS
)
832 padlen
= -padlen
; /* Left Justifty */
834 if ((flags
& DP_F_ZERO
) && (padlen
> 0)) {
836 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
841 dopr_outch(buffer
, currlen
, maxlen
, '0');
846 dopr_outch(buffer
, currlen
, maxlen
, ' ');
850 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
853 dopr_outch(buffer
, currlen
, maxlen
, iconvert
[--iplace
]);
855 #ifdef DEBUG_SNPRINTF
856 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace
, zpadlen
);
860 * Decimal point. This should probably use locale to find the correct
864 dopr_outch(buffer
, currlen
, maxlen
, '.');
867 dopr_outch(buffer
, currlen
, maxlen
, fconvert
[--fplace
]);
870 while (zpadlen
> 0) {
871 dopr_outch(buffer
, currlen
, maxlen
, '0');
876 dopr_outch(buffer
, currlen
, maxlen
, ' ');
881 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
)
883 if (*currlen
< maxlen
) {
884 buffer
[(*currlen
)] = c
;
889 int ht_vsnprintf (char *str
, size_t count
, const char *fmt
, va_list args
)
891 if ((int)count
< 0) count
= 0;
892 int res
= dopr(str
, count
, fmt
, args
);
894 return str
? MIN(res
, (int)count
) : count
;
897 int ht_snprintf(char *str
, size_t count
, const char *fmt
,...)
903 ret
= ht_vsnprintf(str
, count
, fmt
, ap
);
908 int ht_vasprintf(char **ptr
, const char *format
, va_list ap
)
912 ret
= dopr(NULL
, 0, format
, ap
);
918 (*ptr
) = (char *)malloc(ret
+1);
920 ret
= ht_vsnprintf(*ptr
, ret
+1, format
, ap
);
926 int ht_asprintf(char **ptr
, const char *format
, ...)
931 va_start(ap
, format
);
932 ret
= ht_vasprintf(ptr
, format
, ap
);
938 int ht_vfprintf(FILE *file
, const char *fmt
, va_list args
)
942 int ret
= ht_vasprintf(&buf
, fmt
, args
);
947 int ret
= ht_vsnprintf(buf
, sizeof buf
, fmt
, args
);
953 int ht_fprintf(FILE *file
, const char *fmt
, ...)
959 ret
= ht_vfprintf(file
, fmt
, ap
);
966 int ht_vprintf(const char *fmt
, va_list args
)
968 return ht_vfprintf(stdout
, fmt
, args
);
971 int ht_printf(const char *fmt
, ...)
977 ret
= ht_vprintf(fmt
, ap
);
984 #endif //HAVE_SNPRINTF