3 Unix snprintf implementation.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 It can be redistribute also under the terms of GNU Library General
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * put the program under LGPL.
27 * added changes from Miles Bader
28 * corrected a bug with %f
29 * added support for %#g
30 * added more comments :-)
32 * supporting must ANSI syntaxic_sugars
36 THANKS(for the patches and ideas):
47 #include "snprintf-imp.h"
51 * Find the nth power of 10
65 for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
67 for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
72 * Find the integral part of the log in base 10
73 * Note: this not a real log10()
74 I just need and approximation(integerpart) of x in:
94 while (result >= r) {result *= .1; i++;}
97 while (result <= r) {result *= 10.; i++;}
103 * This function return the fraction part of a double
104 * and set in ip the integral part.
105 * In many ways it resemble the modf() found on most Un*x
109 integral(double real, double * ip)
118 double real_integral = 0.;
120 /* take care of the obvious */
121 /* equal to zero ? */
127 /* negative number ? */
136 /* the real work :-) */
137 for (j = log_10(real); j >= 0; j--) {
139 s = (real - real_integral)/p;
141 while (i + 1. <= s) {i++;}
142 real_integral += i*p;
145 return (real - real_integral);
148 #define PRECISION 1.e-6
150 * return an ascii representation of the integral part of the number
151 * and set fract to be an ascii representation of the fraction part
152 * the container for the fraction and the integral part or staticly
153 * declare with fix size
157 numtoa(double number, int base, int precision, char ** fract)
159 numtoa(number, base, precision, fract)
167 double ip, fp; /* integer and fraction part */
169 int digits = MAX_INT - 1;
170 static char integral_part[MAX_INT];
171 static char fraction_part[MAX_FRACT];
175 /* taking care of the obvious case: 0.0 */
177 integral_part[0] = '0';
178 integral_part[1] = '\0';
179 fraction_part[0] = '0';
180 fraction_part[1] = '\0';
181 return integral_part;
184 /* for negative numbers */
185 if ((sign = number) < 0.) {
187 digits--; /* sign consume one digit */
190 fraction = integral(number, &ip);
192 /* do the integral part */
194 integral_part[0] = '0';
197 for ( i = 0; i < digits && number != 0.; ++i) {
199 fp = integral(number, &ip);
200 ch = (int)((fp + PRECISION)*base); /* force to round */
201 integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
202 if (! isxdigit(integral_part[i])) /* bail out overflow !! */
208 /* Oh No !! out of bound, ho well fill it up ! */
210 for (i = 0; i < digits; ++i)
211 integral_part[i] = '9';
215 integral_part[i++] = '-';
217 integral_part[i] = '\0';
219 /* reverse every thing */
220 for ( i--, j = 0; j < i; j++, i--)
221 SWAP_INT(integral_part[i], integral_part[j]);
223 /* the fractionnal part */
224 for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
225 fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
226 if (! isdigit(fraction_part[i])) /* underflow ? */
228 fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
230 fraction_part[i] = '\0';
232 if (fract != (char **)0)
233 *fract = fraction_part;
235 return integral_part;
239 /* for %d and friends, it puts in holder
240 * the representation with the right padding
244 decimal(struct DATA *p, double d)
254 p->width -= strlen(tmp);
258 while (*tmp) { /* the integral */
265 /* for %o octal representation */
268 octal(struct DATA *p, double d)
278 p->width -= strlen(tmp);
280 if (p->square == FOUND) /* had prefix '0' for octal */
282 while (*tmp) { /* octal */
289 /* for %x %X hexadecimal representation */
292 hexa(struct DATA *p, double d)
302 p->width -= strlen(tmp);
304 if (p->square == FOUND) { /* prefix '0x' for hexa */
305 PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
307 while (*tmp) { /* hexa */
308 PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
317 strings(struct DATA *p, char *tmp)
327 if (p->precision != NOT_FOUND) /* the smallest number */
328 i = (i < p->precision ? i : p->precision);
331 while (i-- > 0) { /* put the sting */
338 /* %f or %g floating point representation */
341 floating(struct DATA *p, double d)
353 tmp = dtoa(d, p->precision, &tmp2);
354 /* calculate the padding. 1 for the dot */
355 p->width = p->width -
356 ((d > 0. && p->justify == RIGHT) ? 1:0) -
357 ((p->space == FOUND) ? 1:0) -
358 strlen(tmp) - p->precision - 1;
362 while (*tmp) { /* the integral */
366 if (p->precision != 0 || p->square == FOUND)
367 PUT_CHAR('.', p); /* put the '.' */
368 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
369 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
371 for (; *tmp2; tmp2++)
372 PUT_CHAR(*tmp2, p); /* the fraction */
377 /* %e %E %g exponent representation */
380 exponent(struct DATA *p, double d)
392 d = d / pow_10(j); /* get the Mantissa */
394 tmp = dtoa(d, p->precision, &tmp2);
395 /* 1 for unit, 1 for the '.', 1 for 'e|E',
396 * 1 for '+|-', 3 for 'exp' */
397 /* calculate how much padding need */
398 p->width = p->width -
399 ((d > 0. && p->justify == RIGHT) ? 1:0) -
400 ((p->space == FOUND) ? 1:0) - p->precision - 7;
404 while (*tmp) {/* the integral */
408 if (p->precision != 0 || p->square == FOUND)
409 PUT_CHAR('.', p); /* the '.' */
410 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
411 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
413 for (; *tmp2; tmp2++)
414 PUT_CHAR(*tmp2, p); /* the fraction */
416 if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
420 if (j > 0) { /* the sign of the exp */
426 tmp = itoa((double)j);
427 if (j < 9) { /* need to pad the exponent with 0 '000' */
428 PUT_CHAR('0', p); PUT_CHAR('0', p);
431 while (*tmp) { /* the exponent */
438 /* initialize the conversion specifiers */
441 conv_flag(char * s, struct DATA * p)
448 char number[MAX_FIELD/2];
451 p->precision = p->width = NOT_FOUND;
452 p->star_w = p->star_p = NOT_FOUND;
453 p->square = p->space = NOT_FOUND;
454 p->a_long = p->justify = NOT_FOUND;
459 case ' ': p->space = FOUND; break;
460 case '#': p->square = FOUND; break;
461 case '*': if (p->width == NOT_FOUND)
462 p->width = p->star_w = FOUND;
464 p->precision = p->star_p = FOUND;
466 case '+': p->justify = RIGHT; break;
467 case '-': p->justify = LEFT; break;
468 case '.': if (p->width == NOT_FOUND)
471 case '0': p->pad = '0'; break;
472 case '1': case '2': case '3':
473 case '4': case '5': case '6':
474 case '7': case '8': case '9': /* gob all the digits */
475 for (i = 0; isdigit(*s); i++, s++)
476 if (i < MAX_FIELD/2 - 1)
479 if (p->width == NOT_FOUND)
480 p->width = atoi(number);
482 p->precision = atoi(number);
483 s--; /* went to far go back */
491 vsnprintf(char *string, size_t length, const char * format, va_list args)
493 vsnprintf(string, length, format, args)
501 char conv_field[MAX_FIELD];
502 double d; /* temporary holder */
506 data.length = length - 1; /* leave room for '\0' */
507 data.holder = string;
512 /* sanity check, the string must be > 1 */
517 for (; *data.pf && (data.counter < data.length); data.pf++) {
518 if ( *data.pf == '%' ) { /* we got a magic % cookie */
519 conv_flag((char *)0, &data); /* initialise format flags */
520 for (state = 1; *data.pf && state;) {
521 switch (*(++data.pf)) {
522 case '\0': /* a NULL here ? ? bail out */
526 case 'f': /* float, double */
528 d = va_arg(args, double);
536 d = va_arg(args, double);
539 * for '%g|%G' ANSI: use f if exponent
540 * is in the range or [-4,p] exclusively
543 if (-4 < i && i < data.precision)
550 case 'E': /* Exponent double */
552 d = va_arg(args, double);
556 case 'u': /* unsigned decimal */
558 if (data.a_long == FOUND)
559 d = va_arg(args, unsigned long);
561 d = va_arg(args, unsigned int);
565 case 'd': /* decimal */
566 case 'i': /* "integer" (signed decimal) */
568 if (data.a_long == FOUND)
569 d = va_arg(args, long);
571 d = va_arg(args, int);
575 case 'o': /* octal */
577 if (data.a_long == FOUND)
578 d = va_arg(args, unsigned long);
580 d = va_arg(args, unsigned int);
585 case 'X': /* hexadecimal */
587 if (data.a_long == FOUND)
588 d = va_arg(args, unsigned long);
590 d = va_arg(args, unsigned int);
594 case 'c': /* character */
595 d = va_arg(args, int);
599 case 's': /* string */
601 strings(&data, va_arg(args, char *));
605 *(va_arg(args, int *)) = data.counter; /* what's the count ? */
613 case '%': /* nothing just % */
614 PUT_CHAR('%', &data);
617 case '#': case ' ': case '+': case '*':
618 case '-': case '.': case '0': case '1':
619 case '2': case '3': case '4': case '5':
620 case '6': case '7': case '8': case '9':
621 /* initialize width and precision */
622 for (i = 0; isflag(*data.pf); i++, data.pf++)
623 if (i < MAX_FIELD - 1)
624 conv_field[i] = *data.pf;
625 conv_field[i] = '\0';
626 conv_flag(conv_field, &data);
627 data.pf--; /* went to far go back */
630 /* is this an error ? maybe bail out */
634 } /* end of for state */
636 PUT_CHAR(*data.pf, &data); /* add the char the string */
640 *data.holder = '\0'; /* the end ye ! */
645 #ifndef HAVE_SNPRINTF
648 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
649 snprintf(char *string, size_t length, const char * format, ...)
651 snprintf(string, length, format, va_alist)
661 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
662 va_start(args, format);
667 rval = vsnprintf (string, length, format, args);
674 #endif /* HAVE_SNPRINTF */
681 /* set of small tests for snprintf() */
688 printf("Suite of test for snprintf:\n");
689 printf("a_format\n");
690 printf("printf() format\n");
691 printf("snprintf() format\n\n");
693 /* Checking the field widths */
695 printf("/%%d/, 336\n");
696 snprintf(holder, sizeof holder, "/%d/\n", 336);
697 printf("/%d/\n", 336);
698 printf("%s\n", holder);
700 printf("/%%2d/, 336\n");
701 snprintf(holder, sizeof holder, "/%2d/\n", 336);
702 printf("/%2d/\n", 336);
703 printf("%s\n", holder);
705 printf("/%%10d/, 336\n");
706 snprintf(holder, sizeof holder, "/%10d/\n", 336);
707 printf("/%10d/\n", 336);
708 printf("%s\n", holder);
710 printf("/%%-10d/, 336\n");
711 snprintf(holder, sizeof holder, "/%-10d/\n", 336);
712 printf("/%-10d/\n", 336);
713 printf("%s\n", holder);
716 /* floating points */
718 printf("/%%f/, 1234.56\n");
719 snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
720 printf("/%f/\n", 1234.56);
721 printf("%s\n", holder);
723 printf("/%%e/, 1234.56\n");
724 snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
725 printf("/%e/\n", 1234.56);
726 printf("%s\n", holder);
728 printf("/%%4.2f/, 1234.56\n");
729 snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
730 printf("/%4.2f/\n", 1234.56);
731 printf("%s\n", holder);
733 printf("/%%3.1f/, 1234.56\n");
734 snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
735 printf("/%3.1f/\n", 1234.56);
736 printf("%s\n", holder);
738 printf("/%%10.3f/, 1234.56\n");
739 snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
740 printf("/%10.3f/\n", 1234.56);
741 printf("%s\n", holder);
743 printf("/%%10.3e/, 1234.56\n");
744 snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
745 printf("/%10.3e/\n", 1234.56);
746 printf("%s\n", holder);
748 printf("/%%+4.2f/, 1234.56\n");
749 snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
750 printf("/%+4.2f/\n", 1234.56);
751 printf("%s\n", holder);
753 printf("/%%010.2f/, 1234.56\n");
754 snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
755 printf("/%010.2f/\n", 1234.56);
756 printf("%s\n", holder);
758 #define BLURB "Outstanding acting !"
759 /* strings precisions */
761 printf("/%%2s/, \"%s\"\n", BLURB);
762 snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
763 printf("/%2s/\n", BLURB);
764 printf("%s\n", holder);
766 printf("/%%22s/ %s\n", BLURB);
767 snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
768 printf("/%22s/\n", BLURB);
769 printf("%s\n", holder);
771 printf("/%%22.5s/ %s\n", BLURB);
772 snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
773 printf("/%22.5s/\n", BLURB);
774 printf("%s\n", holder);
776 printf("/%%-22.5s/ %s\n", BLURB);
777 snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
778 printf("/%-22.5s/\n", BLURB);
779 printf("%s\n", holder);
783 printf("%%x %%X %%#x, 31, 31, 31\n");
784 snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
785 printf("%x %X %#x\n", 31, 31, 31);
786 printf("%s\n", holder);
788 printf("**%%d**%% d**%% d**, 42, 42, -42\n");
789 snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
790 printf("**%d**% d**% d**\n", 42, 42, -42);
791 printf("%s\n", holder);
795 printf("/%%g/, 31.4\n");
796 snprintf(holder, sizeof holder, "/%g/\n", 31.4);
797 printf("/%g/\n", 31.4);
798 printf("%s\n", holder);
800 printf("/%%.6g/, 31.4\n");
801 snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
802 printf("/%.6g/\n", 31.4);
803 printf("%s\n", holder);
805 printf("/%%.1G/, 31.4\n");
806 snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
807 printf("/%.1G/\n", 31.4);
808 printf("%s\n", holder);
811 printf("abc%n", &i); printf("%d\n", i);
812 snprintf(holder, sizeof holder, "abc%n", &i);
813 printf("%s", holder); printf("%d\n\n", i);
815 printf("%%*.*s --> 10.10\n");
816 snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
817 printf("%*.*s\n", 10, 10, BLURB);
818 printf("%s\n", holder);
820 printf("%%%%%%%%\n");
821 snprintf(holder, sizeof holder, "%%%%\n");
823 printf("%s\n", holder);
825 #define BIG "Hello this is a too big string for the buffer"
826 /* printf("A buffer to small of 10, trying to put this:\n");*/
827 printf("<%%>, %s\n", BIG);
828 i = snprintf(holder, 10, "%s\n", BIG);
829 printf("<%s>\n", BIG);
830 printf("<%s>\n", holder);