bsd-snprintf.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/**************************************************************
* Original:
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
* A bombproof version of doprnt (dopr) included.
* Sigh. This sort of thing is always nasty do deal with. Note that
* the version here does not include floating point...
*
* snprintf() is used instead of sprintf() as it does limit checks
* for string length. This covers a nasty loophole.
*
* The other functions are there to prevent NULL pointers from
* causing nast effects.
*
* More Recently:
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
* This was ugly. It is still ugly. I opted out of floating point
* numbers, but the formatter understands just about everything
* from the normal C string format, at least as far as I can tell from
* the Solaris 2.5 printf(3S) man page.
*
* Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
* Ok, added some minimal floating point support, which means this
* probably requires libm on most operating systems. Don't yet
* support the exponent (e,E) and sigfig (g,G). Also, fmtint()
* was pretty badly broken, it just wasn't being exercised in ways
* which showed it, so that's been fixed. Also, formated the code
* to mutt conventions, and removed dead code left over from the
* original. Also, there is now a builtin-test, just compile with:
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
* and run snprintf for results.
*
* Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
* The PGP code was using unsigned hexadecimal formats.
* Unfortunately, unsigned formats simply didn't work.
*
* Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
* The original code assumed that both snprintf() and vsnprintf() were
* missing. Some systems only have snprintf() but not vsnprintf(), so
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
*
* Ben Lindstrom <mouring@eviladmin.org> 09/27/00 for OpenSSH
* Welcome to the world of %lld and %qd support. With other
* long long support. This is needed for sftp-server to work
* right.
*
* Ben Lindstrom <mouring@eviladmin.org> 02/12/01 for OpenSSH
* Removed all hint of VARARGS stuff and banished it to the void,
* and did a bit of KNF style work to make things a bit more
* acceptable. Consider stealing from mutt or enlightenment.
**************************************************************/
#include "includes.h"
RCSID("$Id: bsd-snprintf.c,v 1.5 2001/02/25 23:20:41 mouring Exp $");
#pragma ident "%Z%%M% %I% %E% SMI"
#if defined(BROKEN_SNPRINTF) /* For those with broken snprintf() */
#endif
#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
static void
static void
static void
static void
static void
/*
* dopr(): poor man's version of doprintf
*/
/* format read states */
#define DP_S_DEFAULT 0
#define DP_S_FLAGS 1
#define DP_S_MIN 2
#define DP_S_DOT 3
#define DP_S_MAX 4
#define DP_S_MOD 5
#define DP_S_CONV 6
#define DP_S_DONE 7
/* format flags - Bits */
#define DP_F_MINUS (1 << 0)
/* Conversion Flags */
#define DP_C_SHORT 1
#define DP_C_LONG 2
#define DP_C_LDOUBLE 3
#define DP_C_LONG_LONG 4
#define char_to_int(p) (p - '0')
#define abs_val(p) (p < 0 ? -p : p)
static void
{
char *strvalue;
char ch;
long value;
long double fvalue;
int min = 0;
int max = -1;
int state = DP_S_DEFAULT;
int flags = 0;
int cflags = 0;
switch(state) {
case DP_S_DEFAULT:
if (ch == '%')
state = DP_S_FLAGS;
else
break;
case DP_S_FLAGS:
switch (ch) {
case '-':
flags |= DP_F_MINUS;
break;
case '+':
break;
case ' ':
flags |= DP_F_SPACE;
break;
case '#':
break;
case '0':
break;
default:
break;
}
break;
case DP_S_MIN:
} else if (ch == '*') {
} else
break;
case DP_S_DOT:
if (ch == '.') {
} else
break;
case DP_S_MAX:
if (max < 0)
max = 0;
} else if (ch == '*') {
} else
break;
case DP_S_MOD:
switch (ch) {
case 'h':
cflags = DP_C_SHORT;
break;
case 'l':
if (ch == 'l') {
}
break;
case 'q':
break;
case 'L':
break;
default:
break;
}
break;
case DP_S_CONV:
switch (ch) {
case 'd':
case 'i':
if (cflags == DP_C_SHORT)
else if (cflags == DP_C_LONG_LONG)
else
break;
case 'o':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
else if (cflags == DP_C_LONG_LONG)
else
break;
case 'u':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
else if (cflags == DP_C_LONG_LONG)
else
break;
case 'X':
case 'x':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
else if (cflags == DP_C_LONG_LONG)
else
break;
case 'f':
if (cflags == DP_C_LDOUBLE)
else
/* um, floating point? */
break;
case 'E':
case 'e':
if (cflags == DP_C_LDOUBLE)
else
break;
case 'G':
case 'g':
if (cflags == DP_C_LDOUBLE)
else
break;
case 'c':
break;
case 's':
if (max < 0)
break;
case 'p':
break;
case 'n':
if (cflags == DP_C_SHORT) {
short int *num;
long int *num;
} else if (cflags == DP_C_LONG_LONG) {
long long *num;
} else {
int *num;
}
break;
case '%':
break;
case 'w': /* not supported yet, treat as next char */
break;
default: /* Unknown, skip */
break;
}
max = -1;
break;
case DP_S_DONE:
break;
default: /* hmm? */
break; /* some picky compilers need this */
}
}
else
}
static void
{
int cnt = 0;
if (value == 0)
value = "<NULL>";
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
--padlen;
++cnt;
}
++cnt;
}
++padlen;
++cnt;
}
}
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
static void
{
unsigned long uvalue;
char convert[20];
int signvalue = 0;
int place = 0;
int spadlen = 0; /* amount to space pad */
int zpadlen = 0; /* amount to zero pad */
int caps = 0;
if (max < 0)
max = 0;
if (!(flags & DP_F_UNSIGNED)) {
if (value < 0) {
signvalue = '-';
signvalue = '+';
else if (flags & DP_F_SPACE)
signvalue = ' ';
}
do {
if (place == 20)
place--;
if (zpadlen < 0)
zpadlen = 0;
if (spadlen < 0)
spadlen = 0;
spadlen = 0;
}
if (flags & DP_F_MINUS)
/* Spaces */
while (spadlen > 0) {
--spadlen;
}
/* Sign */
if (signvalue)
/* Zeros */
if (zpadlen > 0) {
while (zpadlen > 0) {
--zpadlen;
}
}
/* Digits */
while (place > 0)
/* Left Justified spaces */
while (spadlen < 0) {
++spadlen;
}
}
static long double
{
long double result = 1;
while (exp) {
result *= 10;
exp--;
}
return result;
}
static long
{
if (value >= 0.5)
intpart++;
return intpart;
}
static void
{
char iconvert[20];
char fconvert[20];
int signvalue = 0;
int iplace = 0;
int fplace = 0;
int padlen = 0; /* amount to pad */
int zpadlen = 0;
int caps = 0;
long intpart;
long fracpart;
long double ufvalue;
/*
* AIX manpage says the default is 0, but Solaris says the default
* is 6, and sprintf on AIX defaults to 6
*/
if (max < 0)
max = 6;
if (fvalue < 0)
signvalue = '-';
signvalue = '+';
else if (flags & DP_F_SPACE)
signvalue = ' ';
/*
* Sorry, we only support 9 digits past the decimal because of our
* conversion method
*/
if (max > 9)
max = 9;
/* We "cheat" by converting the fractional part to integer by
* multiplying by a factor of 10
*/
intpart++;
}
/* Convert integer part */
do {
if (iplace == 20)
iplace--;
/* Convert fractional part */
do {
if (fplace == 20)
fplace--;
/* -1 for decimal point, another -1 if we are printing a sign */
if (zpadlen < 0)
zpadlen = 0;
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
if (signvalue) {
--padlen;
signvalue = 0;
}
while (padlen > 0) {
--padlen;
}
}
while (padlen > 0) {
--padlen;
}
if (signvalue)
while (iplace > 0)
/*
* Decimal point. This should probably use locale to find the correct
* char to print out.
*/
while (fplace > 0)
while (zpadlen > 0) {
--zpadlen;
}
while (padlen < 0) {
++padlen;
}
}
static void
{
}
#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
#ifndef HAVE_VSNPRINTF
int
{
str[0] = 0;
}
#endif /* !HAVE_VSNPRINTF */
#ifndef HAVE_SNPRINTF
int
{
}
#ifdef TEST_SNPRINTF
int
main(void)
{
#define LONG_STRING 1024
char buf1[LONG_STRING];
char buf2[LONG_STRING];
char *fp_fmt[] = {
"%-1.5f",
"%1.5f",
"%123.9f",
"%10.5f",
"% 10.5f",
"%+22.9f",
"%+4.9f",
"%01.3f",
"%4f",
"%3.1f",
"%3.2f",
};
double fp_nums[] = {
-1.5,
134.21,
91340.2,
341.1234,
0203.9,
0.96,
0.996,
0.9996,
1.996,
4.136,
0
};
char *int_fmt[] = {
"%-1.5d",
"%1.5d",
"%123.9d",
"%5.5d",
"%10.5d",
"% 10.5d",
"%+22.33d",
"%01.3d",
"%4d",
"%lld",
"%qd",
};
int x, y;
int fail = 0;
int num = 0;
printf("Testing snprintf format codes against system sprintf...\n");
for (y = 0; fp_nums[y] != 0 ; y++) {
printf("snprintf doesn't match Format: %s\n\t"
"snprintf = %s\n\tsprintf = %s\n",
fail++;
}
num++;
}
}
for (y = 0; int_nums[y] != 0 ; y++) {
printf("snprintf doesn't match Format: %s\n\t"
"snprintf = %s\n\tsprintf = %s\n",
fail++;
}
num++;
}
}
return(0);
}
#endif /* SNPRINTF_TEST */
#endif /* !HAVE_SNPRINTF */