2 * $Id: snprintf.c,v 1.11 2000/08/11 22:03:02 guy Exp $
6 Unix snprintf implementation.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13 It can be redistribute also under the terms of GNU Library General
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * put the program under LGPL.
30 * added changes from Miles Bader
31 * corrected a bug with %f
32 * added support for %#g
33 * added more comments :-)
35 * supporting must ANSI syntaxic_sugars
39 THANKS(for the patches and ideas):
50 #include <stdlib.h> /* for atoi and for size_t */
54 #include "snprintf-imp.h"
57 * Find the nth power of 10
71 for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
73 for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
78 * Find the integral part of the log in base 10
79 * Note: this not a real log10()
80 I just need and approximation(integerpart) of x in:
102 while (result >= r) {result *= .1; i++;}
105 while (result <= r) {result *= 10.; i++;}
111 * This function return the fraction part of a double
112 * and set in ip the integral part.
113 * In many ways it resemble the modf() found on most Un*x
117 integral(double real, double * ip)
126 double real_integral = 0.;
128 /* take care of the obvious */
129 /* equal to zero ? */
135 /* negative number ? */
144 /* the real work :-) */
145 for (j = log_10(real); j >= 0; j--) {
147 s = (real - real_integral)/p;
149 while (i + 1. <= s) {i++;}
150 real_integral += i*p;
153 return (real - real_integral);
156 #define PRECISION 1.e-6
158 * return an ascii representation of the integral part of the number
159 * and set fract to be an ascii representation of the fraction part
160 * the container for the fraction and the integral part or staticly
161 * declare with fix size
165 numtoa(double number, int base, int precision, char ** fract)
167 numtoa(number, base, precision, fract)
175 double ip, fp; /* integer and fraction part */
177 int digits = MAX_INT - 1;
178 static char integral_part[MAX_INT];
179 static char fraction_part[MAX_FRACT];
183 /* taking care of the obvious case: 0.0 */
185 integral_part[0] = '0';
186 integral_part[1] = '\0';
187 fraction_part[0] = '0';
188 fraction_part[1] = '\0';
189 return integral_part;
192 /* for negative numbers */
193 if ((sign = number) < 0.) {
195 digits--; /* sign consume one digit */
198 fraction = integral(number, &ip);
200 /* do the integral part */
202 integral_part[0] = '0';
205 for ( i = 0; i < digits && number != 0.; ++i) {
207 fp = integral(number, &ip);
208 ch = (int)((fp + PRECISION)*base); /* force to round */
209 integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
210 if (! isxdigit((unsigned char)integral_part[i])) /* bail out overflow !! */
216 /* Oh No !! out of bound, ho well fill it up ! */
218 for (i = 0; i < digits; ++i)
219 integral_part[i] = '9';
223 integral_part[i++] = '-';
225 integral_part[i] = '\0';
227 /* reverse every thing */
228 for ( i--, j = 0; j < i; j++, i--)
229 SWAP_INT(integral_part[i], integral_part[j]);
231 /* the fractionnal part */
232 for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
233 fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
234 if (! isdigit((unsigned char)fraction_part[i])) /* underflow ? */
236 fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
238 fraction_part[i] = '\0';
240 if (fract != (char **)0)
241 *fract = fraction_part;
243 return integral_part;
247 /* for %d and friends, it puts in holder
248 * the representation with the right padding
252 decimal(struct DATA *p, double d)
262 p->width -= strlen(tmp);
266 while (*tmp) { /* the integral */
273 /* for %o octal representation */
276 octal(struct DATA *p, double d)
286 p->width -= strlen(tmp);
287 if (p->square == FOUND) /* had prefix '0' for octal */
290 while (*tmp) { /* octal */
297 /* for %x %X hexadecimal representation */
300 hexa(struct DATA *p, double d)
310 p->width -= strlen(tmp);
311 if (p->square == FOUND) { /* prefix '0x' for hexa */
312 PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
315 while (*tmp) { /* hexa */
316 PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
325 strings(struct DATA *p, char *tmp)
335 if (p->precision != NOT_FOUND) /* the smallest number */
336 i = (i < p->precision ? i : p->precision);
339 while (i-- > 0) { /* put the sting */
346 /* %f or %g floating point representation */
349 floating(struct DATA *p, double d)
361 tmp = dtoa(d, p->precision, &tmp2);
362 /* calculate the padding. 1 for the dot */
363 p->width = p->width -
364 ((d > 0. && p->justify == RIGHT) ? 1:0) -
365 ((p->space == FOUND) ? 1:0) -
366 strlen(tmp) - p->precision - 1;
370 while (*tmp) { /* the integral */
374 if (p->precision != 0 || p->square == FOUND)
375 PUT_CHAR('.', p); /* put the '.' */
376 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
377 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
379 for (; *tmp2; tmp2++)
380 PUT_CHAR(*tmp2, p); /* the fraction */
385 /* %e %E %g exponent representation */
388 exponent(struct DATA *p, double d)
400 d = d / pow_10(j); /* get the Mantissa */
402 tmp = dtoa(d, p->precision, &tmp2);
403 /* 1 for unit, 1 for the '.', 1 for 'e|E',
404 * 1 for '+|-', 3 for 'exp' */
405 /* calculate how much padding need */
406 p->width = p->width -
407 ((d > 0. && p->justify == RIGHT) ? 1:0) -
408 ((p->space == FOUND) ? 1:0) - p->precision - 7;
412 while (*tmp) {/* the integral */
416 if (p->precision != 0 || p->square == FOUND)
417 PUT_CHAR('.', p); /* the '.' */
418 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
419 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
421 for (; *tmp2; tmp2++)
422 PUT_CHAR(*tmp2, p); /* the fraction */
424 if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
428 if (j > 0) { /* the sign of the exp */
434 tmp = itoa((double)j);
435 if (j < 9) { /* need to pad the exponent with 0 '000' */
436 PUT_CHAR('0', p); PUT_CHAR('0', p);
439 while (*tmp) { /* the exponent */
446 /* initialize the conversion specifiers */
449 conv_flag(char * s, struct DATA * p)
456 char number[MAX_FIELD/2];
459 p->precision = p->width = NOT_FOUND;
460 p->star_w = p->star_p = NOT_FOUND;
461 p->square = p->space = NOT_FOUND;
462 p->a_long = p->justify = NOT_FOUND;
467 case ' ': p->space = FOUND; break;
468 case '#': p->square = FOUND; break;
469 case '*': if (p->width == NOT_FOUND)
470 p->width = p->star_w = FOUND;
472 p->precision = p->star_p = FOUND;
474 case '+': p->justify = RIGHT; break;
475 case '-': p->justify = LEFT; break;
476 case '.': if (p->width == NOT_FOUND)
479 case '0': p->pad = '0'; break;
480 case '1': case '2': case '3':
481 case '4': case '5': case '6':
482 case '7': case '8': case '9': /* gob all the digits */
483 for (i = 0; isdigit((unsigned char)*s); i++, s++)
484 if (i < MAX_FIELD/2 - 1)
487 if (p->width == NOT_FOUND)
488 p->width = atoi(number);
490 p->precision = atoi(number);
491 s--; /* went to far go back */
499 vsnprintf(char *string, size_t length, const char * format, va_list args)
501 vsnprintf(string, length, format, args)
509 char conv_field[MAX_FIELD];
510 double d; /* temporary holder */
514 data.length = length - 1; /* leave room for '\0' */
515 data.holder = string;
520 /* sanity check, the string must be > 1 */
525 for (; *data.pf && (data.counter < data.length); data.pf++) {
526 if ( *data.pf == '%' ) { /* we got a magic % cookie */
527 conv_flag((char *)0, &data); /* initialise format flags */
528 for (state = 1; *data.pf && state;) {
529 switch (*(++data.pf)) {
530 case '\0': /* a NULL here ? ? bail out */
534 case 'f': /* float, double */
536 d = va_arg(args, double);
544 d = va_arg(args, double);
547 * for '%g|%G' ANSI: use f if exponent
548 * is in the range or [-4,p] exclusively
551 if (-4 < i && i < data.precision)
558 case 'E': /* Exponent double */
560 d = va_arg(args, double);
564 case 'u': /* unsigned decimal */
566 if (data.a_long == FOUND)
567 d = va_arg(args, unsigned long);
569 d = va_arg(args, unsigned int);
573 case 'd': /* decimal */
574 case 'i': /* "integer" (signed decimal) */
576 if (data.a_long == FOUND)
577 d = va_arg(args, long);
579 d = va_arg(args, int);
583 case 'o': /* octal */
585 if (data.a_long == FOUND)
586 d = va_arg(args, unsigned long);
588 d = va_arg(args, unsigned int);
593 case 'X': /* hexadecimal */
595 if (data.a_long == FOUND)
596 d = va_arg(args, unsigned long);
598 d = va_arg(args, unsigned int);
602 case 'c': /* character */
603 d = va_arg(args, int);
607 case 's': /* string */
609 strings(&data, va_arg(args, char *));
613 *(va_arg(args, int *)) = data.counter; /* what's the count ? */
621 case '%': /* nothing just % */
622 PUT_CHAR('%', &data);
625 case '#': case ' ': case '+': case '*':
626 case '-': case '.': case '0': case '1':
627 case '2': case '3': case '4': case '5':
628 case '6': case '7': case '8': case '9':
629 /* initialize width and precision */
630 for (i = 0; isflag((unsigned char)*data.pf); i++, data.pf++)
631 if (i < MAX_FIELD - 1)
632 conv_field[i] = *data.pf;
633 conv_field[i] = '\0';
634 conv_flag(conv_field, &data);
635 data.pf--; /* went to far go back */
638 /* is this an error ? maybe bail out */
642 } /* end of for state */
644 PUT_CHAR(*data.pf, &data); /* add the char the string */
648 *data.holder = '\0'; /* the end ye ! */
653 #ifndef HAVE_SNPRINTF
656 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
657 snprintf(char *string, size_t length, const char * format, ...)
659 snprintf(string, length, format, va_alist)
669 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
670 va_start(args, format);
675 rval = vsnprintf (string, length, format, args);
682 #endif /* HAVE_SNPRINTF */
689 /* set of small tests for snprintf() */
696 printf("Suite of test for snprintf:\n");
697 printf("a_format\n");
698 printf("printf() format\n");
699 printf("snprintf() format\n\n");
701 /* Checking the field widths */
703 printf("/%%d/, 336\n");
704 snprintf(holder, sizeof holder, "/%d/\n", 336);
705 printf("/%d/\n", 336);
706 printf("%s\n", holder);
708 printf("/%%2d/, 336\n");
709 snprintf(holder, sizeof holder, "/%2d/\n", 336);
710 printf("/%2d/\n", 336);
711 printf("%s\n", holder);
713 printf("/%%10d/, 336\n");
714 snprintf(holder, sizeof holder, "/%10d/\n", 336);
715 printf("/%10d/\n", 336);
716 printf("%s\n", holder);
718 printf("/%%-10d/, 336\n");
719 snprintf(holder, sizeof holder, "/%-10d/\n", 336);
720 printf("/%-10d/\n", 336);
721 printf("%s\n", holder);
724 /* floating points */
726 printf("/%%f/, 1234.56\n");
727 snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
728 printf("/%f/\n", 1234.56);
729 printf("%s\n", holder);
731 printf("/%%e/, 1234.56\n");
732 snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
733 printf("/%e/\n", 1234.56);
734 printf("%s\n", holder);
736 printf("/%%4.2f/, 1234.56\n");
737 snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
738 printf("/%4.2f/\n", 1234.56);
739 printf("%s\n", holder);
741 printf("/%%3.1f/, 1234.56\n");
742 snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
743 printf("/%3.1f/\n", 1234.56);
744 printf("%s\n", holder);
746 printf("/%%10.3f/, 1234.56\n");
747 snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
748 printf("/%10.3f/\n", 1234.56);
749 printf("%s\n", holder);
751 printf("/%%10.3e/, 1234.56\n");
752 snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
753 printf("/%10.3e/\n", 1234.56);
754 printf("%s\n", holder);
756 printf("/%%+4.2f/, 1234.56\n");
757 snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
758 printf("/%+4.2f/\n", 1234.56);
759 printf("%s\n", holder);
761 printf("/%%010.2f/, 1234.56\n");
762 snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
763 printf("/%010.2f/\n", 1234.56);
764 printf("%s\n", holder);
766 #define BLURB "Outstanding acting !"
767 /* strings precisions */
769 printf("/%%2s/, \"%s\"\n", BLURB);
770 snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
771 printf("/%2s/\n", BLURB);
772 printf("%s\n", holder);
774 printf("/%%22s/ %s\n", BLURB);
775 snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
776 printf("/%22s/\n", BLURB);
777 printf("%s\n", holder);
779 printf("/%%22.5s/ %s\n", BLURB);
780 snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
781 printf("/%22.5s/\n", BLURB);
782 printf("%s\n", holder);
784 printf("/%%-22.5s/ %s\n", BLURB);
785 snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
786 printf("/%-22.5s/\n", BLURB);
787 printf("%s\n", holder);
791 printf("%%x %%X %%#x, 31, 31, 31\n");
792 snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
793 printf("%x %X %#x\n", 31, 31, 31);
794 printf("%s\n", holder);
796 printf("**%%d**%% d**%% d**, 42, 42, -42\n");
797 snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
798 printf("**%d**% d**% d**\n", 42, 42, -42);
799 printf("%s\n", holder);
803 printf("/%%g/, 31.4\n");
804 snprintf(holder, sizeof holder, "/%g/\n", 31.4);
805 printf("/%g/\n", 31.4);
806 printf("%s\n", holder);
808 printf("/%%.6g/, 31.4\n");
809 snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
810 printf("/%.6g/\n", 31.4);
811 printf("%s\n", holder);
813 printf("/%%.1G/, 31.4\n");
814 snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
815 printf("/%.1G/\n", 31.4);
816 printf("%s\n", holder);
819 printf("abc%n", &i); printf("%d\n", i);
820 snprintf(holder, sizeof holder, "abc%n", &i);
821 printf("%s", holder); printf("%d\n\n", i);
823 printf("%%*.*s --> 10.10\n");
824 snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
825 printf("%*.*s\n", 10, 10, BLURB);
826 printf("%s\n", holder);
828 printf("%%%%%%%%\n");
829 snprintf(holder, sizeof holder, "%%%%\n");
831 printf("%s\n", holder);
833 #define BIG "Hello this is a too big string for the buffer"
834 /* printf("A buffer to small of 10, trying to put this:\n");*/
835 printf("<%%>, %s\n", BIG);
836 i = snprintf(holder, 10, "%s\n", BIG);
837 printf("<%s>\n", BIG);
838 printf("<%s>\n", holder);