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"
82 #define MIN(a, b) ((a) < (b) ? (a) : (b))
85 #define MAX(a, b) ((a) > (b) ? (a) : (b))
88 #ifdef HAVE_LONG_DOUBLE
89 #define LDOUBLE long double
91 #define LDOUBLE double
95 #define LLONG long long
100 static size_t dopr(char *buffer
, size_t maxlen
, const char *format
,
102 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
103 char *value
, int flags
, int min
, int max
);
104 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
105 long value
, int base
, int min
, int max
, int flags
);
106 static void fmtqword(char *buffer
, size_t *currlen
, size_t maxlen
,
107 sint64 value
, int base
, int min
, int max
, int flags
);
108 static void fmtfp(char *buffer
, size_t *currlen
, size_t maxlen
,
109 LDOUBLE fvalue
, int min
, int max
, int flags
);
110 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
);
113 * dopr(): poor man's version of doprintf
116 /* format read states */
117 #define DP_S_DEFAULT 0
126 /* format flags - Bits */
127 #define DP_F_MINUS (1 << 0)
128 #define DP_F_PLUS (1 << 1)
129 #define DP_F_SPACE (1 << 2)
130 #define DP_F_NUM (1 << 3)
131 #define DP_F_ZERO (1 << 4)
132 #define DP_F_UP (1 << 5)
133 #define DP_F_UNSIGNED (1 << 6)
135 /* Conversion Flags */
138 #define DP_C_LDOUBLE 3
142 #define char_to_int(p) ((p)- '0')
144 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
147 static size_t dopr(char *buffer
, size_t maxlen
, const char *format
, va_list args
)
160 state
= DP_S_DEFAULT
;
161 currlen
= flags
= cflags
= min
= 0;
165 while (state
!= DP_S_DONE
) {
166 if (ch
== '\0') state
= DP_S_DONE
;
173 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
205 if (isdigit((unsigned char)ch
)) {
206 min
= 10*min
+ char_to_int (ch
);
208 } else if (ch
== '*') {
209 min
= va_arg (args
, int);
225 if (isdigit((unsigned char)ch
)) {
226 if (max
< 0) max
= 0;
227 max
= 10*max
+ char_to_int (ch
);
229 } else if (ch
== '*') {
230 max
= va_arg (args
, int);
246 if (ch
== 'l') { /* It's a long long */
252 cflags
= DP_C_LDOUBLE
;
267 flags
|= DP_F_UNSIGNED
;
268 if (cflags
== DP_C_SHORT
) {
269 value
= va_arg (args
, unsigned int);
270 } else if (cflags
== DP_C_LONG
) {
271 value
= (long)va_arg (args
, unsigned long int);
272 } else if (cflags
== DP_C_LLONG
) {
273 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
274 } else if (cflags
== DP_C_QWORD
) {
275 sint64
*q
= va_arg (args
, sint64
*);
276 fmtqword(buffer
, &currlen
, maxlen
, *q
, 2, min
, max
, flags
);
279 value
= (long)va_arg (args
, unsigned int);
281 fmtint (buffer
, &currlen
, maxlen
, value
, 2, min
, max
, flags
);
285 if (cflags
== DP_C_SHORT
) {
286 value
= va_arg (args
, int);
287 } else if (cflags
== DP_C_LONG
) {
288 value
= va_arg (args
, long int);
289 } else if (cflags
== DP_C_LLONG
) {
290 value
= va_arg (args
, LLONG
);
291 } else if (cflags
== DP_C_QWORD
) {
292 sint64
*q
= va_arg (args
, sint64
*);
293 fmtqword(buffer
, &currlen
, maxlen
, *q
, 10, min
, max
, flags
);
296 value
= va_arg (args
, int);
298 fmtint (buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
301 flags
|= DP_F_UNSIGNED
;
302 if (cflags
== DP_C_SHORT
) {
303 value
= va_arg (args
, unsigned int);
304 } else if (cflags
== DP_C_LONG
) {
305 value
= (long)va_arg (args
, unsigned long int);
306 } else if (cflags
== DP_C_LLONG
) {
307 value
= (long)va_arg (args
, unsigned LLONG
);
308 } else if (cflags
== DP_C_QWORD
) {
309 sint64
*q
= va_arg (args
, sint64
*);
310 fmtqword(buffer
, &currlen
, maxlen
, *q
, 8, min
, max
, flags
);
313 value
= (long)va_arg (args
, unsigned int);
315 fmtint (buffer
, &currlen
, maxlen
, value
, 8, min
, max
, flags
);
318 flags
|= DP_F_UNSIGNED
;
319 if (cflags
== DP_C_SHORT
) {
320 value
= va_arg (args
, unsigned int);
321 } else if (cflags
== DP_C_LONG
) {
322 value
= (long)va_arg (args
, unsigned long int);
323 } else if (cflags
== DP_C_LLONG
) {
324 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
325 } else if (cflags
== DP_C_QWORD
) {
326 sint64
*q
= va_arg (args
, sint64
*);
327 fmtqword(buffer
, &currlen
, maxlen
, *q
, 10, min
, max
, flags
);
330 value
= (long)va_arg (args
, unsigned int);
332 fmtint (buffer
, &currlen
, maxlen
, value
, 10, min
, max
, flags
);
337 flags
|= DP_F_UNSIGNED
;
338 if (cflags
== DP_C_SHORT
) {
339 value
= va_arg (args
, unsigned int);
340 } else if (cflags
== DP_C_LONG
) {
341 value
= (long)va_arg (args
, unsigned long int);
342 } else if (cflags
== DP_C_LLONG
) {
343 value
= (LLONG
)va_arg (args
, unsigned LLONG
);
344 } else if (cflags
== DP_C_QWORD
) {
345 sint64
*q
= va_arg (args
, sint64
*);
346 fmtqword(buffer
, &currlen
, maxlen
, *q
, 16, min
, max
, flags
);
349 value
= (long)va_arg (args
, unsigned int);
351 fmtint (buffer
, &currlen
, maxlen
, value
, 16, min
, max
, flags
);
354 if (cflags
== DP_C_LDOUBLE
)
355 fvalue
= va_arg (args
, LDOUBLE
);
357 fvalue
= va_arg (args
, double);
358 /* um, floating point? */
359 fmtfp (buffer
, &currlen
, maxlen
, fvalue
, min
, max
, flags
);
364 if (cflags
== DP_C_LDOUBLE
)
365 fvalue
= va_arg (args
, LDOUBLE
);
367 fvalue
= va_arg (args
, double);
372 if (cflags
== DP_C_LDOUBLE
)
373 fvalue
= va_arg (args
, LDOUBLE
);
375 fvalue
= va_arg (args
, double);
378 dopr_outch(buffer
, &currlen
, maxlen
, va_arg (args
, int));
381 strvalue
= va_arg (args
, char *);
382 if (!strvalue
) strvalue
= "(null)";
384 max
= strlen(strvalue
);
386 if (min
> 0 && max
>= 0 && min
> max
) max
= min
;
387 fmtstr (buffer
, &currlen
, maxlen
, strvalue
, flags
, min
, max
);
390 strvalue
= va_arg (args
, char *);
391 fmtint (buffer
, &currlen
, maxlen
, (long) strvalue
, 16, min
, max
, flags
);
394 if (cflags
== DP_C_SHORT
) {
396 num
= va_arg (args
, short int *);
398 } else if (cflags
== DP_C_LONG
) {
400 num
= va_arg (args
, long int *);
401 *num
= (long int)currlen
;
402 } else if (cflags
== DP_C_LLONG
) {
404 num
= va_arg (args
, LLONG
*);
405 *num
= (LLONG
)currlen
;
408 num
= va_arg (args
, int *);
413 dopr_outch(buffer
, &currlen
, maxlen
, ch
);
416 /* not supported yet, treat as next char */
421 Object
*obj
= va_arg (args
, Object
*);
423 currlen
+= obj
->toString(buffer
+currlen
, maxlen
- currlen
);
427 max
= strlen(strvalue
);
429 if (min
> 0 && max
>= 0 && min
> max
) max
= min
;
430 fmtstr (buffer
, &currlen
, maxlen
, strvalue
, flags
, min
, max
);
439 state
= DP_S_DEFAULT
;
440 flags
= cflags
= min
= 0;
447 break; /* some picky compilers need this */
451 if (currlen
< maxlen
- 1)
452 buffer
[currlen
] = '\0';
454 buffer
[maxlen
- 1] = '\0';
460 static void fmtstr(char *buffer
, size_t *currlen
, size_t maxlen
,
461 char *value
, int flags
, int min
, int max
)
463 int padlen
, strln
; /* amount to pad */
466 #ifdef DEBUG_SNPRINTF
467 printf("fmtstr min=%d max=%d s=[%s]\n", min
, max
, value
);
470 for (strln
= 0; value
[strln
]; ++strln
); /* strlen */
471 padlen
= min
- strln
;
474 if (flags
& DP_F_MINUS
)
475 padlen
= -padlen
; /* Left Justify */
477 while ((padlen
> 0) && (cnt
< max
)) {
478 dopr_outch(buffer
, currlen
, maxlen
, ' ');
482 while (*value
&& (cnt
< max
)) {
483 dopr_outch(buffer
, currlen
, maxlen
, *value
++);
486 while ((padlen
< 0) && (cnt
< max
)) {
487 dopr_outch(buffer
, currlen
, maxlen
, ' ');
493 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
495 static void fmtint(char *buffer
, size_t *currlen
, size_t maxlen
,
496 long value
, int base
, int min
, int max
, int flags
)
498 #define MAX_CONVERT_PLACES 40
500 unsigned long uvalue
;
501 char convert
[MAX_CONVERT_PLACES
];
503 int spadlen
= 0; /* amount to space pad */
504 int zpadlen
= 0; /* amount to zero pad */
512 if (!(flags
& DP_F_UNSIGNED
)) {
517 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
519 else if (flags
& DP_F_SPACE
)
524 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
528 (caps
? "0123456789ABCDEF":"0123456789abcdef")[uvalue
% (unsigned)base
];
529 uvalue
= (uvalue
/ (unsigned)base
);
530 } while (uvalue
&& (place
< MAX_CONVERT_PLACES
));
531 if (place
== MAX_CONVERT_PLACES
) place
--;
534 zpadlen
= max
- place
;
535 spadlen
= min
- MAX (max
, place
) - (signvalue
? 1 : 0);
536 if (zpadlen
< 0) zpadlen
= 0;
537 if (spadlen
< 0) spadlen
= 0;
538 if (flags
& DP_F_ZERO
) {
539 zpadlen
= MAX(zpadlen
, spadlen
);
542 if (flags
& DP_F_MINUS
)
543 spadlen
= -spadlen
; /* Left Justifty */
545 #ifdef DEBUG_SNPRINTF
546 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
547 zpadlen
, spadlen
, min
, max
, place
);
551 while (spadlen
> 0) {
552 dopr_outch(buffer
, currlen
, maxlen
, ' ');
557 if (signvalue
) dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
561 while (zpadlen
> 0) {
562 dopr_outch(buffer
, currlen
, maxlen
, '0');
568 while (place
> 0) dopr_outch(buffer
, currlen
, maxlen
, convert
[--place
]);
570 /* Left Justified spaces */
571 while (spadlen
< 0) {
572 dopr_outch(buffer
, currlen
, maxlen
, ' ');
577 static void fmtqword(char *buffer
, size_t *currlen
, size_t maxlen
,
578 sint64 value
, int base
, int min
, int max
, int flags
)
580 #undef MAX_CONVERT_PLACES
581 #define MAX_CONVERT_PLACES 80
584 char convert
[MAX_CONVERT_PLACES
];
586 int spadlen
= 0; /* amount to space pad */
587 int zpadlen
= 0; /* amount to zero pad */
590 if (max
< 0) max
= 0;
594 if (!(flags
& DP_F_UNSIGNED
)) {
599 if (flags
& DP_F_PLUS
) /* Do a sign (+/i) */
601 else if (flags
& DP_F_SPACE
)
606 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
609 uint64 uv
= uvalue
% (uint64
)base
;
611 (caps
? "0123456789ABCDEF":"0123456789abcdef")[uv
];
612 uvalue
= (uvalue
/ (uint64
)base
);
613 } while ((uvalue
!= 0) && (place
< MAX_CONVERT_PLACES
));
614 if (place
== MAX_CONVERT_PLACES
) place
--;
617 zpadlen
= max
- place
;
618 spadlen
= min
- MAX (max
, place
) - (signvalue
? 1 : 0);
619 if (zpadlen
< 0) zpadlen
= 0;
620 if (spadlen
< 0) spadlen
= 0;
621 if (flags
& DP_F_ZERO
) {
622 zpadlen
= MAX(zpadlen
, spadlen
);
625 if (flags
& DP_F_MINUS
) spadlen
= -spadlen
; /* Left Justifty */
627 #ifdef DEBUG_SNPRINTF
628 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
629 zpadlen
, spadlen
, min
, max
, place
);
633 while (spadlen
> 0) {
634 dopr_outch(buffer
, currlen
, maxlen
, ' ');
639 if (signvalue
) dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
643 while (zpadlen
> 0) {
644 dopr_outch(buffer
, currlen
, maxlen
, '0');
650 while (place
> 0) dopr_outch(buffer
, currlen
, maxlen
, convert
[--place
]);
652 /* Left Justified spaces */
653 while (spadlen
< 0) {
654 dopr_outch(buffer
, currlen
, maxlen
, ' ');
659 static LDOUBLE
abs_val(LDOUBLE value
)
661 return (value
< 0) ? -value
: value
;
664 static LDOUBLE
POW10(int exp
)
676 static LLONG
ROUND(LDOUBLE value
)
680 intpart
= (LLONG
)value
;
681 value
= value
- intpart
;
682 if (value
>= 0.5) intpart
++;
687 /* a replacement for modf that doesn't need the math library. Should
688 be portable, but slow */
689 static double my_modf(double x0
, double *iptr
)
696 for (i
=0;i
<100;i
++) {
698 if (l
<= (x
+1) && l
>= (x
-1)) break;
704 /* yikes! the number is beyond what we can handle. What do we do? */
713 ret
= my_modf(x0
-l
*f
, &i2
);
723 static void fmtfp (char *buffer
, size_t *currlen
, size_t maxlen
,
724 LDOUBLE fvalue
, int min
, int max
, int flags
)
732 int padlen
= 0; /* amount to pad */
741 * AIX manpage says the default is 0, but Solaris says the default
742 * is 6, and sprintf on AIX defaults to 6
747 ufvalue
= abs_val (fvalue
);
752 if (flags
& DP_F_PLUS
) { /* Do a sign (+/i) */
755 if (flags
& DP_F_SPACE
)
761 if (flags
& DP_F_UP
) caps
= 1; /* Should characters be upper case? */
765 if (max
== 0) ufvalue
+= 0.5; /* if max = 0 we must round */
769 * Sorry, we only support 16 digits past the decimal because of our
775 /* We "cheat" by converting the fractional part to integer by
776 * multiplying by a factor of 10
780 my_modf(temp
, &intpart
);
782 fracpart
= ROUND((POW10(max
)) * (ufvalue
- intpart
));
784 if (fracpart
>= POW10(max
)) {
786 fracpart
-= POW10(max
);
790 /* Convert integer part */
793 my_modf(intpart
*0.1, &intpart
);
795 index
= (int) ((temp
-intpart
+0.05)* 10.0);
796 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
797 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
799 (caps
? "0123456789ABCDEF":"0123456789abcdef")[index
];
800 } while (intpart
&& (iplace
< 311));
801 if (iplace
== 311) iplace
--;
802 iconvert
[iplace
] = 0;
804 /* Convert fractional part */
809 my_modf(fracpart
*0.1, &fracpart
);
811 index
= (int) ((temp
-fracpart
+0.05)* 10.0);
812 /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
813 /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
815 (caps
? "0123456789ABCDEF":"0123456789abcdef")[index
];
816 } while(fracpart
&& (fplace
< 311));
817 if (fplace
== 311) fplace
--;
819 fconvert
[fplace
] = 0;
821 /* -1 for decimal point, another -1 if we are printing a sign */
822 padlen
= min
- iplace
- max
- 1 - ((signvalue
) ? 1 : 0);
823 zpadlen
= max
- fplace
;
824 if (zpadlen
< 0) zpadlen
= 0;
827 if (flags
& DP_F_MINUS
)
828 padlen
= -padlen
; /* Left Justifty */
830 if ((flags
& DP_F_ZERO
) && (padlen
> 0)) {
832 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
837 dopr_outch(buffer
, currlen
, maxlen
, '0');
842 dopr_outch(buffer
, currlen
, maxlen
, ' ');
846 dopr_outch(buffer
, currlen
, maxlen
, signvalue
);
849 dopr_outch(buffer
, currlen
, maxlen
, iconvert
[--iplace
]);
851 #ifdef DEBUG_SNPRINTF
852 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace
, zpadlen
);
856 * Decimal point. This should probably use locale to find the correct
860 dopr_outch(buffer
, currlen
, maxlen
, '.');
863 dopr_outch(buffer
, currlen
, maxlen
, fconvert
[--fplace
]);
866 while (zpadlen
> 0) {
867 dopr_outch(buffer
, currlen
, maxlen
, '0');
872 dopr_outch(buffer
, currlen
, maxlen
, ' ');
877 static void dopr_outch(char *buffer
, size_t *currlen
, size_t maxlen
, char c
)
879 if (*currlen
< maxlen
) {
880 buffer
[(*currlen
)] = c
;
885 int ht_vsnprintf (char *str
, size_t count
, const char *fmt
, va_list args
)
887 if ((int)count
< 0) count
= 0;
888 int res
= dopr(str
, count
, fmt
, args
);
890 return str
? MIN(res
, (int)count
) : count
;
893 int ht_snprintf(char *str
, size_t count
, const char *fmt
,...)
899 ret
= ht_vsnprintf(str
, count
, fmt
, ap
);
904 int ht_vasprintf(char **ptr
, const char *format
, va_list ap
)
908 ret
= dopr(NULL
, 0, format
, ap
);
914 (*ptr
) = (char *)malloc(ret
+1);
916 ret
= ht_vsnprintf(*ptr
, ret
+1, format
, ap
);
922 int ht_asprintf(char **ptr
, const char *format
, ...)
927 va_start(ap
, format
);
928 ret
= ht_vasprintf(ptr
, format
, ap
);
934 int ht_vfprintf(FILE *file
, const char *fmt
, va_list args
)
938 int ret
= ht_vasprintf(&buf
, fmt
, args
);
943 int ret
= ht_vsnprintf(buf
, sizeof buf
, fmt
, args
);
949 int ht_fprintf(FILE *file
, const char *fmt
, ...)
955 ret
= ht_vfprintf(file
, fmt
, ap
);
962 int ht_vprintf(const char *fmt
, va_list args
)
964 return ht_vfprintf(stdout
, fmt
, args
);
967 int ht_printf(const char *fmt
, ...)
973 ret
= ht_vprintf(fmt
, ap
);
980 #endif //HAVE_SNPRINTF