/*
Unix snprintf implementation.
Version 1.4
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Revision History:
1.4:
* integrate in FreeRADIUS's libradius:
* Fetched from: http://savannah.gnu.org/cgi-bin/viewcvs/mailutils/mailutils/lib/snprintf.c?rev=1.4
* Fetched from: http://savannah.gnu.org/cgi-bin/viewcvs/mailutils/mailutils/lib/snprintf.h?rev=1.4
* Replace config.h with autoconf.h
* Protect with HAVE_SNPRINTF and HAVE_VSNPRINTF
1.3:
* add #include <config.h> ifdef HAVE_CONFIG_H
* cosmetic change, when exponent is 0 print xxxE+00
instead of xxxE-00
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)
*/
#include "autoconf.h"
#include "snprintf.h"
#ifndef HAVE_VSNPRINTF
/*
* Find the nth power of 10
*/
PRIVATE double
#ifdef __STDC__
pow_10(int n)
#else
pow_10(n)
int n;
#endif
{
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;
}
/*
* Find the integral part of the log in base 10
* 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
#ifdef __STDC__
log_10(double r)
#else
log_10(r)
double r;
#endif
{
int i = 0;
double result = 1.;
if (r < 0.)
r = -r;
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
#ifdef __STDC__
integral(double real, double * ip)
#else
integral(real, ip)
double real;
double * ip;
#endif
{
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
/*
* 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
* declare with fix size
*/
PRIVATE char *
#ifdef __STDC__
numtoa(double number, int base, int precision, char ** fract)
#else
numtoa(number, base, precision, fract)
double number;
int base;
int precision;
char ** fract;
#endif
{
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 */
if (number == 0.) {
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 */
}
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;
if (! isxdigit(integral_part[i])) /* bail out overflow !! */
break;
number = ip;
}
}
/* 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--)
SWAP_INT(integral_part[i], integral_part[j]);
/* the fractionnal part */
for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision-- ) {
fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
if (! isdigit(fraction_part[i])) /* underflow ? */
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
#ifdef __STDC__
decimal(struct DATA *p, double d)
#else
decimal(p, d)
struct DATA *p;
double d;
#endif
{
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
#ifdef __STDC__
octal(struct DATA *p, double d)
#else
octal(p, d)
struct DATA *p;
double d;
#endif
{
char *tmp;
tmp = otoa(d);
p->width -= strlen(tmp);
PAD_RIGHT(p);
if (p->square == FOUND) /* had prefix '0' for octal */
PUT_CHAR('0', p);
while (*tmp) { /* octal */
PUT_CHAR(*tmp, p);
tmp++;
}
PAD_LEFT(p);
}
/* for %x %X hexadecimal representation */
PRIVATE void
#ifdef __STDC__
hexa(struct DATA *p, double d)
#else
hexa(p, d)
struct DATA *p;
double d;
#endif
{
char *tmp;
tmp = htoa(d);
p->width -= strlen(tmp);
PAD_RIGHT(p);
if (p->square == FOUND) { /* prefix '0x' for hexa */
PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
}
while (*tmp) { /* hexa */
PUT_CHAR((*p->pf == 'X' ? toupper(*tmp) : *tmp), p);
tmp++;
}
PAD_LEFT(p);
}
/* %s strings */
PRIVATE void
#ifdef __STDC__
strings(struct DATA *p, char *tmp)
#else
strings(p, tmp)
struct DATA *p;
char *tmp;
#endif
{
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
#ifdef __STDC__
floating(struct DATA *p, double d)
#else
floating(p, d)
struct DATA *p;
double d;
#endif
{
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;
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); /* put the '.' */
if (*p->pf == 'g' || *p->pf == 'G') /* smash the trailing zeros */
for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
tmp2[i] = '\0';
for (; *tmp2; tmp2++)
PUT_CHAR(*tmp2, p); /* the fraction */
PAD_LEFT(p);
}
/* %e %E %g exponent representation */
PRIVATE void
#ifdef __STDC__
exponent(struct DATA *p, double d)
#else
exponent(p, d)
struct DATA *p;
double d;
#endif
{
char *tmp, *tmp2;
int j, i;
DEF_PREC(p);
j = log_10(d);
d = d / pow_10(j); /* get the Mantissa */
d = ROUND(d, p);
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 */
p->width = p->width -
((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--)
tmp2[i] = '\0';
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
#ifdef __STDC__
conv_flag(char * s, struct DATA * p)
#else
conv_flag(s, p)
char * s;
struct DATA * p;
#endif
{
char number[MAX_FIELD/2];
int i;
/* reset the flags. */
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->a_longlong = 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 */
for (i = 0; isdigit(*s); i++, s++)
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
#ifdef __STDC__
vsnprintf(char *string, size_t length, const char * format, va_list args)
#else
vsnprintf(string, length, format, args)
char *string;
size_t length;
char * format;
va_list args;
#endif
{
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);
if (data.a_long == FOUND)
d = va_arg(args, LONG_DOUBLE);
else
d = va_arg(args, double);
floating(&data, d);
state = 0;
break;
case 'g':
case 'G':
STAR_ARGS(&data);
DEF_PREC(&data);
if (data.a_long == FOUND)
d = va_arg(args, LONG_DOUBLE);
else
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);
if (data.a_long == FOUND)
d = va_arg(args, LONG_DOUBLE);
else
d = va_arg(args, double);
exponent(&data, d);
state = 0;
break;
case 'u': /* unsigned decimal */
STAR_ARGS(&data);
if (data.a_longlong == FOUND)
d = va_arg(args, unsigned LONG_LONG);
else if (data.a_long == FOUND)
d = va_arg(args, unsigned long);
else
d = va_arg(args, unsigned int);
decimal(&data, d);
state = 0;
break;
case 'd': /* decimal */
STAR_ARGS(&data);
if (data.a_longlong == FOUND)
d = va_arg(args, LONG_LONG);
else 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_longlong == FOUND)
d = va_arg(args, LONG_LONG);
else if (data.a_long == FOUND)
d = va_arg(args, long);
else
d = va_arg(args, int);
octal(&data, d);
state = 0;
break;
case 'x':
case 'X': /* hexadecimal */
STAR_ARGS(&data);
if (data.a_longlong == FOUND)
d = va_arg(args, LONG_LONG);
else if (data.a_long == FOUND)
d = va_arg(args, long);
else
d = va_arg(args, int);
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 'q':
data.a_longlong = FOUND;
break;
case 'L':
case 'l':
if (data.a_long == FOUND)
data.a_longlong = FOUND;
else
data.a_long = FOUND;
break;
case 'h':
break;
case '%': /* nothing just % */
PUT_CHAR('%', &data);
state = 0;
break;
case '#': case ' ': case '+': case '*':
case '-': case '.': case '0': case '1':
case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
/* initialize width and precision */
for (i = 0; isflag(*data.pf); i++, data.pf++)
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;
}
#endif /* HAVE_VSNPRINTF */
#ifndef HAVE_SNPRINTF
PUBLIC int
#if __STDC__
snprintf(char *string, size_t length, const char * format, ...)
#else
snprintf(string, length, format, va_alist)
char *string;
size_t length;
char * format;
va_dcl
#endif
{
int rval;
va_list args;
#if __STDC__
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() */
int 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);
/* long long */
printf("/%%lld/, 336\n");
snprintf(holder, sizeof holder, "/%lld/\n", (LONG_LONG)336);
printf("/%lld/\n", (LONG_LONG)336);
printf("%s\n", holder);
printf("/%%2qd/, 336\n");
snprintf(holder, sizeof holder, "/%2qd/\n", (LONG_LONG)336);
printf("/%2qd/\n", (LONG_LONG)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);
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");*/
printf("<%%>, %s\n", BIG);
i = snprintf(holder, 10, "%s\n", BIG);
printf("<%s>\n", BIG);
printf("<%s>\n", holder);
return 0;
}
#endif /* !DRIVER */
syntax highlighted by Code2HTML, v. 0.9.1