#include <fcntl.h>
#include <assert.h>
#include <stdarg.h>
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
+#include <locale.h>
/* For TIOCGWINSZ and friends: */
#ifdef HAVE_SYS_IOCTL_H
}
\f
+/* Get grouping data, the separator and grouping info, by calling
+ localeconv(). The information is cached after the first call to
+ the function.
+
+ 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. */
+
+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)
+ {
+ /* 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;
+ }
+ *sep = cached_sep;
+ *grouping = cached_grouping;
+}
+
/* 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.
with_thousand_seps (wgint n)
{
static char outbuf[48];
+ char *p = outbuf + sizeof outbuf;
- static char loc_sepchar;
- static const char *loc_grouping;
+ /* Info received from locale */
+ const char *grouping, *sep;
+ int seplen;
+ /* State information */
int i = 0, groupsize;
- char *p;
const char *atgroup;
- if (!loc_sepchar)
- {
-#ifdef LC_NUMERIC
- /* Get the grouping character from the locale. */
- struct lconv *lconv;
- const char *oldlocale = setlocale (LC_NUMERIC, "");
- lconv = localeconv ();
- loc_sepchar = *lconv->thousands_sep;
- loc_grouping = xstrdup (lconv->grouping);
- /* Restore the C locale semantics of printing and reading numbers */
- setlocale (LC_NUMERIC, oldlocale);
- if (!loc_sepchar)
-#endif
- /* defaults for C locale or no locale */
- loc_sepchar = ',', loc_grouping = "\x03";
- }
- atgroup = loc_grouping;
+ bool negative = n < 0;
- p = outbuf + sizeof outbuf;
- *--p = '\0';
+ /* Initialize grouping data. */
+ get_grouping_data (&sep, &grouping);
+ seplen = strlen (sep);
+ atgroup = grouping;
groupsize = *atgroup++;
+ /* 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;
- /* Insert the separator on every groupsize'd digit, and get the
- new groupsize. */
+ /* Prepend SEP to every groupsize'd digit and get new groupsize. */
if (++i == groupsize)
{
- *--p = loc_sepchar;
+ if (seplen == 1)
+ *--p = *sep;
+ else
+ memcpy (p -= seplen, sep, seplen);
i = 0;
if (*atgroup)
groupsize = *atgroup++;
}
}
+ if (negative)
+ *--p = '-';
+
return p;
}
*this* power. */
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 < 1024^2, so always fits into long.) */
- 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",
#undef PR
#undef W
+#undef SPRINTF_WGINT
#undef DIGITS_1
#undef DIGITS_2
#undef DIGITS_3
return 0;
#endif /* neither TIOCGWINSZ nor WINDOWS */
}
+\f
+/* Whether the rnd system (either rand or [dl]rand48) has been
+ seeded. */
+static int rnd_seeded;
/* Return a random number between 0 and MAX-1, inclusive.
- If MAX is greater than the value of RAND_MAX+1 on the system, the
- returned value will be in the range [0, RAND_MAX]. This may be
- fixed in a future release.
-
+ If the system does not support lrand48 and MAX is greater than the
+ value of RAND_MAX+1 on the system, the returned value will be in
+ the range [0, RAND_MAX]. This may be fixed in a future release.
The random number generator is seeded automatically the first time
it is called.
- This uses rand() for portability. It has been suggested that
- random() offers better randomness, but this is not required for
- Wget, so I chose to go for simplicity and use rand
- unconditionally.
-
- DO NOT use this for cryptographic purposes. It is only meant to be
- used in situations where quality of the random numbers returned
- doesn't really matter. */
+ This uses lrand48 where available, rand elsewhere. DO NOT use it
+ for cryptography. It is only meant to be used in situations where
+ quality of the random numbers returned doesn't really matter. */
int
random_number (int max)
{
- static int seeded;
+#ifdef HAVE_DRAND48
+ if (!rnd_seeded)
+ {
+ srand48 ((long) time (NULL) ^ (long) getpid ());
+ rnd_seeded = 1;
+ }
+ return lrand48 () % max;
+#else /* not HAVE_DRAND48 */
+
double bounded;
int rnd;
-
- if (!seeded)
+ if (!rnd_seeded)
{
- srand (time (NULL));
- seeded = 1;
+ srand ((unsigned) time (NULL) ^ (unsigned) getpid ());
+ rnd_seeded = 1;
}
rnd = rand ();
- /* On systems that don't define RAND_MAX, assume it to be 2**15 - 1,
- and enforce that assumption by masking other bits. */
-#ifndef RAND_MAX
-# define RAND_MAX 32767
- rnd &= RAND_MAX;
-#endif
+ /* Like rand() % max, but uses the high-order bits for better
+ randomness on architectures where rand() is implemented using a
+ simple congruential generator. */
- /* This is equivalent to rand() % max, but uses the high-order bits
- for better randomness on architecture where rand() is implemented
- using a simple congruential generator. */
+ bounded = (double) max * rnd / (RAND_MAX + 1.0);
+ return (int) bounded;
- bounded = (double)max * rnd / (RAND_MAX + 1.0);
- return (int)bounded;
+#endif /* not HAVE_DRAND48 */
}
/* Return a random uniformly distributed floating point number in the
- [0, 1) range. The precision of returned numbers is 9 digits.
-
- Modify this to use erand48() where available! */
+ [0, 1) range. Uses drand48 where available, and a really lame
+ kludge elsewhere. */
double
random_float (void)
{
- /* We can't rely on any specific value of RAND_MAX, but I'm pretty
- sure it's greater than 1000. */
- int rnd1 = random_number (1000);
- int rnd2 = random_number (1000);
- int rnd3 = random_number (1000);
- return rnd1 / 1000.0 + rnd2 / 1000000.0 + rnd3 / 1000000000.0;
+#ifdef HAVE_DRAND48
+ if (!rnd_seeded)
+ {
+ srand48 ((long) time (NULL) ^ (long) getpid ());
+ rnd_seeded = 1;
+ }
+ return drand48 ();
+#else /* not HAVE_DRAND48 */
+ return ( random_number (10000) / 10000.0
+ + random_number (10000) / (10000.0 * 10000.0)
+ + random_number (10000) / (10000.0 * 10000.0 * 10000.0)
+ + random_number (10000) / (10000.0 * 10000.0 * 10000.0 * 10000.0));
+#endif /* not HAVE_DRAND48 */
}
\f
/* Implementation of run_with_timeout, a generic timeout-forcing
mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun);
}
}
+\f
+/* Print a decimal number. If it is equal to or larger than ten, the
+ number is rounded. Otherwise it is printed with one significant
+ digit without trailing zeros and with no more than three fractional
+ digits total. For example, 0.1 is printed as "0.1", 0.035 is
+ printed as "0.04", 0.0091 as "0.009", and 0.0003 as simply "0".
+
+ This is useful for displaying durations because it provides
+ order-of-magnitude information without unnecessary clutter --
+ long-running downloads are shown without the fractional part, and
+ short ones still retain one significant digit. */
+
+const char *
+print_decimal (double number)
+{
+ static char buf[32];
+ double n = number >= 0 ? number : -number;
+
+ if (n >= 9.95)
+ /* Cut off at 9.95 because the below %.1f would round 9.96 to
+ "10.0" instead of "10". OTOH 9.94 will print as "9.9". */
+ snprintf (buf, sizeof buf, "%.0f", number);
+ else if (n >= 0.95)
+ snprintf (buf, sizeof buf, "%.1f", number);
+ else if (n >= 0.001)
+ snprintf (buf, sizeof buf, "%.1g", number);
+ else if (n >= 0.0005)
+ /* round [0.0005, 0.001) to 0.001 */
+ snprintf (buf, sizeof buf, "%.3f", number);
+ else
+ /* print numbers close to 0 as 0, not 0.000 */
+ strcpy (buf, "0");
+
+ return buf;
+}