#include <fcntl.h>
#include <assert.h>
#include <stdarg.h>
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
/* For TIOCGWINSZ and friends: */
#ifdef HAVE_SYS_IOCTL_H
}
\f
-/* Add thousand separators to a number already in string form. Used
- by with_thousand_seps and with_thousand_seps_large. */
-
-static char *
-add_thousand_seps (const char *repr)
-{
- static char outbuf[48];
- int i, i1, mod;
- char *outptr;
- const char *inptr;
+/* Get grouping data, the separator and grouping info, by calling
+ localeconv(). The information is cached after the first call to
+ the function.
- /* Reset the pointers. */
- outptr = outbuf;
- inptr = repr;
+ In locales that don't set a thousand separator (such as the "C"
+ locale), this forces it to be ",". We are now only showing
+ thousand separators in one place, so this shouldn't be a problem in
+ practice. */
- /* Ignore the sign for the purpose of adding thousand
- separators. */
- if (*inptr == '-')
- {
- *outptr++ = '-';
- ++inptr;
- }
- /* How many digits before the first separator? */
- mod = strlen (inptr) % 3;
- /* Insert them. */
- for (i = 0; i < mod; i++)
- *outptr++ = inptr[i];
- /* Now insert the rest of them, putting separator before every
- third digit. */
- for (i1 = i, i = 0; inptr[i1]; i++, i1++)
+static void
+get_grouping_data (const char **sep, const char **grouping)
+{
+ static const char *cached_sep;
+ static const char *cached_grouping;
+ static bool initialized;
+ if (!initialized)
{
- if (i % 3 == 0 && i1 != 0)
- *outptr++ = ',';
- *outptr++ = inptr[i1];
+ /* Get the grouping info from the locale. */
+ struct lconv *lconv = localeconv ();
+ cached_sep = lconv->thousands_sep;
+ cached_grouping = lconv->grouping;
+ if (!*cached_sep)
+ {
+ /* Many locales (such as "C" or "hr_HR") don't specify
+ grouping, which we still want to use it for legibility.
+ In those locales set the sep char to ',', unless that
+ character is used for decimal point, in which case set it
+ to " ". */
+ if (*lconv->decimal_point != ',')
+ cached_sep = ",";
+ else
+ cached_sep = " ";
+ cached_grouping = "\x03";
+ }
+ initialized = true;
}
- /* Zero-terminate the string. */
- *outptr = '\0';
- return outbuf;
+ *sep = cached_sep;
+ *grouping = cached_grouping;
}
-/* Return a static pointer to the number printed with thousand
- separators inserted at the right places. */
+/* Return a printed representation of N with thousand separators.
+ This should respect locale settings, with the exception of the "C"
+ locale which mandates no separator, but we use one anyway.
-char *
-with_thousand_seps (wgint l)
+ Unfortunately, we cannot use %'d (in fact it would be %'j) to get
+ the separators because it's too non-portable, and it's hard to test
+ for this feature at configure time. Besides, it wouldn't work in
+ the "C" locale, which many Unix users still work in. */
+
+const char *
+with_thousand_seps (wgint n)
{
- char inbuf[24];
- /* Print the number into the buffer. */
- number_to_string (inbuf, l);
- return add_thousand_seps (inbuf);
-}
+ static char outbuf[48];
+ char *p = outbuf + sizeof outbuf;
-/* Write a string representation of LARGE_INT NUMBER into the provided
- buffer.
+ /* Info received from locale */
+ const char *grouping, *sep;
+ int seplen;
- It would be dangerous to use sprintf, because the code wouldn't
- work on a machine with gcc-provided long long support, but without
- libc support for "%lld". However, such old systems platforms
- typically lack snprintf and will end up using our version, which
- does support "%lld" whereever long longs are available. */
+ /* State information */
+ int i = 0, groupsize;
+ const char *atgroup;
-static void
-large_int_to_string (char *buffer, int bufsize, LARGE_INT number)
-{
- snprintf (buffer, bufsize, LARGE_INT_FMT, number);
-}
+ bool negative = n < 0;
-/* The same as with_thousand_seps, but works on LARGE_INT. */
+ /* Initialize grouping data. */
+ get_grouping_data (&sep, &grouping);
+ seplen = strlen (sep);
+ atgroup = grouping;
+ groupsize = *atgroup++;
-char *
-with_thousand_seps_large (LARGE_INT l)
-{
- char inbuf[48];
- large_int_to_string (inbuf, sizeof (inbuf), l);
- return add_thousand_seps (inbuf);
+ /* This will overflow on WGINT_MIN, but we're not using this to
+ print negative numbers anyway. */
+ if (negative)
+ n = -n;
+
+ /* Write the number into the buffer, backwards, inserting the
+ separators as necessary. */
+ *--p = '\0';
+ while (1)
+ {
+ *--p = n % 10 + '0';
+ n /= 10;
+ if (n == 0)
+ break;
+ /* Prepend SEP to every groupsize'd digit and get new groupsize. */
+ if (++i == groupsize)
+ {
+ if (seplen == 1)
+ *--p = *sep;
+ else
+ memcpy (p -= seplen, sep, seplen);
+ i = 0;
+ if (*atgroup)
+ groupsize = *atgroup++;
+ }
+ }
+ if (negative)
+ *--p = '-';
+
+ return p;
}
/* N, a byte quantity, is converted to a human-readable abberviated
usually improves readability."
This intentionally uses kilobyte (KB), megabyte (MB), etc. in their
- original computer science meaning of "powers of 1024". Powers of
+ original computer-related meaning of "powers of 1024". Powers of
1000 would be useless since Wget already displays sizes with
thousand separators. We don't use the "*bibyte" names invented in
1998, and seldom used in practice. Wikipedia's entry on kilobyte
discusses this in some detail. */
char *
-human_readable (wgint n)
+human_readable (HR_NUMTYPE n)
{
/* These suffixes are compatible with those of GNU `ls -lh'. */
static char powers[] =
/* At each iteration N is greater than the *subsequent* power.
That way N/1024.0 produces a decimal number in the units of
*this* power. */
- if ((n >> 10) < 1024 || i == countof (powers) - 1)
+ if ((n / 1024) < 1024 || i == countof (powers) - 1)
{
- /* Must cast to long first because MS VC can't directly cast
- __int64 to double. (This is safe because N is known to
- be <2**20.) */
- double val = (double) (long) n / 1024.0;
+ double val = n / 1024.0;
/* Print values smaller than 10 with one decimal digits, and
others without any decimals. */
snprintf (buf, sizeof (buf), "%.*f%c",
val < 10 ? 1 : 0, val, powers[i]);
return buf;
}
- n >>= 10;
+ n /= 1024;
}
return NULL; /* unreached */
}
#elif SIZEOF_LONG_LONG >= SIZEOF_WGINT
# define SPRINTF_WGINT(buf, n) sprintf (buf, "%lld", (long long) (n))
#elif defined(WINDOWS)
-# define SPRINTF_WGINT(buf, n) sprintf (buf, "%I64", (__int64) (n))
+# define SPRINTF_WGINT(buf, n) sprintf (buf, "%I64d", (__int64) (n))
#else
# define SPRINTF_WGINT(buf, n) sprintf (buf, "%j", (intmax_t) (n))
#endif
#undef PR
#undef W
+#undef SPRINTF_WGINT
#undef DIGITS_1
#undef DIGITS_2
#undef DIGITS_3