diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 4162e51b50..9d76ff207e 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -1492,6 +1492,18 @@ lo_import 152801 + + numericsep + + + Specifies the character separator between groups of three digits + to the left of the decimal marker. The default is '' + (none). Setting this to a period also changes the decimal marker + to a comma. + + + + recordsep diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 870d631068..e6affb5354 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.146 2005/06/13 06:36:22 neilc Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.147 2005/07/10 03:46:13 momjian Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -838,7 +838,6 @@ exec_command(const char *cmd, else if (strcmp(cmd, "x") == 0) success = do_pset("expanded", NULL, &pset.popt, quiet); - /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) { @@ -1421,6 +1420,17 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) : _("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 */ else if (strcmp(param, "null") == 0) { diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 1bbe4acc43..1d24cffd02 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.103 2005/07/06 03:14:48 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.104 2005/07/10 03:46:13 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -239,7 +239,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\pset NAME [VALUE]\n" " set table output option\n" " (NAME := {format|border|expanded|fieldsep|footer|null|\n" - " recordsep|tuples_only|title|tableattr|pager})\n")); + " numericsep|recordsep|tuples_only|title|tableattr|pager})\n")); fprintf(output, _(" \\t show only rows (currently %s)\n"), ON(pset.popt.topt.tuples_only)); fprintf(output, _(" \\T [STRING] set HTML tag attributes, or unset if none\n")); diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 9f605b088e..5523b903cd 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.60 2005/06/14 22:15:57 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.61 2005/07/10 03:46:13 momjian Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -29,20 +29,107 @@ #include "mbprint.h" +static int +num_numericseps(const char *my_str) +{ + int old_len, dec_len, int_len; + + 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 % 3 != 0) + return int_len / 3; + else + return int_len / 3 - 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, 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++; + + 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 */ + + new_str = malloc(new_len); + if (!new_str) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + + for (i=0, j=0; ; i++, j++) + { + /* 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); + 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 : 3)) % 3 == 0) + new_str[j++] = *numericsep; + + new_str[j] = my_str[i]; + } + + strcpy(my_str, new_str); + free(new_str); +} + /*************************/ /* Unaligned text */ /*************************/ static void -print_unaligned_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones, - FILE *fout) +print_unaligned_text(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, const char *opt_fieldsep, + const char *opt_recordsep, bool opt_barebones, + char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; bool need_recordsep = false; if (!opt_fieldsep) @@ -77,7 +164,24 @@ print_unaligned_text(const char *title, const char *const * headers, fputs(opt_recordsep, fout); need_recordsep = false; } - fputs(*ptr, fout); + if ((opt_align[i % col_count] == 'r') && strlen(*ptr) > 0 && + opt_numericsep != NULL && strlen(opt_numericsep) > 0) + { + char *my_cell = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + fputs(my_cell, fout); + free(my_cell); + } + else + fputs(*ptr, fout); + if ((i + 1) % col_count) fputs(opt_fieldsep, fout); else @@ -107,14 +211,15 @@ print_unaligned_text(const char *title, const char *const * headers, static void -print_unaligned_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones, - FILE *fout) +print_unaligned_vertical(const char *title, const char *const *headers, + const char *const *cells, + const char *const *footers, const char *opt_align, + const char *opt_fieldsep, const char *opt_recordsep, + bool opt_barebones, char *opt_numericsep, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; if (!opt_fieldsep) opt_fieldsep = ""; @@ -141,7 +246,23 @@ print_unaligned_vertical(const char *title, const char *const * headers, fputs(headers[i % col_count], fout); fputs(opt_fieldsep, fout); - fputs(*ptr, fout); + if ((opt_align[i % col_count] == 'r') && strlen(*ptr) != 0 && + opt_numericsep != NULL && strlen(opt_numericsep) > 0) + { + char *my_cell = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + fputs(my_cell, fout); + free(my_cell); + } + else + fputs(*ptr, fout); } /* print footers */ @@ -202,9 +323,9 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, 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_barebones, +print_aligned_text(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { @@ -216,7 +337,7 @@ print_aligned_text(const char *title, const char *const * headers, tmp; unsigned int *widths, total_w; - const char *const * ptr; + const char *const *ptr; /* count columns */ for (ptr = headers; *ptr; ptr++) @@ -271,7 +392,15 @@ print_aligned_text(const char *title, const char *const * headers, for (i = 0, ptr = cells; *ptr; ptr++, i++) { - tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding); + 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; + + 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; @@ -351,8 +480,22 @@ print_aligned_text(const char *title, const char *const * headers, /* content */ if (opt_align[i % col_count] == 'r') { - fprintf(fout, "%*s%s", - widths[i % col_count] - cell_w[i], "", cells[i]); + if (strlen(*ptr) > 0 && opt_numericsep != NULL && strlen(opt_numericsep) > 0) + { + char *my_cell = malloc(cell_w[i]); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + 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); + } + else + fprintf(fout, "%*s%s", widths[i % col_count] - cell_w[i], "", *ptr); } else { @@ -406,14 +549,15 @@ print_aligned_text(const char *title, const char *const * headers, static void -print_aligned_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, - bool opt_barebones, unsigned short int opt_border, +print_aligned_vertical(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, + char *opt_numericsep, unsigned short int opt_border, int encoding, FILE *fout) { unsigned int col_count = 0; unsigned int record = 1; - const char *const * ptr; + const char *const *ptr; unsigned int i, tmp = 0, hwidth = 0, @@ -471,7 +615,15 @@ print_aligned_vertical(const char *title, const char *const * headers, /* find longest data cell */ for (i = 0, ptr = cells; *ptr; ptr++, i++) { - tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding); + 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; + + tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding) + numericseps; if (tmp > dwidth) dwidth = tmp; cell_w[i] = tmp; @@ -556,10 +708,24 @@ print_aligned_vertical(const char *title, const char *const * headers, else fputs(" ", fout); - if (opt_border < 2) - fprintf(fout, "%s\n", *ptr); - else - fprintf(fout, "%-s%*s |\n", *ptr, dwidth - cell_w[i], ""); + { + char *my_cell = malloc(cell_w[i]); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + 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 + fprintf(fout, "%-s%*s |\n", my_cell, dwidth - cell_w[i], ""); + free(my_cell); + } } if (opt_border == 2) @@ -637,15 +803,15 @@ html_escaped_print(const char *in, FILE *fout) static void -print_html_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, -const char *opt_align, bool opt_barebones, unsigned short int opt_border, - const char *opt_table_attr, - FILE *fout) +print_html_text(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, + char *opt_numericsep, unsigned short int opt_border, + const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; fprintf(fout, "
\n", fout); fprintf(fout, " \n", fout); if ((i + 1) % col_count == 0) @@ -714,16 +896,16 @@ const char *opt_align, bool opt_barebones, unsigned short int opt_border, static void -print_html_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, -const char *opt_align, bool opt_barebones, unsigned short int opt_border, - const char *opt_table_attr, - FILE *fout) +print_html_vertical(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, + char *opt_numericsep, unsigned short int opt_border, + const char *opt_table_attr, FILE *fout) { unsigned int col_count = 0; unsigned int i; unsigned int record = 1; - const char *const * ptr; + const char *const *ptr; fprintf(fout, "
", opt_align[(i) % col_count] == 'r' ? "right" : "left"); - if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only - * whitespace? */ + /* 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 = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + html_escaped_print(my_cell, fout); + free(my_cell); + } else html_escaped_print(*ptr, fout); + fputs("
\n", fout); fprintf(fout, " \n \n", fout); } @@ -829,14 +1027,14 @@ latex_escaped_print(const char *in, FILE *fout) static void -print_latex_text(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, -const char *opt_align, bool opt_barebones, unsigned short int opt_border, - FILE *fout) +print_latex_text(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, + unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; /* print title */ @@ -921,14 +1119,14 @@ const char *opt_align, bool opt_barebones, unsigned short int opt_border, static void -print_latex_vertical(const char *title, const char *const * headers, - const char *const * cells, const char *const * footers, -const char *opt_align, bool opt_barebones, unsigned short int opt_border, - FILE *fout) +print_latex_vertical(const char *title, const char *const *headers, + const char *const *cells, const char *const *footers, + const char *opt_align, bool opt_barebones, + unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; unsigned int record = 1; (void) opt_align; /* currently unused parameter */ @@ -1027,14 +1225,14 @@ troff_ms_escaped_print(const char *in, FILE *fout) static void -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_barebones, unsigned short int opt_border, - FILE *fout) +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_barebones, + unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; /* print title */ @@ -1111,14 +1309,14 @@ const char *opt_align, bool opt_barebones, unsigned short int opt_border, static void -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_barebones, unsigned short int opt_border, - FILE *fout) +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_barebones, + unsigned short int opt_border, FILE *fout) { unsigned int col_count = 0; unsigned int i; - const char *const * ptr; + const char *const *ptr; unsigned int record = 1; unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ @@ -1263,9 +1461,9 @@ PageOutput(int lines, unsigned short int pager) void printTable(const char *title, - const char *const * headers, - const char *const * cells, - const char *const * footers, + const char *const *headers, + const char *const *cells, + const char *const *footers, const char *align, const printTableOpt *opt, FILE *fout, FILE *flog) { @@ -1298,7 +1496,7 @@ printTable(const char *title, int col_count = 0, row_count = 0, lines; - const char *const * ptr; + const char *const *ptr; /* rough estimate of columns and rows */ if (headers) @@ -1325,38 +1523,38 @@ printTable(const char *title, /* print the stuff */ if (flog) - print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, border, opt->encoding, flog); + print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, opt->numericSep, border, opt->encoding, flog); switch (opt->format) { case PRINT_UNALIGNED: if (use_expanded) - print_unaligned_vertical(title, headers, cells, footers, + print_unaligned_vertical(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, - opt->tuples_only, output); + opt->tuples_only, opt->numericSep, output); else - print_unaligned_text(title, headers, cells, footers, + print_unaligned_text(title, headers, cells, footers, align, opt->fieldSep, opt->recordSep, - opt->tuples_only, output); + opt->tuples_only, opt->numericSep, output); break; case PRINT_ALIGNED: if (use_expanded) - print_aligned_vertical(title, headers, cells, footers, - opt->tuples_only, border, + 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, + 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, + 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, border, + align, opt->tuples_only, opt->numericSep, border, opt->tableAttr, output); break; case PRINT_LATEX: diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 7dcc2657f1..f64f8ba0f8 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.25 2005/06/14 02:57:41 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.26 2005/07/10 03:46:13 momjian Exp $ */ #ifndef PRINT_H #define PRINT_H @@ -40,6 +40,7 @@ typedef struct _printTableOpt 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
", opt_align[i % col_count] == 'r' ? "right" : "left"); - if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only - * whitespace? */ + /* 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 = malloc(len_with_numericsep(*ptr)); + + if (!my_cell) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + strcpy(my_cell, *ptr); + format_numericsep(my_cell, opt_numericsep); + html_escaped_print(my_cell, fout); + free(my_cell); + } else html_escaped_print(*ptr, fout); + fputs("
*/ int encoding; /* character encoding */ bool normal_query; /* are we presenting the results of a