2 * $Id: snprintf.c,v 1.12 2000/11/21 21:24:52 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
66 for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
68 for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
73 * Find the integral part of the log in base 10
74 * Note: this not a real log10()
75 I just need and approximation(integerpart) of x in:
92 while (result >= r) {result *= .1; i++;}
95 while (result <= r) {result *= 10.; i++;}
101 * This function return the fraction part of a double
102 * and set in ip the integral part.
103 * In many ways it resemble the modf() found on most Un*x
106 integral(double real, double * ip)
110 double real_integral = 0.;
112 /* take care of the obvious */
113 /* equal to zero ? */
119 /* negative number ? */
128 /* the real work :-) */
129 for (j = log_10(real); j >= 0; j--) {
131 s = (real - real_integral)/p;
133 while (i + 1. <= s) {i++;}
134 real_integral += i*p;
137 return (real - real_integral);
140 #define PRECISION 1.e-6
142 * return an ascii representation of the integral part of the number
143 * and set fract to be an ascii representation of the fraction part
144 * the container for the fraction and the integral part or staticly
145 * declare with fix size
148 numtoa(double number, int base, int precision, char ** fract)
151 double ip, fp; /* integer and fraction part */
153 int digits = MAX_INT - 1;
154 static char integral_part[MAX_INT];
155 static char fraction_part[MAX_FRACT];
159 /* taking care of the obvious case: 0.0 */
161 integral_part[0] = '0';
162 integral_part[1] = '\0';
163 fraction_part[0] = '0';
164 fraction_part[1] = '\0';
165 return integral_part;
168 /* for negative numbers */
169 if ((sign = number) < 0.) {
171 digits--; /* sign consume one digit */
174 fraction = integral(number, &ip);
176 /* do the integral part */
178 integral_part[0] = '0';
181 for ( i = 0; i < digits && number != 0.; ++i) {
183 fp = integral(number, &ip);
184 ch = (int)((fp + PRECISION)*base); /* force to round */
185 integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
186 if (! isxdigit((unsigned char)integral_part[i])) /* bail out overflow !! */
192 /* Oh No !! out of bound, ho well fill it up ! */
194 for (i = 0; i < digits; ++i)
195 integral_part[i] = '9';
199 integral_part[i++] = '-';
201 integral_part[i] = '\0';
203 /* reverse every thing */
204 for ( i--, j = 0; j < i; j++, i--)
205 SWAP_INT(integral_part[i], integral_part[j]);
207 /* the fractionnal part */
208 for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
209 fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
210 if (! isdigit((unsigned char)fraction_part[i])) /* underflow ? */
212 fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
214 fraction_part[i] = '\0';
216 if (fract != (char **)0)
217 *fract = fraction_part;
219 return integral_part;
223 /* for %d and friends, it puts in holder
224 * the representation with the right padding
227 decimal(struct DATA *p, double d)
232 p->width -= strlen(tmp);
236 while (*tmp) { /* the integral */
243 /* for %o octal representation */
245 octal(struct DATA *p, double d)
250 p->width -= strlen(tmp);
251 if (p->square == FOUND) /* had prefix '0' for octal */
254 while (*tmp) { /* octal */
261 /* for %x %X hexadecimal representation */
263 hexa(struct DATA *p, double d)
268 p->width -= strlen(tmp);
269 if (p->square == FOUND) { /* prefix '0x' for hexa */
270 PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
273 while (*tmp) { /* hexa */
274 PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
282 strings(struct DATA *p, char *tmp)
287 if (p->precision != NOT_FOUND) /* the smallest number */
288 i = (i < p->precision ? i : p->precision);
291 while (i-- > 0) { /* put the sting */
298 /* %f or %g floating point representation */
300 floating(struct DATA *p, double d)
307 tmp = dtoa(d, p->precision, &tmp2);
308 /* calculate the padding. 1 for the dot */
309 p->width = p->width -
310 ((d > 0. && p->justify == RIGHT) ? 1:0) -
311 ((p->space == FOUND) ? 1:0) -
312 strlen(tmp) - p->precision - 1;
316 while (*tmp) { /* the integral */
320 if (p->precision != 0 || p->square == FOUND)
321 PUT_CHAR('.', p); /* put the '.' */
322 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
323 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
325 for (; *tmp2; tmp2++)
326 PUT_CHAR(*tmp2, p); /* the fraction */
331 /* %e %E %g exponent representation */
333 exponent(struct DATA *p, double d)
340 d = d / pow_10(j); /* get the Mantissa */
342 tmp = dtoa(d, p->precision, &tmp2);
343 /* 1 for unit, 1 for the '.', 1 for 'e|E',
344 * 1 for '+|-', 3 for 'exp' */
345 /* calculate how much padding need */
346 p->width = p->width -
347 ((d > 0. && p->justify == RIGHT) ? 1:0) -
348 ((p->space == FOUND) ? 1:0) - p->precision - 7;
352 while (*tmp) {/* the integral */
356 if (p->precision != 0 || p->square == FOUND)
357 PUT_CHAR('.', p); /* the '.' */
358 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
359 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
361 for (; *tmp2; tmp2++)
362 PUT_CHAR(*tmp2, p); /* the fraction */
364 if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
368 if (j > 0) { /* the sign of the exp */
374 tmp = itoa((double)j);
375 if (j < 9) { /* need to pad the exponent with 0 '000' */
376 PUT_CHAR('0', p); PUT_CHAR('0', p);
379 while (*tmp) { /* the exponent */
386 /* initialize the conversion specifiers */
388 conv_flag(char * s, struct DATA * p)
390 char number[MAX_FIELD/2];
393 p->precision = p->width = NOT_FOUND;
394 p->star_w = p->star_p = NOT_FOUND;
395 p->square = p->space = NOT_FOUND;
396 p->a_long = p->justify = NOT_FOUND;
401 case ' ': p->space = FOUND; break;
402 case '#': p->square = FOUND; break;
403 case '*': if (p->width == NOT_FOUND)
404 p->width = p->star_w = FOUND;
406 p->precision = p->star_p = FOUND;
408 case '+': p->justify = RIGHT; break;
409 case '-': p->justify = LEFT; break;
410 case '.': if (p->width == NOT_FOUND)
413 case '0': p->pad = '0'; break;
414 case '1': case '2': case '3':
415 case '4': case '5': case '6':
416 case '7': case '8': case '9': /* gob all the digits */
417 for (i = 0; isdigit((unsigned char)*s); i++, s++)
418 if (i < MAX_FIELD/2 - 1)
421 if (p->width == NOT_FOUND)
422 p->width = atoi(number);
424 p->precision = atoi(number);
425 s--; /* went to far go back */
432 vsnprintf(char *string, size_t length, const char * format, va_list args)
435 char conv_field[MAX_FIELD];
436 double d; /* temporary holder */
440 data.length = length - 1; /* leave room for '\0' */
441 data.holder = string;
446 /* sanity check, the string must be > 1 */
451 for (; *data.pf && (data.counter < data.length); data.pf++) {
452 if ( *data.pf == '%' ) { /* we got a magic % cookie */
453 conv_flag((char *)0, &data); /* initialise format flags */
454 for (state = 1; *data.pf && state;) {
455 switch (*(++data.pf)) {
456 case '\0': /* a NULL here ? ? bail out */
460 case 'f': /* float, double */
462 d = va_arg(args, double);
470 d = va_arg(args, double);
473 * for '%g|%G' ANSI: use f if exponent
474 * is in the range or [-4,p] exclusively
477 if (-4 < i && i < data.precision)
484 case 'E': /* Exponent double */
486 d = va_arg(args, double);
490 case 'u': /* unsigned decimal */
492 if (data.a_long == FOUND)
493 d = va_arg(args, unsigned long);
495 d = va_arg(args, unsigned int);
499 case 'd': /* decimal */
500 case 'i': /* "integer" (signed decimal) */
502 if (data.a_long == FOUND)
503 d = va_arg(args, long);
505 d = va_arg(args, int);
509 case 'o': /* octal */
511 if (data.a_long == FOUND)
512 d = va_arg(args, unsigned long);
514 d = va_arg(args, unsigned int);
519 case 'X': /* hexadecimal */
521 if (data.a_long == FOUND)
522 d = va_arg(args, unsigned long);
524 d = va_arg(args, unsigned int);
528 case 'c': /* character */
529 d = va_arg(args, int);
533 case 's': /* string */
535 strings(&data, va_arg(args, char *));
539 *(va_arg(args, int *)) = data.counter; /* what's the count ? */
547 case '%': /* nothing just % */
548 PUT_CHAR('%', &data);
551 case '#': case ' ': case '+': case '*':
552 case '-': case '.': case '0': case '1':
553 case '2': case '3': case '4': case '5':
554 case '6': case '7': case '8': case '9':
555 /* initialize width and precision */
556 for (i = 0; isflag((unsigned char)*data.pf); i++, data.pf++)
557 if (i < MAX_FIELD - 1)
558 conv_field[i] = *data.pf;
559 conv_field[i] = '\0';
560 conv_flag(conv_field, &data);
561 data.pf--; /* went to far go back */
564 /* is this an error ? maybe bail out */
568 } /* end of for state */
570 PUT_CHAR(*data.pf, &data); /* add the char the string */
574 *data.holder = '\0'; /* the end ye ! */
579 #ifndef HAVE_SNPRINTF
582 snprintf(char *string, size_t length, const char * format, ...)
587 #if defined(HAVE_STDARG_H)
588 va_start(args, format);
593 rval = vsnprintf (string, length, format, args);
600 #endif /* HAVE_SNPRINTF */
607 /* set of small tests for snprintf() */
614 printf("Suite of test for snprintf:\n");
615 printf("a_format\n");
616 printf("printf() format\n");
617 printf("snprintf() format\n\n");
619 /* Checking the field widths */
621 printf("/%%d/, 336\n");
622 snprintf(holder, sizeof holder, "/%d/\n", 336);
623 printf("/%d/\n", 336);
624 printf("%s\n", holder);
626 printf("/%%2d/, 336\n");
627 snprintf(holder, sizeof holder, "/%2d/\n", 336);
628 printf("/%2d/\n", 336);
629 printf("%s\n", holder);
631 printf("/%%10d/, 336\n");
632 snprintf(holder, sizeof holder, "/%10d/\n", 336);
633 printf("/%10d/\n", 336);
634 printf("%s\n", holder);
636 printf("/%%-10d/, 336\n");
637 snprintf(holder, sizeof holder, "/%-10d/\n", 336);
638 printf("/%-10d/\n", 336);
639 printf("%s\n", holder);
642 /* floating points */
644 printf("/%%f/, 1234.56\n");
645 snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
646 printf("/%f/\n", 1234.56);
647 printf("%s\n", holder);
649 printf("/%%e/, 1234.56\n");
650 snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
651 printf("/%e/\n", 1234.56);
652 printf("%s\n", holder);
654 printf("/%%4.2f/, 1234.56\n");
655 snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
656 printf("/%4.2f/\n", 1234.56);
657 printf("%s\n", holder);
659 printf("/%%3.1f/, 1234.56\n");
660 snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
661 printf("/%3.1f/\n", 1234.56);
662 printf("%s\n", holder);
664 printf("/%%10.3f/, 1234.56\n");
665 snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
666 printf("/%10.3f/\n", 1234.56);
667 printf("%s\n", holder);
669 printf("/%%10.3e/, 1234.56\n");
670 snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
671 printf("/%10.3e/\n", 1234.56);
672 printf("%s\n", holder);
674 printf("/%%+4.2f/, 1234.56\n");
675 snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
676 printf("/%+4.2f/\n", 1234.56);
677 printf("%s\n", holder);
679 printf("/%%010.2f/, 1234.56\n");
680 snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
681 printf("/%010.2f/\n", 1234.56);
682 printf("%s\n", holder);
684 #define BLURB "Outstanding acting !"
685 /* strings precisions */
687 printf("/%%2s/, \"%s\"\n", BLURB);
688 snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
689 printf("/%2s/\n", BLURB);
690 printf("%s\n", holder);
692 printf("/%%22s/ %s\n", BLURB);
693 snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
694 printf("/%22s/\n", BLURB);
695 printf("%s\n", holder);
697 printf("/%%22.5s/ %s\n", BLURB);
698 snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
699 printf("/%22.5s/\n", BLURB);
700 printf("%s\n", holder);
702 printf("/%%-22.5s/ %s\n", BLURB);
703 snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
704 printf("/%-22.5s/\n", BLURB);
705 printf("%s\n", holder);
709 printf("%%x %%X %%#x, 31, 31, 31\n");
710 snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
711 printf("%x %X %#x\n", 31, 31, 31);
712 printf("%s\n", holder);
714 printf("**%%d**%% d**%% d**, 42, 42, -42\n");
715 snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
716 printf("**%d**% d**% d**\n", 42, 42, -42);
717 printf("%s\n", holder);
721 printf("/%%g/, 31.4\n");
722 snprintf(holder, sizeof holder, "/%g/\n", 31.4);
723 printf("/%g/\n", 31.4);
724 printf("%s\n", holder);
726 printf("/%%.6g/, 31.4\n");
727 snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
728 printf("/%.6g/\n", 31.4);
729 printf("%s\n", holder);
731 printf("/%%.1G/, 31.4\n");
732 snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
733 printf("/%.1G/\n", 31.4);
734 printf("%s\n", holder);
737 printf("abc%n", &i); printf("%d\n", i);
738 snprintf(holder, sizeof holder, "abc%n", &i);
739 printf("%s", holder); printf("%d\n\n", i);
741 printf("%%*.*s --> 10.10\n");
742 snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
743 printf("%*.*s\n", 10, 10, BLURB);
744 printf("%s\n", holder);
746 printf("%%%%%%%%\n");
747 snprintf(holder, sizeof holder, "%%%%\n");
749 printf("%s\n", holder);
751 #define BIG "Hello this is a too big string for the buffer"
752 /* printf("A buffer to small of 10, trying to put this:\n");*/
753 printf("<%%>, %s\n", BIG);
754 i = snprintf(holder, 10, "%s\n", BIG);
755 printf("<%s>\n", BIG);
756 printf("<%s>\n", holder);