2000-08-11 22:03:03 +00:00
|
|
|
/*
|
2002-08-28 21:04:11 +00:00
|
|
|
* $Id: snprintf.c,v 1.14 2002/08/28 21:00:41 jmayer Exp $
|
2000-08-11 22:03:03 +00:00
|
|
|
*/
|
1998-09-16 02:39:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Unix snprintf implementation.
|
|
|
|
Version 1.2
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
It can be redistribute also under the terms of GNU Library General
|
2000-08-11 22:03:03 +00:00
|
|
|
Public License.
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
Revision History:
|
|
|
|
|
|
|
|
1.2:
|
|
|
|
* put the program under LGPL.
|
|
|
|
1.1:
|
|
|
|
* added changes from Miles Bader
|
|
|
|
* corrected a bug with %f
|
|
|
|
* added support for %#g
|
|
|
|
* added more comments :-)
|
|
|
|
1.0:
|
|
|
|
* supporting must ANSI syntaxic_sugars
|
|
|
|
0.0:
|
|
|
|
* suppot %s %c %d
|
|
|
|
|
|
|
|
THANKS(for the patches and ideas):
|
|
|
|
Miles Bader
|
|
|
|
Cyrille Rustom
|
|
|
|
Jacek Slabocewiz
|
|
|
|
Mike Parker(mouse)
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
1999-08-11 17:02:28 +00:00
|
|
|
# include "config.h"
|
1998-09-16 02:39:15 +00:00
|
|
|
#endif
|
|
|
|
|
2000-08-11 22:03:03 +00:00
|
|
|
#include <stdlib.h> /* for atoi and for size_t */
|
2001-07-12 07:06:25 +00:00
|
|
|
#include <string.h>
|
2000-08-11 22:03:03 +00:00
|
|
|
#include <ctype.h>
|
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
#include "snprintf.h"
|
2000-08-11 13:37:21 +00:00
|
|
|
#include "snprintf-imp.h"
|
1998-09-16 02:39:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the nth power of 10
|
|
|
|
*/
|
|
|
|
PRIVATE double
|
|
|
|
pow_10(int n)
|
2002-08-28 21:04:11 +00:00
|
|
|
{
|
1998-09-16 02:39:15 +00:00
|
|
|
int i;
|
|
|
|
double P;
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
for (i = 1, P = 1., n = -n ; i <= n ; i++) {P *= .1;}
|
|
|
|
else
|
|
|
|
for (i = 1, P = 1. ; i <= n ; i++) {P *= 10.0;}
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-08-28 21:04:11 +00:00
|
|
|
* Find the integral part of the log in base 10
|
1998-09-16 02:39:15 +00:00
|
|
|
* Note: this not a real log10()
|
|
|
|
I just need and approximation(integerpart) of x in:
|
|
|
|
10^x ~= r
|
|
|
|
* log_10(200) = 2;
|
|
|
|
* log_10(250) = 2;
|
|
|
|
*/
|
|
|
|
PRIVATE int
|
|
|
|
log_10(double r)
|
2002-08-28 21:04:11 +00:00
|
|
|
{
|
1998-09-16 02:39:15 +00:00
|
|
|
int i = 0;
|
|
|
|
double result = 1.;
|
|
|
|
|
|
|
|
if (r < 0.)
|
|
|
|
r = -r;
|
|
|
|
|
2000-02-09 19:18:42 +00:00
|
|
|
if (r == 0.0)
|
|
|
|
return(0);
|
1998-09-16 02:39:15 +00:00
|
|
|
if (r < 1.) {
|
|
|
|
while (result >= r) {result *= .1; i++;}
|
|
|
|
return (-i);
|
|
|
|
} else {
|
|
|
|
while (result <= r) {result *= 10.; i++;}
|
|
|
|
return (i - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function return the fraction part of a double
|
|
|
|
* and set in ip the integral part.
|
|
|
|
* In many ways it resemble the modf() found on most Un*x
|
|
|
|
*/
|
|
|
|
PRIVATE double
|
|
|
|
integral(double real, double * ip)
|
2002-08-28 21:04:11 +00:00
|
|
|
{
|
1998-09-16 02:39:15 +00:00
|
|
|
int j;
|
|
|
|
double i, s, p;
|
|
|
|
double real_integral = 0.;
|
|
|
|
|
|
|
|
/* take care of the obvious */
|
|
|
|
/* equal to zero ? */
|
|
|
|
if (real == 0.) {
|
|
|
|
*ip = 0.;
|
|
|
|
return (0.);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* negative number ? */
|
|
|
|
if (real < 0.)
|
|
|
|
real = -real;
|
|
|
|
|
|
|
|
/* a fraction ? */
|
|
|
|
if ( real < 1.) {
|
|
|
|
*ip = 0.;
|
|
|
|
return real;
|
|
|
|
}
|
|
|
|
/* the real work :-) */
|
|
|
|
for (j = log_10(real); j >= 0; j--) {
|
|
|
|
p = pow_10(j);
|
|
|
|
s = (real - real_integral)/p;
|
|
|
|
i = 0.;
|
|
|
|
while (i + 1. <= s) {i++;}
|
|
|
|
real_integral += i*p;
|
|
|
|
}
|
|
|
|
*ip = real_integral;
|
|
|
|
return (real - real_integral);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PRECISION 1.e-6
|
2002-08-28 21:04:11 +00:00
|
|
|
/*
|
1998-09-16 02:39:15 +00:00
|
|
|
* return an ascii representation of the integral part of the number
|
|
|
|
* and set fract to be an ascii representation of the fraction part
|
|
|
|
* the container for the fraction and the integral part or staticly
|
2002-08-28 21:04:11 +00:00
|
|
|
* declare with fix size
|
1998-09-16 02:39:15 +00:00
|
|
|
*/
|
|
|
|
PRIVATE char *
|
|
|
|
numtoa(double number, int base, int precision, char ** fract)
|
|
|
|
{
|
|
|
|
register int i, j;
|
|
|
|
double ip, fp; /* integer and fraction part */
|
|
|
|
double fraction;
|
|
|
|
int digits = MAX_INT - 1;
|
|
|
|
static char integral_part[MAX_INT];
|
|
|
|
static char fraction_part[MAX_FRACT];
|
|
|
|
double sign;
|
|
|
|
int ch;
|
|
|
|
|
|
|
|
/* taking care of the obvious case: 0.0 */
|
2002-08-28 21:04:11 +00:00
|
|
|
if (number == 0.) {
|
1998-09-16 02:39:15 +00:00
|
|
|
integral_part[0] = '0';
|
|
|
|
integral_part[1] = '\0';
|
|
|
|
fraction_part[0] = '0';
|
|
|
|
fraction_part[1] = '\0';
|
|
|
|
return integral_part;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for negative numbers */
|
|
|
|
if ((sign = number) < 0.) {
|
|
|
|
number = -number;
|
|
|
|
digits--; /* sign consume one digit */
|
2002-08-28 21:04:11 +00:00
|
|
|
}
|
1998-09-16 02:39:15 +00:00
|
|
|
|
|
|
|
fraction = integral(number, &ip);
|
|
|
|
number = ip;
|
|
|
|
/* do the integral part */
|
|
|
|
if ( ip == 0.) {
|
|
|
|
integral_part[0] = '0';
|
|
|
|
i = 1;
|
|
|
|
} else {
|
|
|
|
for ( i = 0; i < digits && number != 0.; ++i) {
|
|
|
|
number /= base;
|
|
|
|
fp = integral(number, &ip);
|
|
|
|
ch = (int)((fp + PRECISION)*base); /* force to round */
|
|
|
|
integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
|
2000-08-07 23:05:43 +00:00
|
|
|
if (! isxdigit((unsigned char)integral_part[i])) /* bail out overflow !! */
|
2002-08-28 21:04:11 +00:00
|
|
|
break;
|
1998-09-16 02:39:15 +00:00
|
|
|
number = ip;
|
|
|
|
}
|
|
|
|
}
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
/* Oh No !! out of bound, ho well fill it up ! */
|
|
|
|
if (number != 0.)
|
|
|
|
for (i = 0; i < digits; ++i)
|
|
|
|
integral_part[i] = '9';
|
|
|
|
|
|
|
|
/* put the sign ? */
|
|
|
|
if (sign < 0.)
|
|
|
|
integral_part[i++] = '-';
|
|
|
|
|
|
|
|
integral_part[i] = '\0';
|
|
|
|
|
|
|
|
/* reverse every thing */
|
|
|
|
for ( i--, j = 0; j < i; j++, i--)
|
2002-08-28 21:04:11 +00:00
|
|
|
SWAP_INT(integral_part[i], integral_part[j]);
|
1998-09-16 02:39:15 +00:00
|
|
|
|
|
|
|
/* the fractionnal part */
|
|
|
|
for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
|
|
|
|
fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
|
2000-08-07 23:05:43 +00:00
|
|
|
if (! isdigit((unsigned char)fraction_part[i])) /* underflow ? */
|
1998-09-16 02:39:15 +00:00
|
|
|
break;
|
|
|
|
fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
|
|
|
|
}
|
|
|
|
fraction_part[i] = '\0';
|
|
|
|
|
|
|
|
if (fract != (char **)0)
|
|
|
|
*fract = fraction_part;
|
|
|
|
|
|
|
|
return integral_part;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for %d and friends, it puts in holder
|
|
|
|
* the representation with the right padding
|
|
|
|
*/
|
|
|
|
PRIVATE void
|
|
|
|
decimal(struct DATA *p, double d)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
tmp = itoa(d);
|
|
|
|
p->width -= strlen(tmp);
|
|
|
|
PAD_RIGHT(p);
|
|
|
|
PUT_PLUS(d, p);
|
|
|
|
PUT_SPACE(d, p);
|
|
|
|
while (*tmp) { /* the integral */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
PAD_LEFT(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for %o octal representation */
|
|
|
|
PRIVATE void
|
|
|
|
octal(struct DATA *p, double d)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
tmp = otoa(d);
|
|
|
|
p->width -= strlen(tmp);
|
|
|
|
if (p->square == FOUND) /* had prefix '0' for octal */
|
|
|
|
PUT_CHAR('0', p);
|
1999-12-08 21:44:33 +00:00
|
|
|
PAD_RIGHT(p);
|
1998-09-16 02:39:15 +00:00
|
|
|
while (*tmp) { /* octal */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
PAD_LEFT(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for %x %X hexadecimal representation */
|
|
|
|
PRIVATE void
|
|
|
|
hexa(struct DATA *p, double d)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
tmp = htoa(d);
|
|
|
|
p->width -= strlen(tmp);
|
|
|
|
if (p->square == FOUND) { /* prefix '0x' for hexa */
|
|
|
|
PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
|
|
|
|
}
|
1999-12-08 21:44:33 +00:00
|
|
|
PAD_RIGHT(p);
|
1998-09-16 02:39:15 +00:00
|
|
|
while (*tmp) { /* hexa */
|
|
|
|
PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
PAD_LEFT(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* %s strings */
|
|
|
|
PRIVATE void
|
|
|
|
strings(struct DATA *p, char *tmp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = strlen(tmp);
|
|
|
|
if (p->precision != NOT_FOUND) /* the smallest number */
|
|
|
|
i = (i < p->precision ? i : p->precision);
|
|
|
|
p->width -= i;
|
|
|
|
PAD_RIGHT(p);
|
|
|
|
while (i-- > 0) { /* put the sting */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
PAD_LEFT(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* %f or %g floating point representation */
|
|
|
|
PRIVATE void
|
|
|
|
floating(struct DATA *p, double d)
|
|
|
|
{
|
|
|
|
char *tmp, *tmp2;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DEF_PREC(p);
|
|
|
|
d = ROUND(d, p);
|
|
|
|
tmp = dtoa(d, p->precision, &tmp2);
|
|
|
|
/* calculate the padding. 1 for the dot */
|
|
|
|
p->width = p->width -
|
|
|
|
((d > 0. && p->justify == RIGHT) ? 1:0) -
|
|
|
|
((p->space == FOUND) ? 1:0) -
|
|
|
|
strlen(tmp) - p->precision - 1;
|
2002-08-28 21:04:11 +00:00
|
|
|
PAD_RIGHT(p);
|
1998-09-16 02:39:15 +00:00
|
|
|
PUT_PLUS(d, p);
|
|
|
|
PUT_SPACE(d, p);
|
|
|
|
while (*tmp) { /* the integral */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
if (p->precision != 0 || p->square == FOUND)
|
|
|
|
PUT_CHAR('.', p); /* put the '.' */
|
|
|
|
if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
|
|
|
|
for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
|
2002-08-28 21:04:11 +00:00
|
|
|
tmp2[i] = '\0';
|
1998-09-16 02:39:15 +00:00
|
|
|
for (; *tmp2; tmp2++)
|
|
|
|
PUT_CHAR(*tmp2, p); /* the fraction */
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
PAD_LEFT(p);
|
2002-08-28 21:04:11 +00:00
|
|
|
}
|
1998-09-16 02:39:15 +00:00
|
|
|
|
|
|
|
/* %e %E %g exponent representation */
|
|
|
|
PRIVATE void
|
|
|
|
exponent(struct DATA *p, double d)
|
|
|
|
{
|
|
|
|
char *tmp, *tmp2;
|
|
|
|
int j, i;
|
|
|
|
|
|
|
|
DEF_PREC(p);
|
|
|
|
j = log_10(d);
|
|
|
|
d = d / pow_10(j); /* get the Mantissa */
|
2002-08-28 21:04:11 +00:00
|
|
|
d = ROUND(d, p);
|
1998-09-16 02:39:15 +00:00
|
|
|
tmp = dtoa(d, p->precision, &tmp2);
|
|
|
|
/* 1 for unit, 1 for the '.', 1 for 'e|E',
|
|
|
|
* 1 for '+|-', 3 for 'exp' */
|
|
|
|
/* calculate how much padding need */
|
2002-08-28 21:04:11 +00:00
|
|
|
p->width = p->width -
|
1998-09-16 02:39:15 +00:00
|
|
|
((d > 0. && p->justify == RIGHT) ? 1:0) -
|
|
|
|
((p->space == FOUND) ? 1:0) - p->precision - 7;
|
|
|
|
PAD_RIGHT(p);
|
|
|
|
PUT_PLUS(d, p);
|
|
|
|
PUT_SPACE(d, p);
|
|
|
|
while (*tmp) {/* the integral */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
if (p->precision != 0 || p->square == FOUND)
|
|
|
|
PUT_CHAR('.', p); /* the '.' */
|
|
|
|
if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
|
|
|
|
for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
|
2002-08-28 21:04:11 +00:00
|
|
|
tmp2[i] = '\0';
|
1998-09-16 02:39:15 +00:00
|
|
|
for (; *tmp2; tmp2++)
|
|
|
|
PUT_CHAR(*tmp2, p); /* the fraction */
|
|
|
|
|
|
|
|
if (*p->pf == 'g' || *p->pf == 'e') { /* the exponent put the 'e|E' */
|
|
|
|
PUT_CHAR('e', p);
|
|
|
|
} else
|
|
|
|
PUT_CHAR('E', p);
|
|
|
|
if (j > 0) { /* the sign of the exp */
|
|
|
|
PUT_CHAR('+', p);
|
|
|
|
} else {
|
|
|
|
PUT_CHAR('-', p);
|
|
|
|
j = -j;
|
|
|
|
}
|
|
|
|
tmp = itoa((double)j);
|
|
|
|
if (j < 9) { /* need to pad the exponent with 0 '000' */
|
|
|
|
PUT_CHAR('0', p); PUT_CHAR('0', p);
|
|
|
|
} else if (j < 99)
|
|
|
|
PUT_CHAR('0', p);
|
|
|
|
while (*tmp) { /* the exponent */
|
|
|
|
PUT_CHAR(*tmp, p);
|
|
|
|
tmp++;
|
|
|
|
}
|
|
|
|
PAD_LEFT(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* initialize the conversion specifiers */
|
|
|
|
PRIVATE void
|
|
|
|
conv_flag(char * s, struct DATA * p)
|
|
|
|
{
|
|
|
|
char number[MAX_FIELD/2];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
p->precision = p->width = NOT_FOUND;
|
|
|
|
p->star_w = p->star_p = NOT_FOUND;
|
|
|
|
p->square = p->space = NOT_FOUND;
|
|
|
|
p->a_long = p->justify = NOT_FOUND;
|
|
|
|
p->pad = ' ';
|
|
|
|
|
|
|
|
for(;s && *s ;s++) {
|
|
|
|
switch(*s) {
|
|
|
|
case ' ': p->space = FOUND; break;
|
|
|
|
case '#': p->square = FOUND; break;
|
|
|
|
case '*': if (p->width == NOT_FOUND)
|
|
|
|
p->width = p->star_w = FOUND;
|
|
|
|
else
|
|
|
|
p->precision = p->star_p = FOUND;
|
|
|
|
break;
|
|
|
|
case '+': p->justify = RIGHT; break;
|
|
|
|
case '-': p->justify = LEFT; break;
|
|
|
|
case '.': if (p->width == NOT_FOUND)
|
|
|
|
p->width = 0;
|
|
|
|
break;
|
|
|
|
case '0': p->pad = '0'; break;
|
|
|
|
case '1': case '2': case '3':
|
|
|
|
case '4': case '5': case '6':
|
|
|
|
case '7': case '8': case '9': /* gob all the digits */
|
2002-08-28 21:04:11 +00:00
|
|
|
for (i = 0; isdigit((unsigned char)*s); i++, s++)
|
1998-09-16 02:39:15 +00:00
|
|
|
if (i < MAX_FIELD/2 - 1)
|
|
|
|
number[i] = *s;
|
|
|
|
number[i] = '\0';
|
|
|
|
if (p->width == NOT_FOUND)
|
|
|
|
p->width = atoi(number);
|
|
|
|
else
|
|
|
|
p->precision = atoi(number);
|
|
|
|
s--; /* went to far go back */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC int
|
|
|
|
vsnprintf(char *string, size_t length, const char * format, va_list args)
|
|
|
|
{
|
|
|
|
struct DATA data;
|
|
|
|
char conv_field[MAX_FIELD];
|
|
|
|
double d; /* temporary holder */
|
|
|
|
int state;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
data.length = length - 1; /* leave room for '\0' */
|
|
|
|
data.holder = string;
|
|
|
|
data.pf = format;
|
|
|
|
data.counter = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* sanity check, the string must be > 1 */
|
|
|
|
if (length < 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
for (; *data.pf && (data.counter < data.length); data.pf++) {
|
|
|
|
if ( *data.pf == '%' ) { /* we got a magic % cookie */
|
|
|
|
conv_flag((char *)0, &data); /* initialise format flags */
|
|
|
|
for (state = 1; *data.pf && state;) {
|
|
|
|
switch (*(++data.pf)) {
|
|
|
|
case '\0': /* a NULL here ? ? bail out */
|
|
|
|
*data.holder = '\0';
|
|
|
|
return data.counter;
|
|
|
|
break;
|
|
|
|
case 'f': /* float, double */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
d = va_arg(args, double);
|
2002-08-28 21:04:11 +00:00
|
|
|
floating(&data, d);
|
1998-09-16 02:39:15 +00:00
|
|
|
state = 0;
|
|
|
|
break;
|
2002-08-28 21:04:11 +00:00
|
|
|
case 'g':
|
1998-09-16 02:39:15 +00:00
|
|
|
case 'G':
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
DEF_PREC(&data);
|
|
|
|
d = va_arg(args, double);
|
|
|
|
i = log_10(d);
|
|
|
|
/*
|
|
|
|
* for '%g|%G' ANSI: use f if exponent
|
|
|
|
* is in the range or [-4,p] exclusively
|
|
|
|
* else use %e|%E
|
|
|
|
*/
|
|
|
|
if (-4 < i && i < data.precision)
|
|
|
|
floating(&data, d);
|
|
|
|
else
|
|
|
|
exponent(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 'e':
|
|
|
|
case 'E': /* Exponent double */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
d = va_arg(args, double);
|
|
|
|
exponent(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
1998-11-08 00:05:45 +00:00
|
|
|
case 'u': /* unsigned decimal */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
if (data.a_long == FOUND)
|
|
|
|
d = va_arg(args, unsigned long);
|
|
|
|
else
|
|
|
|
d = va_arg(args, unsigned int);
|
|
|
|
decimal(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
1998-09-16 02:39:15 +00:00
|
|
|
case 'd': /* decimal */
|
1999-02-15 06:36:19 +00:00
|
|
|
case 'i': /* "integer" (signed decimal) */
|
1998-09-16 02:39:15 +00:00
|
|
|
STAR_ARGS(&data);
|
|
|
|
if (data.a_long == FOUND)
|
|
|
|
d = va_arg(args, long);
|
|
|
|
else
|
|
|
|
d = va_arg(args, int);
|
|
|
|
decimal(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 'o': /* octal */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
if (data.a_long == FOUND)
|
1999-01-28 21:29:36 +00:00
|
|
|
d = va_arg(args, unsigned long);
|
1998-09-16 02:39:15 +00:00
|
|
|
else
|
1999-01-28 21:29:36 +00:00
|
|
|
d = va_arg(args, unsigned int);
|
1998-09-16 02:39:15 +00:00
|
|
|
octal(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
2002-08-28 21:04:11 +00:00
|
|
|
case 'x':
|
1998-09-16 02:39:15 +00:00
|
|
|
case 'X': /* hexadecimal */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
if (data.a_long == FOUND)
|
1999-01-28 21:29:36 +00:00
|
|
|
d = va_arg(args, unsigned long);
|
1998-09-16 02:39:15 +00:00
|
|
|
else
|
1999-01-28 21:29:36 +00:00
|
|
|
d = va_arg(args, unsigned int);
|
1998-09-16 02:39:15 +00:00
|
|
|
hexa(&data, d);
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 'c': /* character */
|
|
|
|
d = va_arg(args, int);
|
|
|
|
PUT_CHAR(d, &data);
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 's': /* string */
|
|
|
|
STAR_ARGS(&data);
|
|
|
|
strings(&data, va_arg(args, char *));
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
*(va_arg(args, int *)) = data.counter; /* what's the count ? */
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
data.a_long = FOUND;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
break;
|
|
|
|
case '%': /* nothing just % */
|
|
|
|
PUT_CHAR('%', &data);
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
case '#': case ' ': case '+': case '*':
|
2002-08-28 21:04:11 +00:00
|
|
|
case '-': case '.': case '0': case '1':
|
1998-09-16 02:39:15 +00:00
|
|
|
case '2': case '3': case '4': case '5':
|
|
|
|
case '6': case '7': case '8': case '9':
|
|
|
|
/* initialize width and precision */
|
2002-08-28 21:04:11 +00:00
|
|
|
for (i = 0; isflag((unsigned char)*data.pf); i++, data.pf++)
|
1998-09-16 02:39:15 +00:00
|
|
|
if (i < MAX_FIELD - 1)
|
|
|
|
conv_field[i] = *data.pf;
|
|
|
|
conv_field[i] = '\0';
|
|
|
|
conv_flag(conv_field, &data);
|
|
|
|
data.pf--; /* went to far go back */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* is this an error ? maybe bail out */
|
|
|
|
state = 0;
|
|
|
|
break;
|
|
|
|
} /* end switch */
|
|
|
|
} /* end of for state */
|
|
|
|
} else { /* not % */
|
|
|
|
PUT_CHAR(*data.pf, &data); /* add the char the string */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*data.holder = '\0'; /* the end ye ! */
|
|
|
|
|
|
|
|
return data.counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef HAVE_SNPRINTF
|
|
|
|
|
|
|
|
PUBLIC int
|
|
|
|
snprintf(char *string, size_t length, const char * format, ...)
|
|
|
|
{
|
|
|
|
int rval;
|
|
|
|
va_list args;
|
|
|
|
|
Some compilers, e.g. Microsoft Visual C++, don't define __STDC__ unless
extensions to ANSI C are disabled if they may cause strictly conforming
programs not to compile, or to work differently if those extensions are
enabled. (Other compilers #define it as 0, e.g. Sun's and, I think,
other AT&T-derived compilers; still others cheerfully define it as 1
even when those extensions are enabled, e.g. GCC.)
As such, checking whether __STDC__ is defined, or is defined as a
non-zero value, isn't the right way to check whether function prototypes
are supported; MSVC++ 6.0 supports them, but, by default, leaves
extensions of the sort described above enabled, and thus doesn't define
__STDC__. This means that the compiler warns about arguments to
"snprintf()" when compiling it, as the declaration is an old-style
declaration.
As Ethereal uses function prototypes, there's not much point in making
it possible for its private "snprintf()" to be compiled or used when
function prototypes aren't supported; just get rid of the tests for
__STDC__, so that it's compiled with function prototypes regardless of
whether __STDC__ is defined or not.
While we're at it, have "snprintf()" give it a "__attribute__((format
(printf, 3, 4))))" when compiled by GCC 2.x or later, so that
format/argument checks can be done even on platforms lacking
"snprintf()".
svn path=/trunk/; revision=2689
2000-11-21 21:24:52 +00:00
|
|
|
#if defined(HAVE_STDARG_H)
|
1998-09-16 02:39:15 +00:00
|
|
|
va_start(args, format);
|
|
|
|
#else
|
|
|
|
va_start(args);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rval = vsnprintf (string, length, format, args);
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_SNPRINTF */
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DRIVER
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
/* set of small tests for snprintf() */
|
|
|
|
void main()
|
|
|
|
{
|
|
|
|
char holder[100];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
printf("Suite of test for snprintf:\n");
|
|
|
|
printf("a_format\n");
|
|
|
|
printf("printf() format\n");
|
|
|
|
printf("snprintf() format\n\n");
|
|
|
|
*/
|
|
|
|
/* Checking the field widths */
|
|
|
|
|
|
|
|
printf("/%%d/, 336\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%d/\n", 336);
|
|
|
|
printf("/%d/\n", 336);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%2d/, 336\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%2d/\n", 336);
|
|
|
|
printf("/%2d/\n", 336);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%10d/, 336\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%10d/\n", 336);
|
|
|
|
printf("/%10d/\n", 336);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%-10d/, 336\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%-10d/\n", 336);
|
|
|
|
printf("/%-10d/\n", 336);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
|
|
|
|
/* floating points */
|
|
|
|
|
|
|
|
printf("/%%f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
|
|
|
|
printf("/%f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%e/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
|
|
|
|
printf("/%e/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%4.2f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
|
|
|
|
printf("/%4.2f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%3.1f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
|
|
|
|
printf("/%3.1f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%10.3f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
|
|
|
|
printf("/%10.3f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%10.3e/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
|
|
|
|
printf("/%10.3e/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%+4.2f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
|
|
|
|
printf("/%+4.2f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%010.2f/, 1234.56\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
|
|
|
|
printf("/%010.2f/\n", 1234.56);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
#define BLURB "Outstanding acting !"
|
|
|
|
/* strings precisions */
|
|
|
|
|
|
|
|
printf("/%%2s/, \"%s\"\n", BLURB);
|
|
|
|
snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
|
|
|
|
printf("/%2s/\n", BLURB);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%22s/ %s\n", BLURB);
|
|
|
|
snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
|
|
|
|
printf("/%22s/\n", BLURB);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%22.5s/ %s\n", BLURB);
|
|
|
|
snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
|
|
|
|
printf("/%22.5s/\n", BLURB);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%-22.5s/ %s\n", BLURB);
|
|
|
|
snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
|
|
|
|
printf("/%-22.5s/\n", BLURB);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
/* see some flags */
|
|
|
|
|
|
|
|
printf("%%x %%X %%#x, 31, 31, 31\n");
|
|
|
|
snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
|
|
|
|
printf("%x %X %#x\n", 31, 31, 31);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("**%%d**%% d**%% d**, 42, 42, -42\n");
|
|
|
|
snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
|
|
|
|
printf("**%d**% d**% d**\n", 42, 42, -42);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
/* other flags */
|
|
|
|
|
|
|
|
printf("/%%g/, 31.4\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%g/\n", 31.4);
|
|
|
|
printf("/%g/\n", 31.4);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%.6g/, 31.4\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
|
|
|
|
printf("/%.6g/\n", 31.4);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("/%%.1G/, 31.4\n");
|
|
|
|
snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
|
|
|
|
printf("/%.1G/\n", 31.4);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("abc%%n\n");
|
|
|
|
printf("abc%n", &i); printf("%d\n", i);
|
|
|
|
snprintf(holder, sizeof holder, "abc%n", &i);
|
|
|
|
printf("%s", holder); printf("%d\n\n", i);
|
2002-08-28 21:04:11 +00:00
|
|
|
|
1998-09-16 02:39:15 +00:00
|
|
|
printf("%%*.*s --> 10.10\n");
|
|
|
|
snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
|
|
|
|
printf("%*.*s\n", 10, 10, BLURB);
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
printf("%%%%%%%%\n");
|
|
|
|
snprintf(holder, sizeof holder, "%%%%\n");
|
|
|
|
printf("%%%%\n");
|
|
|
|
printf("%s\n", holder);
|
|
|
|
|
|
|
|
#define BIG "Hello this is a too big string for the buffer"
|
|
|
|
/* printf("A buffer to small of 10, trying to put this:\n");*/
|
2002-08-28 21:04:11 +00:00
|
|
|
printf("<%%>, %s\n", BIG);
|
1998-09-16 02:39:15 +00:00
|
|
|
i = snprintf(holder, 10, "%s\n", BIG);
|
|
|
|
printf("<%s>\n", BIG);
|
|
|
|
printf("<%s>\n", holder);
|
|
|
|
}
|
|
|
|
#endif
|