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:
96 while (result >= r) {result *= .1; i++;}
99 while (result <= r) {result *= 10.; i++;}
105 * This function return the fraction part of a double
106 * and set in ip the integral part.
107 * In many ways it resemble the modf() found on most Un*x
111 integral(double real, double * ip)
120 double real_integral = 0.;
122 /* take care of the obvious */
123 /* equal to zero ? */
129 /* negative number ? */
138 /* the real work :-) */
139 for (j = log_10(real); j >= 0; j--) {
141 s = (real - real_integral)/p;
143 while (i + 1. <= s) {i++;}
144 real_integral += i*p;
147 return (real - real_integral);
150 #define PRECISION 1.e-6
152 * return an ascii representation of the integral part of the number
153 * and set fract to be an ascii representation of the fraction part
154 * the container for the fraction and the integral part or staticly
155 * declare with fix size
159 numtoa(double number, int base, int precision, char ** fract)
161 numtoa(number, base, precision, fract)
169 double ip, fp; /* integer and fraction part */
171 int digits = MAX_INT - 1;
172 static char integral_part[MAX_INT];
173 static char fraction_part[MAX_FRACT];
177 /* taking care of the obvious case: 0.0 */
179 integral_part[0] = '0';
180 integral_part[1] = '\0';
181 fraction_part[0] = '0';
182 fraction_part[1] = '\0';
183 return integral_part;
186 /* for negative numbers */
187 if ((sign = number) < 0.) {
189 digits--; /* sign consume one digit */
192 fraction = integral(number, &ip);
194 /* do the integral part */
196 integral_part[0] = '0';
199 for ( i = 0; i < digits && number != 0.; ++i) {
201 fp = integral(number, &ip);
202 ch = (int)((fp + PRECISION)*base); /* force to round */
203 integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
204 if (! isxdigit((unsigned char)integral_part[i])) /* bail out overflow !! */
210 /* Oh No !! out of bound, ho well fill it up ! */
212 for (i = 0; i < digits; ++i)
213 integral_part[i] = '9';
217 integral_part[i++] = '-';
219 integral_part[i] = '\0';
221 /* reverse every thing */
222 for ( i--, j = 0; j < i; j++, i--)
223 SWAP_INT(integral_part[i], integral_part[j]);
225 /* the fractionnal part */
226 for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
227 fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
228 if (! isdigit((unsigned char)fraction_part[i])) /* underflow ? */
230 fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
232 fraction_part[i] = '\0';
234 if (fract != (char **)0)
235 *fract = fraction_part;
237 return integral_part;
241 /* for %d and friends, it puts in holder
242 * the representation with the right padding
246 decimal(struct DATA *p, double d)
256 p->width -= strlen(tmp);
260 while (*tmp) { /* the integral */
267 /* for %o octal representation */
270 octal(struct DATA *p, double d)
280 p->width -= strlen(tmp);
281 if (p->square == FOUND) /* had prefix '0' for octal */
284 while (*tmp) { /* octal */
291 /* for %x %X hexadecimal representation */
294 hexa(struct DATA *p, double d)
304 p->width -= strlen(tmp);
305 if (p->square == FOUND) { /* prefix '0x' for hexa */
306 PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
309 while (*tmp) { /* hexa */
310 PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
319 strings(struct DATA *p, char *tmp)
329 if (p->precision != NOT_FOUND) /* the smallest number */
330 i = (i < p->precision ? i : p->precision);
333 while (i-- > 0) { /* put the sting */
340 /* %f or %g floating point representation */
343 floating(struct DATA *p, double d)
355 tmp = dtoa(d, p->precision, &tmp2);
356 /* calculate the padding. 1 for the dot */
357 p->width = p->width -
358 ((d > 0. && p->justify == RIGHT) ? 1:0) -
359 ((p->space == FOUND) ? 1:0) -
360 strlen(tmp) - p->precision - 1;
364 while (*tmp) { /* the integral */
368 if (p->precision != 0 || p->square == FOUND)
369 PUT_CHAR('.', p); /* put the '.' */
370 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
371 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
373 for (; *tmp2; tmp2++)
374 PUT_CHAR(*tmp2, p); /* the fraction */
379 /* %e %E %g exponent representation */
382 exponent(struct DATA *p, double d)
394 d = d / pow_10(j); /* get the Mantissa */
396 tmp = dtoa(d, p->precision, &tmp2);
397 /* 1 for unit, 1 for the '.', 1 for 'e|E',
398 * 1 for '+|-', 3 for 'exp' */
399 /* calculate how much padding need */
400 p->width = p->width -
401 ((d > 0. && p->justify == RIGHT) ? 1:0) -
402 ((p->space == FOUND) ? 1:0) - p->precision - 7;
406 while (*tmp) {/* the integral */
410 if (p->precision != 0 || p->square == FOUND)
411 PUT_CHAR('.', p); /* the '.' */
412 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
413 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
415 for (; *tmp2; tmp2++)
416 PUT_CHAR(*tmp2, p); /* the fraction */
418 if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
422 if (j > 0) { /* the sign of the exp */
428 tmp = itoa((double)j);
429 if (j < 9) { /* need to pad the exponent with 0 '000' */
430 PUT_CHAR('0', p); PUT_CHAR('0', p);
433 while (*tmp) { /* the exponent */
440 /* initialize the conversion specifiers */
443 conv_flag(char * s, struct DATA * p)
450 char number[MAX_FIELD/2];
453 p->precision = p->width = NOT_FOUND;
454 p->star_w = p->star_p = NOT_FOUND;
455 p->square = p->space = NOT_FOUND;
456 p->a_long = p->justify = NOT_FOUND;
461 case ' ': p->space = FOUND; break;
462 case '#': p->square = FOUND; break;
463 case '*': if (p->width == NOT_FOUND)
464 p->width = p->star_w = FOUND;
466 p->precision = p->star_p = FOUND;
468 case '+': p->justify = RIGHT; break;
469 case '-': p->justify = LEFT; break;
470 case '.': if (p->width == NOT_FOUND)
473 case '0': p->pad = '0'; break;
474 case '1': case '2': case '3':
475 case '4': case '5': case '6':
476 case '7': case '8': case '9': /* gob all the digits */
477 for (i = 0; isdigit((unsigned char)*s); i++, s++)
478 if (i < MAX_FIELD/2 - 1)
481 if (p->width == NOT_FOUND)
482 p->width = atoi(number);
484 p->precision = atoi(number);
485 s--; /* went to far go back */
493 vsnprintf(char *string, size_t length, const char * format, va_list args)
495 vsnprintf(string, length, format, args)
503 char conv_field[MAX_FIELD];
504 double d; /* temporary holder */
508 data.length = length - 1; /* leave room for '\0' */
509 data.holder = string;
514 /* sanity check, the string must be > 1 */
519 for (; *data.pf && (data.counter < data.length); data.pf++) {
520 if ( *data.pf == '%' ) { /* we got a magic % cookie */
521 conv_flag((char *)0, &data); /* initialise format flags */
522 for (state = 1; *data.pf && state;) {
523 switch (*(++data.pf)) {
524 case '\0': /* a NULL here ? ? bail out */
528 case 'f': /* float, double */
530 d = va_arg(args, double);
538 d = va_arg(args, double);
541 * for '%g|%G' ANSI: use f if exponent
542 * is in the range or [-4,p] exclusively
545 if (-4 < i && i < data.precision)
552 case 'E': /* Exponent double */
554 d = va_arg(args, double);
558 case 'u': /* unsigned decimal */
560 if (data.a_long == FOUND)
561 d = va_arg(args, unsigned long);
563 d = va_arg(args, unsigned int);
567 case 'd': /* decimal */
568 case 'i': /* "integer" (signed decimal) */
570 if (data.a_long == FOUND)
571 d = va_arg(args, long);
573 d = va_arg(args, int);
577 case 'o': /* octal */
579 if (data.a_long == FOUND)
580 d = va_arg(args, unsigned long);
582 d = va_arg(args, unsigned int);
587 case 'X': /* hexadecimal */
589 if (data.a_long == FOUND)
590 d = va_arg(args, unsigned long);
592 d = va_arg(args, unsigned int);
596 case 'c': /* character */
597 d = va_arg(args, int);
601 case 's': /* string */
603 strings(&data, va_arg(args, char *));
607 *(va_arg(args, int *)) = data.counter; /* what's the count ? */
615 case '%': /* nothing just % */
616 PUT_CHAR('%', &data);
619 case '#': case ' ': case '+': case '*':
620 case '-': case '.': case '0': case '1':
621 case '2': case '3': case '4': case '5':
622 case '6': case '7': case '8': case '9':
623 /* initialize width and precision */
624 for (i = 0; isflag((unsigned char)*data.pf); i++, data.pf++)
625 if (i < MAX_FIELD - 1)
626 conv_field[i] = *data.pf;
627 conv_field[i] = '\0';
628 conv_flag(conv_field, &data);
629 data.pf--; /* went to far go back */
632 /* is this an error ? maybe bail out */
636 } /* end of for state */
638 PUT_CHAR(*data.pf, &data); /* add the char the string */
642 *data.holder = '\0'; /* the end ye ! */
647 #ifndef HAVE_SNPRINTF
650 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
651 snprintf(char *string, size_t length, const char * format, ...)
653 snprintf(string, length, format, va_alist)
663 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
664 va_start(args, format);
669 rval = vsnprintf (string, length, format, args);
676 #endif /* HAVE_SNPRINTF */
683 /* set of small tests for snprintf() */
690 printf("Suite of test for snprintf:\n");
691 printf("a_format\n");
692 printf("printf() format\n");
693 printf("snprintf() format\n\n");
695 /* Checking the field widths */
697 printf("/%%d/, 336\n");
698 snprintf(holder, sizeof holder, "/%d/\n", 336);
699 printf("/%d/\n", 336);
700 printf("%s\n", holder);
702 printf("/%%2d/, 336\n");
703 snprintf(holder, sizeof holder, "/%2d/\n", 336);
704 printf("/%2d/\n", 336);
705 printf("%s\n", holder);
707 printf("/%%10d/, 336\n");
708 snprintf(holder, sizeof holder, "/%10d/\n", 336);
709 printf("/%10d/\n", 336);
710 printf("%s\n", holder);
712 printf("/%%-10d/, 336\n");
713 snprintf(holder, sizeof holder, "/%-10d/\n", 336);
714 printf("/%-10d/\n", 336);
715 printf("%s\n", holder);
718 /* floating points */
720 printf("/%%f/, 1234.56\n");
721 snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
722 printf("/%f/\n", 1234.56);
723 printf("%s\n", holder);
725 printf("/%%e/, 1234.56\n");
726 snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
727 printf("/%e/\n", 1234.56);
728 printf("%s\n", holder);
730 printf("/%%4.2f/, 1234.56\n");
731 snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
732 printf("/%4.2f/\n", 1234.56);
733 printf("%s\n", holder);
735 printf("/%%3.1f/, 1234.56\n");
736 snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
737 printf("/%3.1f/\n", 1234.56);
738 printf("%s\n", holder);
740 printf("/%%10.3f/, 1234.56\n");
741 snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
742 printf("/%10.3f/\n", 1234.56);
743 printf("%s\n", holder);
745 printf("/%%10.3e/, 1234.56\n");
746 snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
747 printf("/%10.3e/\n", 1234.56);
748 printf("%s\n", holder);
750 printf("/%%+4.2f/, 1234.56\n");
751 snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
752 printf("/%+4.2f/\n", 1234.56);
753 printf("%s\n", holder);
755 printf("/%%010.2f/, 1234.56\n");
756 snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
757 printf("/%010.2f/\n", 1234.56);
758 printf("%s\n", holder);
760 #define BLURB "Outstanding acting !"
761 /* strings precisions */
763 printf("/%%2s/, \"%s\"\n", BLURB);
764 snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
765 printf("/%2s/\n", BLURB);
766 printf("%s\n", holder);
768 printf("/%%22s/ %s\n", BLURB);
769 snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
770 printf("/%22s/\n", BLURB);
771 printf("%s\n", holder);
773 printf("/%%22.5s/ %s\n", BLURB);
774 snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
775 printf("/%22.5s/\n", BLURB);
776 printf("%s\n", holder);
778 printf("/%%-22.5s/ %s\n", BLURB);
779 snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
780 printf("/%-22.5s/\n", BLURB);
781 printf("%s\n", holder);
785 printf("%%x %%X %%#x, 31, 31, 31\n");
786 snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
787 printf("%x %X %#x\n", 31, 31, 31);
788 printf("%s\n", holder);
790 printf("**%%d**%% d**%% d**, 42, 42, -42\n");
791 snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
792 printf("**%d**% d**% d**\n", 42, 42, -42);
793 printf("%s\n", holder);
797 printf("/%%g/, 31.4\n");
798 snprintf(holder, sizeof holder, "/%g/\n", 31.4);
799 printf("/%g/\n", 31.4);
800 printf("%s\n", holder);
802 printf("/%%.6g/, 31.4\n");
803 snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
804 printf("/%.6g/\n", 31.4);
805 printf("%s\n", holder);
807 printf("/%%.1G/, 31.4\n");
808 snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
809 printf("/%.1G/\n", 31.4);
810 printf("%s\n", holder);
813 printf("abc%n", &i); printf("%d\n", i);
814 snprintf(holder, sizeof holder, "abc%n", &i);
815 printf("%s", holder); printf("%d\n\n", i);
817 printf("%%*.*s --> 10.10\n");
818 snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
819 printf("%*.*s\n", 10, 10, BLURB);
820 printf("%s\n", holder);
822 printf("%%%%%%%%\n");
823 snprintf(holder, sizeof holder, "%%%%\n");
825 printf("%s\n", holder);
827 #define BIG "Hello this is a too big string for the buffer"
828 /* printf("A buffer to small of 10, trying to put this:\n");*/
829 printf("<%%>, %s\n", BIG);
830 i = snprintf(holder, 10, "%s\n", BIG);
831 printf("<%s>\n", BIG);
832 printf("<%s>\n", holder);