Folks,
The floating point output formatting routines used by rsync (snprintf)
contain a truncation issue, at least for gcc on Solaris (but probably
generally too). This will result in values (e.g. bytes read, bytes
written, etc.) greater than LONG_MAX being truncated/mangled in the
summary reporting. (Quite noticeable if you're mirroring BIG trees.)
SunOS 5.6 Generic_105181-10 sun4u sparc SUNW,Ultra-250
gcc version 2.95.2 19991024 (release)
GNU assembler version 2.9.1 (sparc-sun-solaris2.5.1), using BFD version 2.9.1
GNU ld version 2.9.1 (with BFD 2.9.1)
The problem arises from the fact that the snprintf implementation in
rsync chooses to convert the mantissa of a floating point number to a
long before formatting. This is a reasonable and simple way to go
about doing this (although presumably we could grab some more
sophisticated routines from Linux glibc or something).
Here is a patch that provides a higher degree of precision for
machines with HAVE_LONGLONG (and is backward compatible for those
which do not). Again, we could probably use a better implementation
overall if someone knows of one (as usual, I'm a bit short of cycles
myself at the moment .... :-)
--- rsync-2.4.3/lib/snprintf.c.orig Tue May 30 15:29:50 2000
+++ rsync-2.4.3/lib/snprintf.c Tue May 30 15:42:41 2000
@@ -89,10 +89,16 @@
#define LDOUBLE long double
#else
#define LDOUBLE double
#endif
+#if defined( HAVE_LONGLONG )
+# define LINTEGER long long
+#else
+# define LINTEGER long
+#endif
+
/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
static void dopr (char *buffer, size_t maxlen, const char *format,
va_list args);
@@ -555,19 +561,19 @@
static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
LDOUBLE fvalue, int min, int max, int flags)
{
int signvalue = 0;
LDOUBLE ufvalue;
- char iconvert[20];
- char fconvert[20];
+ char iconvert[30];
+ char fconvert[30];
int iplace = 0;
int fplace = 0;
int padlen = 0; /* amount to pad */
int zpadlen = 0;
int caps = 0;
- long intpart;
- long fracpart;
+ unsigned LINTEGER intpart;
+ unsigned LINTEGER fracpart;
/*
* AIX manpage says the default is 0, but Solaris says the default
* is 6, and sprintf on AIX defaults to 6
*/
@@ -587,11 +593,11 @@
#if 0
if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
#endif
- intpart = (long)ufvalue;
+ intpart = ufvalue;
/*
* Sorry, we only support 9 digits past the decimal because of our
* conversion method
*/
@@ -608,30 +614,33 @@
intpart++;
fracpart -= pow10 (max);
}
#ifdef DEBUG_SNPRINTF
- printf("fmtfp: %g %d.%d min=%d max=%d\n",
+# if !defined( HAVE_LONGLONG )
+ printf("fmtfp: %g %lu.%lu min=%d max=%d\n",
+ (double)fvalue, intpart, fracpart, min, max);
+# else
+ printf("fmtfp: %g %llu.%llu min=%d max=%d\n",
(double)fvalue, intpart, fracpart, min, max);
+# endif
#endif
/* Convert integer part */
do {
iconvert[iplace++] =
(caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
intpart = (intpart / 10);
- } while(intpart && (iplace < 20));
- if (iplace == 20) iplace--;
+ } while(intpart && (iplace < sizeof(iconvert)-1));
iconvert[iplace] = 0;
/* Convert fractional part */
do {
fconvert[fplace++] =
(caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
fracpart = (fracpart / 10);
- } while(fracpart && (fplace < 20));
- if (fplace == 20) fplace--;
+ } while(fracpart && (fplace < sizeof(fconvert)-1));
fconvert[fplace] = 0;
/* -1 for decimal point, another -1 if we are printing a sign */
padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
zpadlen = max - fplace;
Regards,
Neil
--
Neil Schellenberger | Voice : (613) 599-2300 ext. 8445
CrossKeys Systems Corporation | Fax : (613) 599-2330
350 Terry Fox Drive | E-Mail: [EMAIL PROTECTED]
Kanata, Ont., Canada, K2K 2W5 | URL : http://www.crosskeys.com/
+ Greg Moore (1975-1999), Gentleman racer and great Canadian +