Re: thousands comma numeric formatting in psql - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: thousands comma numeric formatting in psql
Date
Msg-id 200507140848.j6E8mCC28979@candle.pha.pa.us
Whole thread Raw
In response to thousands comma numeric formatting in psql  (Eugen Nedelcu <eugen@sifolt.ro>)
Responses Re: thousands comma numeric formatting in psql
Re: thousands comma numeric formatting in psql
List pgsql-patches
Eugen Nedelcu wrote:
> Regarding locale aproach, it is trivial to replace langinfo with
> localeconv:
>
> struct lconv *l = localeconv();
> char *dec_point = l->decimal_point;
>
> instead of:
>
> #include langinfo.h
> char *dec_point = nl_langinfo(__DECIMAL_POINT);
>
> I used langinfo because in linux libc it is considered
> "The Elegant and Fast Way" of using locale and conforms with
> X/Open portability guide that every modern Unix follows
> (Solaris, Linux, latest FreeBSD).
>
> Regarding locale vs. \pset numericsep, for me it doesn't matter
> which one is used. I consider the patch very usefull, and I think
> that other guys that use psql daily for working with financial data
> will appreciate it too.
>
> With a quick setting like \pset numericsep ',' all my numeric fields
> will appear in easy readable form. I must underline that to_char is
> a backend function and we talk here about a little psql feature which
> makes life a little easier (for me at least).

OK, I have applied the following patch to make numerisep a boolean, made
it locale-aware, set defaults if the locale doesn't return meaningful
values, added code to handle locale-reported groupings, and updated the
documentation.

The only question I have is whether those locale values are single-byte
strings in all locals, or could they be multi-byte or multi-character,
in which case I have to treat them as strings.  For now, I added a code
comment that we treat them as single-byte strings.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.146
retrieving revision 1.147
diff -c -r1.146 -r1.147
*** doc/src/sgml/ref/psql-ref.sgml    10 Jul 2005 03:46:12 -0000    1.146
--- doc/src/sgml/ref/psql-ref.sgml    14 Jul 2005 08:42:36 -0000    1.147
***************
*** 1,5 ****
  <!--
! $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.146 2005/07/10 03:46:12 momjian Exp $
  PostgreSQL documentation
  -->

--- 1,5 ----
  <!--
! $PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.147 2005/07/14 08:42:36 momjian Exp $
  PostgreSQL documentation
  -->

***************
*** 1496,1505 ****
            <term><literal>numericsep</literal></term>
            <listitem>
            <para>
!           Specifies the character separator between groups of three digits
!           to the left of the decimal marker.  The default is <literal>''</>
!           (none).  Setting this to a period also changes the decimal marker
!           to a comma.
            </para>
            </listitem>
            </varlistentry>
--- 1496,1504 ----
            <term><literal>numericsep</literal></term>
            <listitem>
            <para>
!           Toggles the display of a locale-aware character to separate groups
!           of digits to the left of the decimal marker.  It also enables
!           a locale-aware decimal marker.
            </para>
            </listitem>
            </varlistentry>
Index: src/bin/psql/command.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v
retrieving revision 1.148
retrieving revision 1.149
diff -c -r1.148 -r1.149
*** src/bin/psql/command.c    14 Jul 2005 06:49:58 -0000    1.148
--- src/bin/psql/command.c    14 Jul 2005 08:42:37 -0000    1.149
***************
*** 3,9 ****
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.148 2005/07/14 06:49:58 momjian Exp $
   */
  #include "postgres_fe.h"
  #include "command.h"
--- 3,9 ----
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.149 2005/07/14 08:42:37 momjian Exp $
   */
  #include "postgres_fe.h"
  #include "command.h"
***************
*** 1420,1434 ****
                     : _("Expanded display is off.\n"));
      }

      else if (strcmp(param, "numericsep") == 0)
      {
!         if (value)
          {
!             free(popt->topt.numericSep);
!             popt->topt.numericSep = pg_strdup(value);
          }
-         if (!quiet)
-             printf(_("Numeric separator is \"%s\".\n"), popt->topt.numericSep);
      }

      /* null display */
--- 1420,1436 ----
                     : _("Expanded display is off.\n"));
      }

+     /* numeric separators */
      else if (strcmp(param, "numericsep") == 0)
      {
!         popt->topt.numericSep = !popt->topt.numericSep;
!         if (!quiet)
          {
!             if (popt->topt.numericSep)
!                 puts(_("Showing numeric separators."));
!             else
!                 puts(_("Numeric separators are off."));
          }
      }

      /* null display */
Index: src/bin/psql/print.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v
retrieving revision 1.66
retrieving revision 1.67
diff -c -r1.66 -r1.67
*** src/bin/psql/print.c    14 Jul 2005 07:32:01 -0000    1.66
--- src/bin/psql/print.c    14 Jul 2005 08:42:37 -0000    1.67
***************
*** 3,9 ****
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.66 2005/07/14 07:32:01 momjian Exp $
   */
  #include "postgres_fe.h"
  #include "common.h"
--- 3,9 ----
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.67 2005/07/14 08:42:37 momjian Exp $
   */
  #include "postgres_fe.h"
  #include "common.h"
***************
*** 24,34 ****
--- 24,40 ----
  #include <termios.h>
  #endif

+ #include <locale.h>
+
  #include "pqsignal.h"
  #include "libpq-fe.h"

  #include "mbprint.h"

+ static char *decimal_point;
+ static char *grouping;
+ static char *thousands_sep;
+
  static void *
  pg_local_malloc(size_t size)
  {
***************
*** 47,52 ****
--- 53,59 ----
  num_numericseps(const char *my_str)
  {
      int old_len, dec_len, int_len;
+     int    groupdigits = atoi(grouping);

      if (my_str[0] == '-')
          my_str++;
***************
*** 55,64 ****
      dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;

      int_len = old_len - dec_len;
!     if (int_len % 3 != 0)
!         return int_len / 3;
      else
!         return int_len / 3 - 1;    /* no leading separator */
  }

  static int
--- 62,71 ----
      dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;

      int_len = old_len - dec_len;
!     if (int_len % groupdigits != 0)
!         return int_len / groupdigits;
      else
!         return int_len / groupdigits - 1;    /* no leading separator */
  }

  static int
***************
*** 68,84 ****
  }

  static void
! format_numericsep(char *my_str, char *numericsep)
  {
      int i, j, digits_before_sep, old_len, new_len, dec_len, int_len;
-     char *dec_point;
      char *new_str;
      char *dec_value;
!
!     if (strcmp(numericsep, ".") != 0)
!         dec_point = ".";
!     else
!         dec_point = ",";

      if (my_str[0] == '-')
          my_str++;
--- 75,86 ----
  }

  static void
! format_numericsep(char *my_str)
  {
      int i, j, digits_before_sep, old_len, new_len, dec_len, int_len;
      char *new_str;
      char *dec_value;
!     int    groupdigits = atoi(grouping);

      if (my_str[0] == '-')
          my_str++;
***************
*** 86,94 ****
      old_len = strlen(my_str);
      dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
      int_len = old_len - dec_len;
!     digits_before_sep = int_len % 3;

!     new_len = int_len + int_len / 3 + dec_len;
      if (digits_before_sep == 0)
          new_len--;    /* no leading separator */

--- 88,96 ----
      old_len = strlen(my_str);
      dec_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
      int_len = old_len - dec_len;
!     digits_before_sep = int_len % groupdigits;

!     new_len = int_len + int_len / groupdigits + dec_len;
      if (digits_before_sep == 0)
          new_len--;    /* no leading separator */

***************
*** 99,105 ****
          /* hit decimal point */
          if (my_str[i] == '.')
          {
!             new_str[j] = *dec_point;
              new_str[j+1] = '\0';
              dec_value = strchr(my_str, '.');
              strcat(new_str, ++dec_value);
--- 101,107 ----
          /* hit decimal point */
          if (my_str[i] == '.')
          {
!             new_str[j] = *decimal_point;
              new_str[j+1] = '\0';
              dec_value = strchr(my_str, '.');
              strcat(new_str, ++dec_value);
***************
*** 115,122 ****

          /* add separator? */
          if (i != 0 &&
!             (i - (digits_before_sep ? digits_before_sep : 3)) % 3 == 0)
!             new_str[j++] = *numericsep;

          new_str[j] = my_str[i];
      }
--- 117,125 ----

          /* add separator? */
          if (i != 0 &&
!             (i - (digits_before_sep ? digits_before_sep : groupdigits))
!                 % groupdigits == 0)
!             new_str[j++] = *thousands_sep;

          new_str[j] = my_str[i];
      }
***************
*** 135,141 ****
                       const char *const *cells, const char *const *footers,
                       const char *opt_align, const char *opt_fieldsep,
                       const char *opt_recordsep, bool opt_tuples_only,
!                      char *opt_numericsep, FILE *fout)
  {
      unsigned int col_count = 0;
      unsigned int i;
--- 138,144 ----
                       const char *const *cells, const char *const *footers,
                       const char *opt_align, const char *opt_fieldsep,
                       const char *opt_recordsep, bool opt_tuples_only,
!                      bool opt_numericsep, FILE *fout)
  {
      unsigned int col_count = 0;
      unsigned int i;
***************
*** 174,186 ****
              fputs(opt_recordsep, fout);
              need_recordsep = false;
          }
!         if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              fputs(my_cell, fout);
              free(my_cell);
          }
--- 177,188 ----
              fputs(opt_recordsep, fout);
              need_recordsep = false;
          }
!         if (opt_align[i % col_count] == 'r' && opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              fputs(my_cell, fout);
              free(my_cell);
          }
***************
*** 220,226 ****
                           const char *const *cells,
                           const char *const *footers, const char *opt_align,
                           const char *opt_fieldsep, const char *opt_recordsep,
!                          bool opt_tuples_only, char *opt_numericsep, FILE *fout)
  {
      unsigned int col_count = 0;
      unsigned int i;
--- 222,228 ----
                           const char *const *cells,
                           const char *const *footers, const char *opt_align,
                           const char *opt_fieldsep, const char *opt_recordsep,
!                          bool opt_tuples_only, bool opt_numericsep, FILE *fout)
  {
      unsigned int col_count = 0;
      unsigned int i;
***************
*** 251,263 ****

          fputs(headers[i % col_count], fout);
          fputs(opt_fieldsep, fout);
!         if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              fputs(my_cell, fout);
              free(my_cell);
          }
--- 253,264 ----

          fputs(headers[i % col_count], fout);
          fputs(opt_fieldsep, fout);
!         if (opt_align[i % col_count] == 'r' && opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              fputs(my_cell, fout);
              free(my_cell);
          }
***************
*** 325,331 ****
  static void
  print_aligned_text(const char *title, const char *const *headers,
                     const char *const *cells, const char *const *footers,
!                    const char *opt_align, bool opt_tuples_only, char *opt_numericsep,
                     unsigned short int opt_border, int encoding,
                     FILE *fout)
  {
--- 326,332 ----
  static void
  print_aligned_text(const char *title, const char *const *headers,
                     const char *const *cells, const char *const *footers,
!                    const char *opt_align, bool opt_tuples_only, bool opt_numericsep,
                     unsigned short int opt_border, int encoding,
                     FILE *fout)
  {
***************
*** 394,401 ****
      {
          int numericseps;

!         if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
              numericseps = num_numericseps(*ptr);
          else
              numericseps = 0;
--- 395,401 ----
      {
          int numericseps;

!         if (opt_align[i % col_count] == 'r' && opt_numericsep)
              numericseps = num_numericseps(*ptr);
          else
              numericseps = 0;
***************
*** 480,491 ****
          /* content */
          if (opt_align[i % col_count] == 'r')
          {
!             if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0)
              {
                  char *my_cell = pg_local_malloc(cell_w[i] + 1);

                  strcpy(my_cell, *ptr);
!                 format_numericsep(my_cell, opt_numericsep);
                  fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell);
                  free(my_cell);
              }
--- 480,491 ----
          /* content */
          if (opt_align[i % col_count] == 'r')
          {
!             if (opt_numericsep)
              {
                  char *my_cell = pg_local_malloc(cell_w[i] + 1);

                  strcpy(my_cell, *ptr);
!                 format_numericsep(my_cell);
                  fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell);
                  free(my_cell);
              }
***************
*** 547,553 ****
  print_aligned_vertical(const char *title, const char *const *headers,
                         const char *const *cells, const char *const *footers,
                         const char *opt_align, bool opt_tuples_only,
!                        char *opt_numericsep, unsigned short int opt_border,
                         int encoding, FILE *fout)
  {
      unsigned int col_count = 0;
--- 547,553 ----
  print_aligned_vertical(const char *title, const char *const *headers,
                         const char *const *cells, const char *const *footers,
                         const char *opt_align, bool opt_tuples_only,
!                        bool opt_numericsep, unsigned short int opt_border,
                         int encoding, FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 612,619 ****
      {
          int numericseps;

!         if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
              numericseps = num_numericseps(*ptr);
          else
              numericseps = 0;
--- 612,618 ----
      {
          int numericseps;

!         if (opt_align[i % col_count] == 'r' && opt_numericsep)
              numericseps = num_numericseps(*ptr);
          else
              numericseps = 0;
***************
*** 696,704 ****
              char *my_cell = pg_local_malloc(cell_w[i] + 1);

              strcpy(my_cell, *ptr);
!             if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!                 opt_numericsep != NULL && strlen(opt_numericsep) > 0)
!                 format_numericsep(my_cell, opt_numericsep);
              if (opt_border < 2)
                  puts(my_cell);
              else
--- 695,702 ----
              char *my_cell = pg_local_malloc(cell_w[i] + 1);

              strcpy(my_cell, *ptr);
!             if (opt_align[i % col_count] == 'r' && opt_numericsep)
!                 format_numericsep(my_cell);
              if (opt_border < 2)
                  puts(my_cell);
              else
***************
*** 785,791 ****
  print_html_text(const char *title, const char *const *headers,
                  const char *const *cells, const char *const *footers,
                  const char *opt_align, bool opt_tuples_only,
!                 char *opt_numericsep, unsigned short int opt_border,
                  const char *opt_table_attr, FILE *fout)
  {
      unsigned int col_count = 0;
--- 783,789 ----
  print_html_text(const char *title, const char *const *headers,
                  const char *const *cells, const char *const *footers,
                  const char *opt_align, bool opt_tuples_only,
!                 bool opt_numericsep, unsigned short int opt_border,
                  const char *opt_table_attr, FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 831,843 ****
          /* is string only whitespace? */
          if ((*ptr)[strspn(*ptr, " \t")] == '\0')
              fputs("  ", fout);
!         else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!                  opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              html_escaped_print(my_cell, fout);
              free(my_cell);
          }
--- 829,840 ----
          /* is string only whitespace? */
          if ((*ptr)[strspn(*ptr, " \t")] == '\0')
              fputs("  ", fout);
!         else if (opt_align[i % col_count] == 'r' && opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              html_escaped_print(my_cell, fout);
              free(my_cell);
          }
***************
*** 873,879 ****
  print_html_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   char *opt_numericsep, unsigned short int opt_border,
                    const char *opt_table_attr, FILE *fout)
  {
      unsigned int col_count = 0;
--- 870,876 ----
  print_html_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   bool opt_numericsep, unsigned short int opt_border,
                    const char *opt_table_attr, FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 917,929 ****
          /* is string only whitespace? */
          if ((*ptr)[strspn(*ptr, " \t")] == '\0')
              fputs("  ", fout);
!         else if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              html_escaped_print(my_cell, fout);
              free(my_cell);
          }
--- 914,925 ----
          /* is string only whitespace? */
          if ((*ptr)[strspn(*ptr, " \t")] == '\0')
              fputs("  ", fout);
!         else if (opt_align[i % col_count] == 'r' && opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              html_escaped_print(my_cell, fout);
              free(my_cell);
          }
***************
*** 999,1005 ****
  print_latex_text(const char *title, const char *const *headers,
                   const char *const *cells, const char *const *footers,
                   const char *opt_align, bool opt_tuples_only,
!                  char *opt_numericsep, unsigned short int opt_border,
                   FILE *fout)
  {
      unsigned int col_count = 0;
--- 995,1001 ----
  print_latex_text(const char *title, const char *const *headers,
                   const char *const *cells, const char *const *footers,
                   const char *opt_align, bool opt_tuples_only,
!                  bool opt_numericsep, unsigned short int opt_border,
                   FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 1060,1072 ****
      /* print cells */
      for (i = 0, ptr = cells; *ptr; i++, ptr++)
      {
!         if (strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              latex_escaped_print(my_cell, fout);
              free(my_cell);
          }
--- 1056,1067 ----
      /* print cells */
      for (i = 0, ptr = cells; *ptr; i++, ptr++)
      {
!         if (opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              latex_escaped_print(my_cell, fout);
              free(my_cell);
          }
***************
*** 1103,1109 ****
  print_latex_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   char *opt_numericsep, unsigned short int opt_border,
                    FILE *fout)
  {
      unsigned int col_count = 0;
--- 1098,1104 ----
  print_latex_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   bool opt_numericsep, unsigned short int opt_border,
                    FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 1174,1186 ****
      if (footers && !opt_tuples_only)
          for (ptr = footers; *ptr; ptr++)
          {
!             if (strlen(*ptr) != 0 &&
!                 opt_numericsep != NULL && strlen(opt_numericsep) > 0)
              {
                  char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

                  strcpy(my_cell, *ptr);
!                 format_numericsep(my_cell, opt_numericsep);
                  latex_escaped_print(my_cell, fout);
                  free(my_cell);
              }
--- 1169,1180 ----
      if (footers && !opt_tuples_only)
          for (ptr = footers; *ptr; ptr++)
          {
!             if (opt_numericsep)
              {
                  char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

                  strcpy(my_cell, *ptr);
!                 format_numericsep(my_cell);
                  latex_escaped_print(my_cell, fout);
                  free(my_cell);
              }
***************
*** 1221,1227 ****
  print_troff_ms_text(const char *title, const char *const *headers,
                   const char *const *cells, const char *const *footers,
                   const char *opt_align, bool opt_tuples_only,
!                  char *opt_numericsep, unsigned short int opt_border,
                   FILE *fout)
  {
      unsigned int col_count = 0;
--- 1215,1221 ----
  print_troff_ms_text(const char *title, const char *const *headers,
                   const char *const *cells, const char *const *footers,
                   const char *opt_align, bool opt_tuples_only,
!                  bool opt_numericsep, unsigned short int opt_border,
                   FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 1275,1287 ****
      /* print cells */
      for (i = 0, ptr = cells; *ptr; i++, ptr++)
      {
!         if (strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              troff_ms_escaped_print(my_cell, fout);
              free(my_cell);
          }
--- 1269,1280 ----
      /* print cells */
      for (i = 0, ptr = cells; *ptr; i++, ptr++)
      {
!         if (opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              troff_ms_escaped_print(my_cell, fout);
              free(my_cell);
          }
***************
*** 1315,1321 ****
  print_troff_ms_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   char *opt_numericsep, unsigned short int opt_border,
                    FILE *fout)
  {
      unsigned int col_count = 0;
--- 1308,1314 ----
  print_troff_ms_vertical(const char *title, const char *const *headers,
                    const char *const *cells, const char *const *footers,
                    const char *opt_align, bool opt_tuples_only,
!                   bool opt_numericsep, unsigned short int opt_border,
                    FILE *fout)
  {
      unsigned int col_count = 0;
***************
*** 1345,1356 ****
          if (opt_tuples_only)
           fputs("c l;\n", fout);

-
      /* count columns */
      for (ptr = headers; *ptr; ptr++)
          col_count++;

-
      /* print records */
      for (i = 0, ptr = cells; *ptr; i++, ptr++)
      {
--- 1338,1347 ----
***************
*** 1390,1402 ****

          troff_ms_escaped_print(headers[i % col_count], fout);
          fputc('\t', fout);
!         if (strlen(*ptr) != 0 &&
!             opt_numericsep != NULL && strlen(opt_numericsep) > 0)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell, opt_numericsep);
              troff_ms_escaped_print(my_cell, fout);
              free(my_cell);
          }
--- 1381,1392 ----

          troff_ms_escaped_print(headers[i % col_count], fout);
          fputc('\t', fout);
!         if (opt_numericsep)
          {
              char *my_cell = pg_local_malloc(len_with_numericsep(*ptr) + 1);

              strcpy(my_cell, *ptr);
!             format_numericsep(my_cell);
              troff_ms_escaped_print(my_cell, fout);
              free(my_cell);
          }
***************
*** 1714,1717 ****
  }


! /* the end */
--- 1704,1729 ----
  }


! void
! setDecimalLocale(void)
! {
!     struct lconv *extlconv;
!
!     extlconv = localeconv();
!
!     /* These are treated as single-byte strings in the code */
!     if (*extlconv->decimal_point)
!         decimal_point = strdup(extlconv->decimal_point);
!     else
!         decimal_point = ".";    /* SQL output standard */
!     if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
!         grouping = strdup(extlconv->grouping);
!     else
!         grouping = "3";        /* most common */
!     if (*extlconv->thousands_sep)
!         thousands_sep = strdup(extlconv->thousands_sep);
!     else
!         thousands_sep = ",";    /* matches SQL standard decimal marker */
! }
!
!
Index: src/bin/psql/print.h
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/print.h,v
retrieving revision 1.26
retrieving revision 1.27
diff -c -r1.26 -r1.27
*** src/bin/psql/print.h    10 Jul 2005 03:46:13 -0000    1.26
--- src/bin/psql/print.h    14 Jul 2005 08:42:37 -0000    1.27
***************
*** 3,9 ****
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.26 2005/07/10 03:46:13 momjian Exp $
   */
  #ifndef PRINT_H
  #define PRINT_H
--- 3,9 ----
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.27 2005/07/14 08:42:37 momjian Exp $
   */
  #ifndef PRINT_H
  #define PRINT_H
***************
*** 40,46 ****
      char       *fieldSep;        /* field separator for unaligned text mode */
      char       *recordSep;        /* record separator for unaligned text
                                   * mode */
!     char       *numericSep;        /* numeric units separator */
      char       *tableAttr;        /* attributes for HTML <table ...> */
      int            encoding;        /* character encoding */
      bool        normal_query;    /* are we presenting the results of a
--- 40,47 ----
      char       *fieldSep;        /* field separator for unaligned text mode */
      char       *recordSep;        /* record separator for unaligned text
                                   * mode */
!     bool        numericSep;        /* locale-aware numeric units separator and
!                                  *  decimal marker */
      char       *tableAttr;        /* attributes for HTML <table ...> */
      int            encoding;        /* character encoding */
      bool        normal_query;    /* are we presenting the results of a
***************
*** 86,91 ****
--- 87,94 ----
  void        printQuery(const PGresult *result, const printQueryOpt *opt,
                         FILE *fout, FILE *flog);

+ void    setDecimalLocale(void);
+
  #ifndef __CYGWIN__
  #define DEFAULT_PAGER "more"
  #else
Index: src/bin/psql/startup.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.118
retrieving revision 1.119
diff -c -r1.118 -r1.119
*** src/bin/psql/startup.c    21 Jun 2005 04:02:33 -0000    1.118
--- src/bin/psql/startup.c    14 Jul 2005 08:42:37 -0000    1.119
***************
*** 3,9 ****
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.118 2005/06/21 04:02:33 tgl Exp $
   */
  #include "postgres_fe.h"

--- 3,9 ----
   *
   * Copyright (c) 2000-2005, PostgreSQL Global Development Group
   *
!  * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.119 2005/07/14 08:42:37 momjian Exp $
   */
  #include "postgres_fe.h"

***************
*** 130,135 ****
--- 130,136 ----
      setvbuf(stderr, NULL, _IONBF, 0);
      setup_win32_locks();
  #endif
+     setDecimalLocale();
      pset.cur_cmd_source = stdin;
      pset.cur_cmd_interactive = false;
      pset.encoding = PQenv2encoding();

pgsql-patches by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: Final cleanup of SQL:1999 references
Next
From: Koichi Suzuki
Date:
Subject: Re: A couple of patches for PostgreSQL 64bit support