]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Include ETA information in dot progress.
[wget] / src / utils.c
index 31598348103685b4de50f4b70c5924cb2f8bdbcf..9c8beb10f5e05062dd43896fa48ba5183ef5ff70 100644 (file)
@@ -1164,6 +1164,46 @@ free_keys_and_values (struct hash_table *ht)
 }
 
 \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.
@@ -1177,52 +1217,53 @@ const char *
 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;
 }
 
@@ -1276,10 +1317,7 @@ human_readable (HR_NUMTYPE n)
         *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",
@@ -1439,6 +1477,7 @@ number_to_string (char *buffer, wgint number)
 
 #undef PR
 #undef W
+#undef SPRINTF_WGINT
 #undef DIGITS_1
 #undef DIGITS_2
 #undef DIGITS_3
@@ -1583,13 +1622,13 @@ random_number (int max)
 /* 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!  */
+   Modify this to use drand48() where available!  */
 
 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.  */
+  /* We can't rely on any specific value of RAND_MAX, but it must
+     always be greater than 1000.  */
   int rnd1 = random_number (1000);
   int rnd2 = random_number (1000);
   int rnd3 = random_number (1000);
@@ -2000,3 +2039,38 @@ stable_sort (void *base, size_t nmemb, size_t size,
       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;
+}