2 * $Id: snprintf.c,v 1.14 2002/08/28 21:00:41 jmayer 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 */
55 #include "snprintf-imp.h"
58 * Find the nth power of 10
67 for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
69 for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
74 * Find the integral part of the log in base 10
75 * Note: this not a real log10()
76 I just need and approximation(integerpart) of x in:
93 while (result >= r) {result *= .1; i++;}
96 while (result <= r) {result *= 10.; i++;}
102 * This function return the fraction part of a double
103 * and set in ip the integral part.
104 * In many ways it resemble the modf() found on most Un*x
107 integral(double real, double * ip)
111 double real_integral = 0.;
113 /* take care of the obvious */
114 /* equal to zero ? */
120 /* negative number ? */
129 /* the real work :-) */
130 for (j = log_10(real); j >= 0; j--) {
132 s = (real - real_integral)/p;
134 while (i + 1. <= s) {i++;}
135 real_integral += i*p;
138 return (real - real_integral);
141 #define PRECISION 1.e-6
143 * return an ascii representation of the integral part of the number
144 * and set fract to be an ascii representation of the fraction part
145 * the container for the fraction and the integral part or staticly
146 * declare with fix size
149 numtoa(double number, int base, int precision, char ** fract)
152 double ip, fp; /* integer and fraction part */
154 int digits = MAX_INT - 1;
155 static char integral_part[MAX_INT];
156 static char fraction_part[MAX_FRACT];
160 /* taking care of the obvious case: 0.0 */
162 integral_part[0] = '0';
163 integral_part[1] = '\0';
164 fraction_part[0] = '0';
165 fraction_part[1] = '\0';
166 return integral_part;
169 /* for negative numbers */
170 if ((sign = number) < 0.) {
172 digits--; /* sign consume one digit */
175 fraction = integral(number, &ip);
177 /* do the integral part */
179 integral_part[0] = '0';
182 for ( i = 0; i < digits && number != 0.; ++i) {
184 fp = integral(number, &ip);
185 ch = (int)((fp + PRECISION)*base); /* force to round */
186 integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
187 if (! isxdigit((unsigned char)integral_part[i])) /* bail out overflow !! */
193 /* Oh No !! out of bound, ho well fill it up ! */
195 for (i = 0; i < digits; ++i)
196 integral_part[i] = '9';
200 integral_part[i++] = '-';
202 integral_part[i] = '\0';
204 /* reverse every thing */
205 for ( i--, j = 0; j < i; j++, i--)
206 SWAP_INT(integral_part[i], integral_part[j]);
208 /* the fractionnal part */
209 for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
210 fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
211 if (! isdigit((unsigned char)fraction_part[i])) /* underflow ? */
213 fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
215 fraction_part[i] = '\0';
217 if (fract != (char **)0)
218 *fract = fraction_part;
220 return integral_part;
224 /* for %d and friends, it puts in holder
225 * the representation with the right padding
228 decimal(struct DATA *p, double d)
233 p->width -= strlen(tmp);
237 while (*tmp) { /* the integral */
244 /* for %o octal representation */
246 octal(struct DATA *p, double d)
251 p->width -= strlen(tmp);
252 if (p->square == FOUND) /* had prefix '0' for octal */
255 while (*tmp) { /* octal */
262 /* for %x %X hexadecimal representation */
264 hexa(struct DATA *p, double d)
269 p->width -= strlen(tmp);
270 if (p->square == FOUND) { /* prefix '0x' for hexa */
271 PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
274 while (*tmp) { /* hexa */
275 PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
283 strings(struct DATA *p, char *tmp)
288 if (p->precision != NOT_FOUND) /* the smallest number */
289 i = (i < p->precision ? i : p->precision);
292 while (i-- > 0) { /* put the sting */
299 /* %f or %g floating point representation */
301 floating(struct DATA *p, double d)
308 tmp = dtoa(d, p->precision, &tmp2);
309 /* calculate the padding. 1 for the dot */
310 p->width = p->width -
311 ((d > 0. && p->justify == RIGHT) ? 1:0) -
312 ((p->space == FOUND) ? 1:0) -
313 strlen(tmp) - p->precision - 1;
317 while (*tmp) { /* the integral */
321 if (p->precision != 0 || p->square == FOUND)
322 PUT_CHAR('.', p); /* put the '.' */
323 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
324 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
326 for (; *tmp2; tmp2++)
327 PUT_CHAR(*tmp2, p); /* the fraction */
332 /* %e %E %g exponent representation */
334 exponent(struct DATA *p, double d)
341 d = d / pow_10(j); /* get the Mantissa */
343 tmp = dtoa(d, p->precision, &tmp2);
344 /* 1 for unit, 1 for the '.', 1 for 'e|E',
345 * 1 for '+|-', 3 for 'exp' */
346 /* calculate how much padding need */
347 p->width = p->width -
348 ((d > 0. && p->justify == RIGHT) ? 1:0) -
349 ((p->space == FOUND) ? 1:0) - p->precision - 7;
353 while (*tmp) {/* the integral */
357 if (p->precision != 0 || p->square == FOUND)
358 PUT_CHAR('.', p); /* the '.' */
359 if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
360 for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
362 for (; *tmp2; tmp2++)
363 PUT_CHAR(*tmp2, p); /* the fraction */
365 if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
369 if (j > 0) { /* the sign of the exp */
375 tmp = itoa((double)j);
376 if (j < 9) { /* need to pad the exponent with 0 '000' */
377 PUT_CHAR('0', p); PUT_CHAR('0', p);
380 while (*tmp) { /* the exponent */
387 /* initialize the conversion specifiers */
389 conv_flag(char * s, struct DATA * p)
391 char number[MAX_FIELD/2];
394 p->precision = p->width = NOT_FOUND;
395 p->star_w = p->star_p = NOT_FOUND;
396 p->square = p->space = NOT_FOUND;
397 p->a_long = p->justify = NOT_FOUND;
402 case ' ': p->space = FOUND; break;
403 case '#': p->square = FOUND; break;
404 case '*': if (p->width == NOT_FOUND)
405 p->width = p->star_w = FOUND;
407 p->precision = p->star_p = FOUND;
409 case '+': p->justify = RIGHT; break;
410 case '-': p->justify = LEFT; break;
411 case '.': if (p->width == NOT_FOUND)
414 case '0': p->pad = '0'; break;
415 case '1': case '2': case '3':
416 case '4': case '5': case '6':
417 case '7': case '8': case '9': /* gob all the digits */
418 for (i = 0; isdigit((unsigned char)*s); i++, s++)
419 if (i < MAX_FIELD/2 - 1)
422 if (p->width == NOT_FOUND)
423 p->width = atoi(number);
425 p->precision = atoi(number);
426 s--; /* went to far go back */
433 vsnprintf(char *string, size_t length, const char * format, va_list args)
436 char conv_field[MAX_FIELD];
437 double d; /* temporary holder */
441 data.length = length - 1; /* leave room for '\0' */
442 data.holder = string;
447 /* sanity check, the string must be > 1 */
452 for (; *data.pf && (data.counter < data.length); data.pf++) {
453 if ( *data.pf == '%' ) { /* we got a magic % cookie */
454 conv_flag((char *)0, &data); /* initialise format flags */
455 for (state = 1; *data.pf && state;) {
456 switch (*(++data.pf)) {
457 case '\0': /* a NULL here ? ? bail out */
461 case 'f': /* float, double */
463 d = va_arg(args, double);
471 d = va_arg(args, double);
474 * for '%g|%G' ANSI: use f if exponent
475 * is in the range or [-4,p] exclusively
478 if (-4 < i && i < data.precision)
485 case 'E': /* Exponent double */
487 d = va_arg(args, double);
491 case 'u': /* unsigned decimal */
493 if (data.a_long == FOUND)
494 d = va_arg(args, unsigned long);
496 d = va_arg(args, unsigned int);
500 case 'd': /* decimal */
501 case 'i': /* "integer" (signed decimal) */
503 if (data.a_long == FOUND)
504 d = va_arg(args, long);
506 d = va_arg(args, int);
510 case 'o': /* octal */
512 if (data.a_long == FOUND)
513 d = va_arg(args, unsigned long);
515 d = va_arg(args, unsigned int);
520 case 'X': /* hexadecimal */
522 if (data.a_long == FOUND)
523 d = va_arg(args, unsigned long);
525 d = va_arg(args, unsigned int);
529 case 'c': /* character */
530 d = va_arg(args, int);
534 case 's': /* string */
536 strings(&data, va_arg(args, char *));
540 *(va_arg(args, int *)) = data.counter; /* what's the count ? */
548 case '%': /* nothing just % */
549 PUT_CHAR('%', &data);
552 case '#': case ' ': case '+': case '*':
553 case '-': case '.': case '0': case '1':
554 case '2': case '3': case '4': case '5':
555 case '6': case '7': case '8': case '9':
556 /* initialize width and precision */
557 for (i = 0; isflag((unsigned char)*data.pf); i++, data.pf++)
558 if (i < MAX_FIELD - 1)
559 conv_field[i] = *data.pf;
560 conv_field[i] = '\0';
561 conv_flag(conv_field, &data);
562 data.pf--; /* went to far go back */
565 /* is this an error ? maybe bail out */
569 } /* end of for state */
571 PUT_CHAR(*data.pf, &data); /* add the char the string */
575 *data.holder = '\0'; /* the end ye ! */
580 #ifndef HAVE_SNPRINTF
583 snprintf(char *string, size_t length, const char * format, ...)
588 #if defined(HAVE_STDARG_H)
589 va_start(args, format);
594 rval = vsnprintf (string, length, format, args);
601 #endif /* HAVE_SNPRINTF */
608 /* set of small tests for snprintf() */
615 printf("Suite of test for snprintf:\n");
616 printf("a_format\n");
617 printf("printf() format\n");
618 printf("snprintf() format\n\n");
620 /* Checking the field widths */
622 printf("/%%d/, 336\n");
623 snprintf(holder, sizeof holder, "/%d/\n", 336);
624 printf("/%d/\n", 336);
625 printf("%s\n", holder);
627 printf("/%%2d/, 336\n");
628 snprintf(holder, sizeof holder, "/%2d/\n", 336);
629 printf("/%2d/\n", 336);
630 printf("%s\n", holder);
632 printf("/%%10d/, 336\n");
633 snprintf(holder, sizeof holder, "/%10d/\n", 336);
634 printf("/%10d/\n", 336);
635 printf("%s\n", holder);
637 printf("/%%-10d/, 336\n");
638 snprintf(holder, sizeof holder, "/%-10d/\n", 336);
639 printf("/%-10d/\n", 336);
640 printf("%s\n", holder);
643 /* floating points */
645 printf("/%%f/, 1234.56\n");
646 snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
647 printf("/%f/\n", 1234.56);
648 printf("%s\n", holder);
650 printf("/%%e/, 1234.56\n");
651 snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
652 printf("/%e/\n", 1234.56);
653 printf("%s\n", holder);
655 printf("/%%4.2f/, 1234.56\n");
656 snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
657 printf("/%4.2f/\n", 1234.56);
658 printf("%s\n", holder);
660 printf("/%%3.1f/, 1234.56\n");
661 snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
662 printf("/%3.1f/\n", 1234.56);
663 printf("%s\n", holder);
665 printf("/%%10.3f/, 1234.56\n");
666 snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
667 printf("/%10.3f/\n", 1234.56);
668 printf("%s\n", holder);
670 printf("/%%10.3e/, 1234.56\n");
671 snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
672 printf("/%10.3e/\n", 1234.56);
673 printf("%s\n", holder);
675 printf("/%%+4.2f/, 1234.56\n");
676 snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
677 printf("/%+4.2f/\n", 1234.56);
678 printf("%s\n", holder);
680 printf("/%%010.2f/, 1234.56\n");
681 snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
682 printf("/%010.2f/\n", 1234.56);
683 printf("%s\n", holder);
685 #define BLURB "Outstanding acting !"
686 /* strings precisions */
688 printf("/%%2s/, \"%s\"\n", BLURB);
689 snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
690 printf("/%2s/\n", BLURB);
691 printf("%s\n", holder);
693 printf("/%%22s/ %s\n", BLURB);
694 snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
695 printf("/%22s/\n", BLURB);
696 printf("%s\n", holder);
698 printf("/%%22.5s/ %s\n", BLURB);
699 snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
700 printf("/%22.5s/\n", BLURB);
701 printf("%s\n", holder);
703 printf("/%%-22.5s/ %s\n", BLURB);
704 snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
705 printf("/%-22.5s/\n", BLURB);
706 printf("%s\n", holder);
710 printf("%%x %%X %%#x, 31, 31, 31\n");
711 snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
712 printf("%x %X %#x\n", 31, 31, 31);
713 printf("%s\n", holder);
715 printf("**%%d**%% d**%% d**, 42, 42, -42\n");
716 snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
717 printf("**%d**% d**% d**\n", 42, 42, -42);
718 printf("%s\n", holder);
722 printf("/%%g/, 31.4\n");
723 snprintf(holder, sizeof holder, "/%g/\n", 31.4);
724 printf("/%g/\n", 31.4);
725 printf("%s\n", holder);
727 printf("/%%.6g/, 31.4\n");
728 snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
729 printf("/%.6g/\n", 31.4);
730 printf("%s\n", holder);
732 printf("/%%.1G/, 31.4\n");
733 snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
734 printf("/%.1G/\n", 31.4);
735 printf("%s\n", holder);
738 printf("abc%n", &i); printf("%d\n", i);
739 snprintf(holder, sizeof holder, "abc%n", &i);
740 printf("%s", holder); printf("%d\n\n", i);
742 printf("%%*.*s --> 10.10\n");
743 snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
744 printf("%*.*s\n", 10, 10, BLURB);
745 printf("%s\n", holder);
747 printf("%%%%%%%%\n");
748 snprintf(holder, sizeof holder, "%%%%\n");
750 printf("%s\n", holder);
752 #define BIG "Hello this is a too big string for the buffer"
753 /* printf("A buffer to small of 10, trying to put this:\n");*/
754 printf("<%%>, %s\n", BIG);
755 i = snprintf(holder, 10, "%s\n", BIG);
756 printf("<%s>\n", BIG);
757 printf("<%s>\n", holder);