wireshark/snprintf.c

833 lines
20 KiB
C

/*
Unix snprintf implementation.
Version 1.2
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
Public Lincense.
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.
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.
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
# include "config.h"
#endif
#include "snprintf-imp.h"
#include "snprintf.h"
/*
* 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);
if (p->square == FOUND) /* had prefix '0' for octal */
PUT_CHAR('0', p);
PAD_RIGHT(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);
if (p->square == FOUND) { /* prefix '0x' for hexa */
PUT_CHAR('0', p); PUT_CHAR(*p->pf, p);
}
PAD_RIGHT(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;
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 */
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);
d = va_arg(args, double);
floating(&data, d);
state = 0;
break;
case 'g':
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;
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;
case 'd': /* decimal */
case 'i': /* "integer" (signed decimal) */
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)
d = va_arg(args, unsigned long);
else
d = va_arg(args, unsigned int);
octal(&data, d);
state = 0;
break;
case 'x':
case 'X': /* hexadecimal */
STAR_ARGS(&data);
if (data.a_long == FOUND)
d = va_arg(args, unsigned long);
else
d = va_arg(args, unsigned 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 'l':
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;
}
#ifndef HAVE_SNPRINTF
PUBLIC int
#if defined(HAVE_STDARG_H) && defined(__STDC__) && __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 defined(HAVE_STDARG_H) && defined(__STDC__) && __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() */
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);
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);
}
#endif