This is a discussion on Re: thousands comma numeric formatting in psql within the Pgsql Patches forums, part of the PostgreSQL category; --> Eugen Nedelcu wrote: > Regarding locale aproach, it is trivial to replace langinfo with > localeconv: > > struct ...
| |||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| 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(); ---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match |
| |||
| Am Donnerstag, 14. Juli 2005 10:48 schrieb Bruce Momjian: > 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. Then the name "numericsep" doesn't seem to make much sense anymore. -- Peter Eisentraut http://developer.postgresql.org/~petere/ ---------------------------(end of broadcast)--------------------------- TIP 5: don't forget to increase your free space map settings |
| |||
| Bruce Momjian <pgman@candle.pha.pa.us> writes: > OK, I have applied the following patch to make numerisep a boolean, "numericsep" is no longer even remotely reasonable as a name for the parameter. Something like "numeric_use_locale" would be appropriate (but probably too wordy). > 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. I think you have to assume they could be strings. regards, tom lane ---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives? http://archives.postgresql.org |
| |||
| Tom Lane wrote: > > 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. > > I think you have to assume they could be strings. OK, the following applied patch enables multi-byte locale strings for numericsep. I also reorganized the code a little. -- 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: src/bin/psql/print.c ================================================== ================= RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.68 diff -c -c -r1.68 print.c *** src/bin/psql/print.c 14 Jul 2005 15:54:21 -0000 1.68 --- src/bin/psql/print.c 14 Jul 2005 20:54:05 -0000 *************** *** 50,125 **** } static int ! num_numericseps(const char *my_str) { ! int old_len, dec_len, int_len; ! int groupdigits = atoi(grouping); if (my_str[0] == '-') my_str++; ! old_len = strlen(my_str); ! 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 len_with_numericsep(const char *my_str) { ! return strlen(my_str) + num_numericseps(my_str); } 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++; ! ! 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 */ ! new_str = pg_local_malloc(new_len + 1); for (i=0, j=0; ; i++, j++) { ! /* 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); break; } ! /* end of string */ if (my_str[i] == '\0') { new_str[j] = '\0'; break; } ! /* 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]; } --- 50,128 ---- } static int ! integer_digits(const char *my_str) { ! int frac_len; if (my_str[0] == '-') my_str++; ! frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0; ! ! return strlen(my_str) - frac_len; ! } ! ! static int ! len_numericseps(const char *my_str) ! { ! int int_len = integer_digits(my_str), sep_len; ! int groupdigits = atoi(grouping); if (int_len % groupdigits != 0) ! sep_len = int_len / groupdigits; else ! sep_len = int_len / groupdigits - 1; /* no leading separator */ ! ! return sep_len * strlen(thousands_sep) - ! strlen(".") + strlen(decimal_point); } static int len_with_numericsep(const char *my_str) { ! return strlen(my_str) + len_numericseps(my_str); } static void format_numericsep(char *my_str) { ! int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); + char *new_str; if (my_str[0] == '-') my_str++; ! ! new_str = pg_local_malloc(len_numericseps(my_str) + 1); ! leading_digits = (int_len % groupdigits != 0) ? ! int_len % groupdigits : groupdigits; for (i=0, j=0; ; i++, j++) { ! /* Hit decimal point? */ if (my_str[i] == '.') { ! strcpy(&new_str[j], decimal_point); ! j += strlen(decimal_point); ! /* add fractional part */ ! strcpy(&new_str[j], &my_str[i] + 1); break; } ! /* End of string? */ if (my_str[i] == '\0') { new_str[j] = '\0'; break; } ! /* Add separator? */ ! if (i != 0 && (i - leading_digits) % groupdigits == 0) ! { ! strcpy(&new_str[j], thousands_sep); ! j += strlen(thousands_sep); ! } new_str[j] = my_str[i]; } *************** *** 396,402 **** int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = num_numericseps(*ptr); else numericseps = 0; --- 399,405 ---- int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else numericseps = 0; *************** *** 613,619 **** int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = num_numericseps(*ptr); else numericseps = 0; --- 616,622 ---- int numericseps; if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else numericseps = 0; *************** *** 1711,1717 **** extlconv = localeconv(); - /* These are treated as single-byte strings in the code */ if (*extlconv->decimal_point) decimal_point = strdup(extlconv->decimal_point); else --- 1714,1719 ---- ---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq |
| |||
| Tom Lane wrote: > Bruce Momjian <pgman@candle.pha.pa.us> writes: > > OK, I have applied the following patch to make numerisep a boolean, > > "numericsep" is no longer even remotely reasonable as a name for the > parameter. Something like "numeric_use_locale" would be appropriate > (but probably too wordy). Basically, with the C locale, it changes 1000.00 to 1,000.00, so indeed is does add a numeric separator. For a European locale, 1000.00 becomes 1.000,00. so in that case is changes both the separator and decimal marker. We don't currently have a way to display 1000.00 as 1000,00. I am thinking of calling it just numericlocale. -- 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 ---------------------------(end of broadcast)--------------------------- TIP 6: explain analyze is your friend |
| |||
| The new code is broken. Please test it with resonably large tables. Do not test it with querys like: select -132323435.34343; If I use a query like: select * from my_table limit 100; I can't see anything on my screen until I hit CTRL^C If I use a pager (\pset pager less) strange things happened: I can't see anything on my screen, hit CTRL^C, then quit psql, reset xterm, then less is still running and eat lots of memory and cpu. Best Regards, Eugen ---------------------------(end of broadcast)--------------------------- TIP 2: Don't 'kill -9' the postmaster |
| |||
| Eugen Nedelcu wrote: > The new code is broken. Please test it with resonably large tables. > Do not test it with querys like: select -132323435.34343; > > If I use a query like: > > select * from my_table limit 100; > > I can't see anything on my screen until I hit CTRL^C > > If I use a pager (\pset pager less) strange things happened: > I can't see anything on my screen, hit CTRL^C, then quit psql, > reset xterm, then less is still running and eat lots of memory and > cpu. I did: SELECT random() * 1000000000 INTO test FROM generate_series(1,100000); \pset numericsep SELECT * FROM test; and it seemed to work fine. Did you do a complete rebuild/install? -- 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 ---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives? http://archives.postgresql.org |
| |||
| In function format_numericsep() you have: new_str = pg_local_malloc(len_numericseps(my_str) + 1), instead of: new_str = pg_local_malloc(len_with_numericsep(my_str) + 1) Another bug is in function len_numericseps(). This apear for querys like: select NULL::numeric; or select * from table_with_numeric_fields; where one of numeric fields have null values; For such values, len_numericseps returns -1, instead of 0. Instead of: if (int_len % groupdigits != 0) sep_len = int_len / groupdigits; else sep_len = int_len / groupdigits - 1; you must have: if (int_len % groupdigits != 0 || int_len == 0) sep_len = int_len / groupdigits; else sep_len = int_len / groupdigits - 1; Best Regards, Eugen ---------------------------(end of broadcast)--------------------------- TIP 2: Don't 'kill -9' the postmaster |
| |||
| Thanks, fix attached and applied --------------------------------------------------------------------------- Eugen Nedelcu wrote: > In function format_numericsep() you have: > > new_str = pg_local_malloc(len_numericseps(my_str) + 1), > > instead of: > > new_str = pg_local_malloc(len_with_numericsep(my_str) + 1) > > Another bug is in function len_numericseps(). This apear for querys > like: > > select NULL::numeric; or > select * from table_with_numeric_fields; where one of numeric fields > have null values; > > For such values, len_numericseps returns -1, instead of 0. > > Instead of: > > if (int_len % groupdigits != 0) > sep_len = int_len / groupdigits; > else > sep_len = int_len / groupdigits - 1; > > you must have: > > if (int_len % groupdigits != 0 || int_len == 0) > sep_len = int_len / groupdigits; > else > sep_len = int_len / groupdigits - 1; > > Best Regards, > Eugen > -- 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: src/bin/psql/print.c ================================================== ================= RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.69 diff -c -c -r1.69 print.c *** src/bin/psql/print.c 14 Jul 2005 21:12:41 -0000 1.69 --- src/bin/psql/print.c 18 Jul 2005 17:16:37 -0000 *************** *** 68,77 **** int int_len = integer_digits(my_str), sep_len; int groupdigits = atoi(grouping); ! if (int_len % groupdigits != 0) ! sep_len = int_len / groupdigits; else ! sep_len = int_len / groupdigits - 1; /* no leading separator */ return sep_len * strlen(thousands_sep) - strlen(".") + strlen(decimal_point); --- 68,78 ---- int int_len = integer_digits(my_str), sep_len; int groupdigits = atoi(grouping); ! if (int_len == 0) ! sep_len = 0; else ! /* Don't count a leading separator */ ! sep_len = int_len / groupdigits - (int_len % groupdigits == 0); return sep_len * strlen(thousands_sep) - strlen(".") + strlen(decimal_point); *************** *** 93,99 **** if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_numericseps(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; --- 94,100 ---- if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_with_numericsep(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; ---------------------------(end of broadcast)--------------------------- TIP 4: Have you searched our list archives? http://archives.postgresql.org |
| ||||
| Bruce Momjian wrote: > Tom Lane wrote: > > Bruce Momjian <pgman@candle.pha.pa.us> writes: > > > OK, I have applied the following patch to make numerisep a boolean, > > > > "numericsep" is no longer even remotely reasonable as a name for the > > parameter. Something like "numeric_use_locale" would be appropriate > > (but probably too wordy). > > Basically, with the C locale, it changes 1000.00 to 1,000.00, so indeed > is does add a numeric separator. For a European locale, 1000.00 becomes > 1.000,00. so in that case is changes both the separator and decimal > marker. We don't currently have a way to display 1000.00 as 1000,00. > > I am thinking of calling it just numericlocale. Done, and attached. -- 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.147 diff -c -c -r1.147 psql-ref.sgml *** doc/src/sgml/ref/psql-ref.sgml 14 Jul 2005 08:42:36 -0000 1.147 --- doc/src/sgml/ref/psql-ref.sgml 18 Jul 2005 19:38:56 -0000 *************** *** 1493,1499 **** </varlistentry> <varlistentry> ! <term><literal>numericsep</literal></term> <listitem> <para> Toggles the display of a locale-aware character to separate groups --- 1493,1499 ---- </varlistentry> <varlistentry> ! <term><literal>numericlocale</literal></term> <listitem> <para> Toggles the display of a locale-aware character to separate groups Index: src/bin/psql/command.c ================================================== ================= RCS file: /cvsroot/pgsql/src/bin/psql/command.c,v retrieving revision 1.149 diff -c -c -r1.149 command.c *** src/bin/psql/command.c 14 Jul 2005 08:42:37 -0000 1.149 --- src/bin/psql/command.c 18 Jul 2005 19:39:02 -0000 *************** *** 1420,1435 **** : _("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.")); } } --- 1420,1435 ---- : _("Expanded display is off.\n")); } ! /* locale-aware numeric output */ ! else if (strcmp(param, "numericlocale") == 0) { ! popt->topt.numericLocale = !popt->topt.numericLocale; if (!quiet) { ! if (popt->topt.numericLocale) ! puts(_("Showing locale-adjusted numeric output.")); else ! puts(_("Locale-adjusted numeric output is off.")); } } Index: src/bin/psql/help.c ================================================== ================= RCS file: /cvsroot/pgsql/src/bin/psql/help.c,v retrieving revision 1.104 diff -c -c -r1.104 help.c *** src/bin/psql/help.c 10 Jul 2005 03:46:13 -0000 1.104 --- src/bin/psql/help.c 18 Jul 2005 19:39:02 -0000 *************** *** 239,245 **** fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericsep|recordsep|tuples_only|title|tableattr|p ager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); --- 239,245 ---- fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" ! " numericlocale|recordsep|tuples_only|title|tableatt r|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n")); Index: src/bin/psql/print.c ================================================== ================= RCS file: /cvsroot/pgsql/src/bin/psql/print.c,v retrieving revision 1.71 diff -c -c -r1.71 print.c *** src/bin/psql/print.c 18 Jul 2005 19:27:37 -0000 1.71 --- src/bin/psql/print.c 18 Jul 2005 19:39:03 -0000 *************** *** 62,69 **** return strlen(my_str) - frac_len; } static int ! len_numericseps(const char *my_str) { int int_len = integer_digits(my_str), len = 0; int groupdigits = atoi(grouping); --- 62,70 ---- return strlen(my_str) - frac_len; } + /* Return additional length required for locale-aware numeric output */ static int ! additional_numeric_locale_len(const char *my_str) { int int_len = integer_digits(my_str), len = 0; int groupdigits = atoi(grouping); *************** *** 80,92 **** } static int ! len_with_numericsep(const char *my_str) { ! return strlen(my_str) + len_numericseps(my_str); } static void ! format_numericsep(char *my_str) { int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); --- 81,93 ---- } static int ! strlen_with_numeric_locale(const char *my_str) { ! return strlen(my_str) + additional_numeric_locale_len(my_str); } static void ! format_numeric_locale(char *my_str) { int i, j, int_len = integer_digits(my_str), leading_digits; int groupdigits = atoi(grouping); *************** *** 95,101 **** if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(len_with_numericsep(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; --- 96,102 ---- if (my_str[0] == '-') my_str++; ! new_str = pg_local_malloc(strlen_with_numeric_locale(my_str) + 1); leading_digits = (int_len % groupdigits != 0) ? int_len % groupdigits : groupdigits; *************** *** 143,149 **** 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; --- 144,150 ---- 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_numeric_locale, FILE *fout) { unsigned int col_count = 0; unsigned int i; *************** *** 182,193 **** 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); } --- 183,194 ---- fputs(opt_recordsep, fout); need_recordsep = false; } ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 227,233 **** 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; --- 228,234 ---- 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_numeric_locale, FILE *fout) { unsigned int col_count = 0; unsigned int i; *************** *** 258,269 **** 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); } --- 259,270 ---- fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fputs(my_cell, fout); free(my_cell); } *************** *** 331,337 **** 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) { --- 332,338 ---- 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_numeric_locale, unsigned short int opt_border, int encoding, FILE *fout) { *************** *** 398,411 **** for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else ! numericseps = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; --- 399,412 ---- for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); else ! numeric_locale_len = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numeric_locale_len; if (tmp > widths[i % col_count]) widths[i % col_count] = tmp; cell_w[i] = tmp; *************** *** 485,496 **** /* 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); } --- 486,497 ---- /* content */ if (opt_align[i % col_count] == 'r') { ! if (opt_numeric_locale) { char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", my_cell); free(my_cell); } *************** *** 552,558 **** 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; --- 553,559 ---- 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_numeric_locale, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; *************** *** 615,628 **** /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numericseps; ! if (opt_align[i % col_count] == 'r' && opt_numericsep) ! numericseps = len_numericseps(*ptr); else ! numericseps = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; --- 616,629 ---- /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { ! int numeric_locale_len; ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! numeric_locale_len = additional_numeric_locale_len(*ptr); else ! numeric_locale_len = 0; ! tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numeric_locale_len; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; *************** *** 700,707 **** 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 --- 701,708 ---- char *my_cell = pg_local_malloc(cell_w[i] + 1); strcpy(my_cell, *ptr); ! if (opt_align[i % col_count] == 'r' && opt_numeric_locale) ! format_numeric_locale(my_cell); if (opt_border < 2) puts(my_cell); else *************** *** 788,794 **** 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; --- 789,795 ---- 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_numeric_locale, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; *************** *** 834,845 **** /* 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); } --- 835,846 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 875,881 **** 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; --- 876,882 ---- 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_numeric_locale, unsigned short int opt_border, const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; *************** *** 919,930 **** /* 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); } --- 920,931 ---- /* is string only whitespace? */ if ((*ptr)[strspn(*ptr, " \t")] == '\0') fputs(" ", fout); ! else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); html_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1000,1006 **** 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; --- 1001,1007 ---- 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_numeric_locale, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1061,1072 **** /* 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); } --- 1062,1073 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(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, ! bool opt_numericsep, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; --- 1104,1110 ---- 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_numeric_locale, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1174,1185 **** 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); } --- 1175,1186 ---- if (footers && !opt_tuples_only) for (ptr = footers; *ptr; ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); latex_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1220,1226 **** 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; --- 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, ! bool opt_numeric_locale, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1274,1285 **** /* 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); } --- 1275,1286 ---- /* print cells */ for (i = 0, ptr = cells; *ptr; i++, ptr++) { ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1313,1319 **** 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; --- 1314,1320 ---- 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_numeric_locale, unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; *************** *** 1386,1397 **** 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); } --- 1387,1398 ---- troff_ms_escaped_print(headers[i % col_count], fout); fputc('\t', fout); ! if (opt_numeric_locale) { ! char *my_cell = pg_local_malloc(strlen_with_numeric_locale(*ptr) + 1); strcpy(my_cell, *ptr); ! format_numeric_locale(my_cell); troff_ms_escaped_print(my_cell, fout); free(my_cell); } *************** *** 1533,1539 **** /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericSep, border, opt->encoding, flog); switch (opt->format) { --- 1534,1540 ---- /* print the stuff */ if (flog) ! print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericLocale, border, opt->encoding, flog); switch (opt->format) { *************** *** 1541,1590 **** if (use_expanded) print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); else print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericSep, output); break; case PRINT_ALIGNED: if (use_expanded) print_aligned_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); else print_aligned_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->encoding, output); break; case PRINT_HTML: if (use_expanded) print_html_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); else print_html_text(title, headers, cells, footers, ! align, opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); break; case PRINT_LATEX: if (use_expanded) print_latex_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); else print_latex_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); break; case PRINT_TROFF_MS: if (use_expanded) print_troff_ms_vertical(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); else print_troff_ms_text(title, headers, cells, footers, align, ! opt->tuples_only, opt->numericSep, border, output); break; default: --- 1542,1591 ---- if (use_expanded) print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericLocale, output); else print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, ! opt->tuples_only, opt->numericLocale, output); break; case PRINT_ALIGNED: if (use_expanded) print_aligned_vertical(title, headers, cells, footers, align, |