]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Move extern declarations to .h files.
[wget] / src / utils.c
index 9eea3a4e1d66429d5c13f99b7c70fc5ddfc5e694..fbf9b47f7ff26f04daa28048a7e0847314467871 100644 (file)
@@ -58,6 +58,9 @@ so, delete this exception statement from your version.  */
 #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
@@ -1161,71 +1164,108 @@ free_keys_and_values (struct hash_table *ht)
 }
 
 \f
-/* Add thousand separators to a number already in string form.  Used
-   by with_thousand_seps and with_thousand_seps_sum.  */
-
-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;
 
-/* When SUM_SIZE_INT is wgint, with_thousand_seps_large is #defined to
-   with_thousand_seps.  The function below is used on non-LFS systems
-   where SUM_SIZE_INT typedeffed to double.  */
+  /* Info received from locale */
+  const char *grouping, *sep;
+  int seplen;
 
-#ifndef with_thousand_seps_sum
-char *
-with_thousand_seps_sum (SUM_SIZE_INT l)
-{
-  char inbuf[32];
-  snprintf (inbuf, sizeof (inbuf), "%.0f", l);
-  return add_thousand_seps (inbuf);
+  /* State information */
+  int i = 0, groupsize;
+  const char *atgroup;
+
+  bool negative = n < 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;
+      /* 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;
 }
-#endif /* not with_thousand_seps_sum */
 
 /* N, a byte quantity, is converted to a human-readable abberviated
    form a la sizes printed by `ls -lh'.  The result is written to a
@@ -1245,7 +1285,7 @@ with_thousand_seps_sum (SUM_SIZE_INT l)
    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[] =
@@ -1277,10 +1317,7 @@ human_readable (wgint 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",
@@ -1440,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