1 #if defined (OS2) && defined (__IBMC__)
\r
3 * Copyright Patrick Powell 1995
\r
4 * This code is based on code written by Patrick Powell (papowell@astart.com)
\r
5 * It may be used for any purpose as long as this notice remains intact
\r
6 * on all source code distributions
\r
9 /**************************************************************
\r
11 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
\r
12 * A bombproof version of doprnt (dopr) included.
\r
13 * Sigh. This sort of thing is always nasty do deal with. Note that
\r
14 * the version here does not include floating point...
\r
16 * snprintf() is used instead of sprintf() as it does limit checks
\r
17 * for string length. This covers a nasty loophole.
\r
19 * The other functions are there to prevent NULL pointers from
\r
20 * causing nast effects.
\r
23 * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
\r
24 * This was ugly. It is still ugly. I opted out of floating point
\r
25 * numbers, but the formatter understands just about everything
\r
26 * from the normal C string format, at least as far as I can tell from
\r
27 * the Solaris 2.5 printf(3S) man page.
\r
29 * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
\r
30 * Ok, added some minimal floating point support, which means this
\r
31 * probably requires libm on most operating systems. Don't yet
\r
32 * support the exponent (e,E) and sigfig (g,G). Also, fmtint()
\r
33 * was pretty badly broken, it just wasn't being exercised in ways
\r
34 * which showed it, so that's been fixed. Also, formated the code
\r
35 * to mutt conventions, and removed dead code left over from the
\r
36 * original. Also, there is now a builtin-test, just compile with:
\r
37 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
\r
38 * and run snprintf for results.
\r
40 * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
\r
41 * The PGP code was using unsigned hexadecimal formats.
\r
42 * Unfortunately, unsigned formats simply didn't work.
\r
44 * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
\r
45 * The original code assumed that both snprintf() and vsnprintf() were
\r
46 * missing. Some systems only have snprintf() but not vsnprintf(), so
\r
47 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
\r
49 * Andrew Tridgell (tridge@samba.org) Oct 1998
\r
50 * fixed handling of %.0f
\r
51 * added test for HAVE_LONG_DOUBLE
\r
53 * tridge@samba.org, idra@samba.org, April 2001
\r
54 * got rid of fcvt code (twas buggy and made testing harder)
\r
55 * added C99 semantics
\r
57 **************************************************************/
\r
59 #ifndef NO_CONFIG_H /* for some tests */
\r
63 #ifdef HAVE_STRING_H
\r
67 #ifdef HAVE_STRINGS_H
\r
68 #include <strings.h>
\r
73 #include <sys/types.h>
\r
75 #ifdef HAVE_STDLIB_H
\r
79 #if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
\r
80 /* only include stdio.h if we are not re-defining snprintf or vsnprintf */
\r
82 /* make the compiler happy with an empty file */
\r
83 void dummy_snprintf(void) {}
\r
86 #ifdef HAVE_LONG_DOUBLE
\r
87 #define LDOUBLE long double
\r
89 #define LDOUBLE double
\r
92 #ifdef HAVE_LONG_LONG
\r
93 #define LLONG long long
\r
98 static size_t dopr(char *buffer, size_t maxlen, const char *format,
\r
100 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
\r
101 char *value, int flags, int min, int max);
\r
102 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
\r
103 long value, int base, int min, int max, int flags);
\r
104 static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
\r
105 LDOUBLE fvalue, int min, int max, int flags);
\r
106 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);
\r
109 * dopr(): poor man's version of doprintf
\r
112 /* format read states */
\r
113 #define DP_S_DEFAULT 0
\r
114 #define DP_S_FLAGS 1
\r
119 #define DP_S_CONV 6
\r
120 #define DP_S_DONE 7
\r
122 /* format flags - Bits */
\r
123 #define DP_F_MINUS (1 << 0)
\r
124 #define DP_F_PLUS (1 << 1)
\r
125 #define DP_F_SPACE (1 << 2)
\r
126 #define DP_F_NUM (1 << 3)
\r
127 #define DP_F_ZERO (1 << 4)
\r
128 #define DP_F_UP (1 << 5)
\r
129 #define DP_F_UNSIGNED (1 << 6)
\r
131 /* Conversion Flags */
\r
132 #define DP_C_SHORT 1
\r
133 #define DP_C_LONG 2
\r
134 #define DP_C_LDOUBLE 3
\r
135 #define DP_C_LLONG 4
\r
137 #define char_to_int(p) ((p)- '0')
\r
139 #define MAX(p,q) (((p) >= (q)) ? (p) : (q))
\r
142 static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args)
\r
155 state = DP_S_DEFAULT;
\r
156 currlen = flags = cflags = min = 0;
\r
160 while (state != DP_S_DONE) {
\r
167 state = DP_S_FLAGS;
\r
169 dopr_outch (buffer, &currlen, maxlen, ch);
\r
175 flags |= DP_F_MINUS;
\r
179 flags |= DP_F_PLUS;
\r
183 flags |= DP_F_SPACE;
\r
191 flags |= DP_F_ZERO;
\r
200 if (isdigit((unsigned char)ch)) {
\r
201 min = 10*min + char_to_int (ch);
\r
203 } else if (ch == '*') {
\r
204 min = va_arg (args, int);
\r
220 if (isdigit((unsigned char)ch)) {
\r
223 max = 10*max + char_to_int (ch);
\r
225 } else if (ch == '*') {
\r
226 max = va_arg (args, int);
\r
236 cflags = DP_C_SHORT;
\r
240 cflags = DP_C_LONG;
\r
242 if (ch == 'l') { /* It's a long long */
\r
243 cflags = DP_C_LLONG;
\r
248 cflags = DP_C_LDOUBLE;
\r
260 if (cflags == DP_C_SHORT)
\r
261 value = va_arg (args, int);
\r
262 else if (cflags == DP_C_LONG)
\r
263 value = va_arg (args, long int);
\r
264 else if (cflags == DP_C_LLONG)
\r
265 value = va_arg (args, LLONG);
\r
267 value = va_arg (args, int);
\r
268 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
\r
271 flags |= DP_F_UNSIGNED;
\r
272 if (cflags == DP_C_SHORT)
\r
273 value = va_arg (args, unsigned int);
\r
274 else if (cflags == DP_C_LONG)
\r
275 value = (long)va_arg (args, unsigned long int);
\r
276 else if (cflags == DP_C_LLONG)
\r
277 value = (long)va_arg (args, unsigned LLONG);
\r
279 value = (long)va_arg (args, unsigned int);
\r
280 fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
\r
283 flags |= DP_F_UNSIGNED;
\r
284 if (cflags == DP_C_SHORT)
\r
285 value = va_arg (args, unsigned int);
\r
286 else if (cflags == DP_C_LONG)
\r
287 value = (long)va_arg (args, unsigned long int);
\r
288 else if (cflags == DP_C_LLONG)
\r
289 value = (LLONG)va_arg (args, unsigned LLONG);
\r
291 value = (long)va_arg (args, unsigned int);
\r
292 fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
\r
297 flags |= DP_F_UNSIGNED;
\r
298 if (cflags == DP_C_SHORT)
\r
299 value = va_arg (args, unsigned int);
\r
300 else if (cflags == DP_C_LONG)
\r
301 value = (long)va_arg (args, unsigned long int);
\r
302 else if (cflags == DP_C_LLONG)
\r
303 value = (LLONG)va_arg (args, unsigned LLONG);
\r
305 value = (long)va_arg (args, unsigned int);
\r
306 fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
\r
309 if (cflags == DP_C_LDOUBLE)
\r
310 fvalue = va_arg (args, LDOUBLE);
\r
312 fvalue = va_arg (args, double);
\r
313 /* um, floating point? */
\r
314 fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
\r
319 if (cflags == DP_C_LDOUBLE)
\r
320 fvalue = va_arg (args, LDOUBLE);
\r
322 fvalue = va_arg (args, double);
\r
327 if (cflags == DP_C_LDOUBLE)
\r
328 fvalue = va_arg (args, LDOUBLE);
\r
330 fvalue = va_arg (args, double);
\r
333 dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
\r
336 strvalue = va_arg (args, char *);
\r
338 max = strlen(strvalue);
\r
340 if (min > 0 && max >= 0 && min > max) max = min;
\r
341 fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
\r
344 strvalue = va_arg (args, void *);
\r
345 fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
\r
348 if (cflags == DP_C_SHORT) {
\r
350 num = va_arg (args, short int *);
\r
352 } else if (cflags == DP_C_LONG) {
\r
354 num = va_arg (args, long int *);
\r
355 *num = (long int)currlen;
\r
356 } else if (cflags == DP_C_LLONG) {
\r
358 num = va_arg (args, LLONG *);
\r
359 *num = (LLONG)currlen;
\r
362 num = va_arg (args, int *);
\r
367 dopr_outch (buffer, &currlen, maxlen, ch);
\r
370 /* not supported yet, treat as next char */
\r
374 /* Unknown, skip */
\r
378 state = DP_S_DEFAULT;
\r
379 flags = cflags = min = 0;
\r
386 break; /* some picky compilers need this */
\r
390 if (currlen < maxlen - 1)
\r
391 buffer[currlen] = '\0';
\r
392 else if (maxlen > 0)
\r
393 buffer[maxlen - 1] = '\0';
\r
399 static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
\r
400 char *value, int flags, int min, int max)
\r
402 int padlen, strln; /* amount to pad */
\r
405 #ifdef DEBUG_SNPRINTF
\r
406 printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
\r
412 for (strln = 0; value[strln]; ++strln); /* strlen */
\r
413 padlen = min - strln;
\r
416 if (flags & DP_F_MINUS)
\r
417 padlen = -padlen; /* Left Justify */
\r
419 while ((padlen > 0) && (cnt < max)) {
\r
420 dopr_outch (buffer, currlen, maxlen, ' ');
\r
424 while (*value && (cnt < max)) {
\r
425 dopr_outch (buffer, currlen, maxlen, *value++);
\r
428 while ((padlen < 0) && (cnt < max)) {
\r
429 dopr_outch (buffer, currlen, maxlen, ' ');
\r
435 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
\r
437 static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
\r
438 long value, int base, int min, int max, int flags)
\r
441 unsigned long uvalue;
\r
444 int spadlen = 0; /* amount to space pad */
\r
445 int zpadlen = 0; /* amount to zero pad */
\r
453 if(!(flags & DP_F_UNSIGNED)) {
\r
458 if (flags & DP_F_PLUS) /* Do a sign (+/i) */
\r
460 else if (flags & DP_F_SPACE)
\r
465 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
\r
469 (caps? "0123456789ABCDEF":"0123456789abcdef")
\r
470 [uvalue % (unsigned)base ];
\r
471 uvalue = (uvalue / (unsigned)base );
\r
472 } while(uvalue && (place < 20));
\r
473 if (place == 20) place--;
\r
474 convert[place] = 0;
\r
476 zpadlen = max - place;
\r
477 spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
\r
478 if (zpadlen < 0) zpadlen = 0;
\r
479 if (spadlen < 0) spadlen = 0;
\r
480 if (flags & DP_F_ZERO) {
\r
481 zpadlen = MAX(zpadlen, spadlen);
\r
484 if (flags & DP_F_MINUS)
\r
485 spadlen = -spadlen; /* Left Justifty */
\r
487 #ifdef DEBUG_SNPRINTF
\r
488 printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
\r
489 zpadlen, spadlen, min, max, place);
\r
493 while (spadlen > 0) {
\r
494 dopr_outch (buffer, currlen, maxlen, ' ');
\r
500 dopr_outch (buffer, currlen, maxlen, signvalue);
\r
504 while (zpadlen > 0) {
\r
505 dopr_outch (buffer, currlen, maxlen, '0');
\r
512 dopr_outch (buffer, currlen, maxlen, convert[--place]);
\r
514 /* Left Justified spaces */
\r
515 while (spadlen < 0) {
\r
516 dopr_outch (buffer, currlen, maxlen, ' ');
\r
521 static LDOUBLE abs_val(LDOUBLE value)
\r
523 LDOUBLE result = value;
\r
531 static LDOUBLE POW10(int exp)
\r
533 LDOUBLE result = 1;
\r
543 static LLONG ROUND(LDOUBLE value)
\r
547 intpart = (LLONG)value;
\r
548 value = value - intpart;
\r
549 if (value >= 0.5) intpart++;
\r
554 /* a replacement for modf that doesn't need the math library. Should
\r
555 be portable, but slow */
\r
556 static double my_modf(double x0, double *iptr)
\r
563 for (i=0;i<100;i++) {
\r
565 if (l <= (x+1) && l >= (x-1)) break;
\r
571 /* yikes! the number is beyond what we can handle. What do we do? */
\r
580 ret = my_modf(x0-l*f, &i2);
\r
581 (*iptr) = l*f + i2;
\r
586 return x - (*iptr);
\r
590 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
\r
591 LDOUBLE fvalue, int min, int max, int flags)
\r
595 char iconvert[311];
\r
596 char fconvert[311];
\r
599 int padlen = 0; /* amount to pad */
\r
608 * AIX manpage says the default is 0, but Solaris says the default
\r
609 * is 6, and sprintf on AIX defaults to 6
\r
614 ufvalue = abs_val (fvalue);
\r
619 if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
\r
622 if (flags & DP_F_SPACE)
\r
628 if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
\r
632 if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */
\r
636 * Sorry, we only support 16 digits past the decimal because of our
\r
637 * conversion method
\r
642 /* We "cheat" by converting the fractional part to integer by
\r
643 * multiplying by a factor of 10
\r
647 my_modf(temp, &intpart);
\r
649 fracpart = ROUND((POW10(max)) * (ufvalue - intpart));
\r
651 if (fracpart >= POW10(max)) {
\r
653 fracpart -= POW10(max);
\r
657 /* Convert integer part */
\r
660 my_modf(intpart*0.1, &intpart);
\r
662 index = (int) ((temp -intpart +0.05)* 10.0);
\r
663 /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
\r
664 /* printf ("%llf, %f, %x\n", temp, intpart, index); */
\r
665 iconvert[iplace++] =
\r
666 (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
\r
667 } while (intpart && (iplace < 311));
\r
668 if (iplace == 311) iplace--;
\r
669 iconvert[iplace] = 0;
\r
671 /* Convert fractional part */
\r
676 my_modf(fracpart*0.1, &fracpart);
\r
678 index = (int) ((temp -fracpart +0.05)* 10.0);
\r
679 /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */
\r
680 /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
\r
681 fconvert[fplace++] =
\r
682 (caps? "0123456789ABCDEF":"0123456789abcdef")[index];
\r
683 } while(fracpart && (fplace < 311));
\r
684 if (fplace == 311) fplace--;
\r
686 fconvert[fplace] = 0;
\r
688 /* -1 for decimal point, another -1 if we are printing a sign */
\r
689 padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
\r
690 zpadlen = max - fplace;
\r
691 if (zpadlen < 0) zpadlen = 0;
\r
694 if (flags & DP_F_MINUS)
\r
695 padlen = -padlen; /* Left Justifty */
\r
697 if ((flags & DP_F_ZERO) && (padlen > 0)) {
\r
699 dopr_outch (buffer, currlen, maxlen, signvalue);
\r
703 while (padlen > 0) {
\r
704 dopr_outch (buffer, currlen, maxlen, '0');
\r
708 while (padlen > 0) {
\r
709 dopr_outch (buffer, currlen, maxlen, ' ');
\r
713 dopr_outch (buffer, currlen, maxlen, signvalue);
\r
716 dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
\r
718 #ifdef DEBUG_SNPRINTF
\r
719 printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
\r
723 * Decimal point. This should probably use locale to find the correct
\r
724 * char to print out.
\r
727 dopr_outch (buffer, currlen, maxlen, '.');
\r
730 dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
\r
733 while (zpadlen > 0) {
\r
734 dopr_outch (buffer, currlen, maxlen, '0');
\r
738 while (padlen < 0) {
\r
739 dopr_outch (buffer, currlen, maxlen, ' ');
\r
744 static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
\r
746 if (*currlen < maxlen) {
\r
747 buffer[(*currlen)] = c;
\r
752 #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
\r
753 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
\r
755 return dopr(str, count, fmt, args);
\r
759 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
\r
760 int snprintf(char *str,size_t count,const char *fmt,...)
\r
766 ret = vsnprintf(str, count, fmt, ap);
\r
774 #ifndef HAVE_VASPRINTF
\r
775 int vasprintf(char **ptr, const char *format, va_list ap)
\r
779 ret = vsnprintf(NULL, 0, format, ap);
\r
780 if (ret <= 0) return ret;
\r
782 (*ptr) = (char *)malloc(ret+1);
\r
783 if (!*ptr) return -1;
\r
784 ret = vsnprintf(*ptr, ret+1, format, ap);
\r
791 #ifndef HAVE_ASPRINTF
\r
792 int asprintf(char **ptr, const char *format, ...)
\r
797 va_start(ap, format);
\r
798 ret = vasprintf(ptr, format, ap);
\r
805 #ifdef TEST_SNPRINTF
\r
807 int sprintf(char *str,const char *fmt,...);
\r
831 double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
\r
832 0.9996, 1.996, 4.136, 0};
\r
833 char *int_fmt[] = {
\r
846 long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
\r
847 char *str_fmt[] = {
\r
860 char *str_vals[] = {"hello", "a", "", "a longer string", NULL};
\r
865 printf ("Testing snprintf format codes against system sprintf...\n");
\r
867 for (x = 0; fp_fmt[x] ; x++) {
\r
868 for (y = 0; fp_nums[y] != 0 ; y++) {
\r
869 int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]);
\r
870 int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]);
\r
871 sprintf (buf2, fp_fmt[x], fp_nums[y]);
\r
872 if (strcmp (buf1, buf2)) {
\r
873 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
\r
874 fp_fmt[x], buf1, buf2);
\r
878 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]);
\r
885 for (x = 0; int_fmt[x] ; x++) {
\r
886 for (y = 0; int_nums[y] != 0 ; y++) {
\r
887 int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]);
\r
888 int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]);
\r
889 sprintf (buf2, int_fmt[x], int_nums[y]);
\r
890 if (strcmp (buf1, buf2)) {
\r
891 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
\r
892 int_fmt[x], buf1, buf2);
\r
896 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]);
\r
903 for (x = 0; str_fmt[x] ; x++) {
\r
904 for (y = 0; str_vals[y] != 0 ; y++) {
\r
905 int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]);
\r
906 int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]);
\r
907 sprintf (buf2, str_fmt[x], str_vals[y]);
\r
908 if (strcmp (buf1, buf2)) {
\r
909 printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n",
\r
910 str_fmt[x], buf1, buf2);
\r
914 printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]);
\r
921 printf ("%d tests failed out of %d.\n", fail, num);
\r
923 printf("seeing how many digits we support\n");
\r
925 double v0 = 0.12345678901234567890123456789012345678901;
\r
926 for (x=0; x<100; x++) {
\r
927 snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x));
\r
928 sprintf(buf2, "%1.1f", v0*pow(10, x));
\r
929 if (strcmp(buf1, buf2)) {
\r
930 printf("we seem to support %d digits\n", x-1);
\r
938 #endif /* SNPRINTF_TEST */
\r
940 #endif /* OS2 && __IBMC__ */
\r