On Fri, Oct 02, 2009 at 05:34:16PM -0700, Brad T. Sliger wrote: > On Friday 02 October 2009 04:21:35 Roger Leigh wrote: > > I have attached a patch which implements the feature as a pset > > variable. This also slightly simplifies some of the patch since > > the table style is passed to functions directly in printTableContent > > rather than separately. The psql option '-P tablestyle=ascii' is > > passed to psql in pg_regress_main.c which means the testsuite doesn't > > fail any more. The option is documented in the psql docs, and is > > also tab-completed. Users can just put '\pset tablestyle ascii' in > > their .psqlrc if they want the old format in a UTF-8 locale. > > I looked at psql-utf8-table-5.patch.
Many thanks for taking the time to do this. I've attached a followup patch which addresses your point below: > Lint(1) says there is an extra trailing ',' in src/bin/psql/print.h. in > 'typedef enum printTextRule'. The addition to > src/bin/psql/command.c could use a comment, like adjacent code. Fixed. > 'ASCII' and 'UTF8' may need <acronym></acronym> tags in > doc/src/sgml/ref/psql-ref.sgml, like adjacent > code. I'm not sure someone who hasn't seen this patch in action would > immediately know what it does from the > documentation. `gmake html` works without the patch, but fails with the > patch: Also fixed. I also added some additional explanation of the option which hopefully makes its purpose more obvious. The <acronym> tag isn't used for the itemised option list names, but is used in the descriptive text; I can also add it there if appropriate. It's likely that "tablestyle" could well be named better. "format" is already used, but if there's a more intuitive name that fits better, I'm happy to change it. > openjade:ref/psql-ref.sgml:1692:15:E: document type does not allow > element "TERM" here; assuming > missing "VARLISTENTRY" start-tag Also fixed. > After the patch, `\pset format wrapped` produces '\pset: unknown > option: format'. I saw this in interactive psql > and from .psqlrc. I think this can be fixed by changing the addition to > src/bin/psql/command.c from an 'if' clause to > an 'else if' clause. Oops, yes. Sorry about that hiccup. I've also fixed this. Regards, Roger -- .''`. Roger Leigh : :' : Debian GNU/Linux http://people.debian.org/~rleigh/ `. `' Printing on GNU/Linux? http://gutenprint.sourceforge.net/ `- GPG Public Key: 0x25BFB848 Please GPG sign your mail.
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 85e9375..6f1bdc7 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1690,6 +1690,54 @@ lo_import 152801 </varlistentry> <varlistentry> + <term><literal>tablestyle</literal></term> + <listitem> + <para> + Sets the style of text table output to one + of <literal>ascii</literal>, or <literal>utf8</literal>. + Unique abbreviations are allowed. (That would mean one + letter is enough.) <literal>utf8</literal> will be selected + by default if supported by your locale, + otherwise <literal>ascii</literal> will be used. + </para> + + <para> + Query result tables are displayed as text for some output + formats (<literal>unaligned</literal>, + <literal>aligned</literal>, and <literal>wrapped</literal> + formats). The tables are drawn using characters of the + user's locale character set. By + default, <acronym>ASCII</acronym> characters will be used, + which will display correctly in all locales. However, if + the user is using a locale with a <acronym>UTF-8</acronym> + character set, the default will be to + use <acronym>UTF-8</acronym> box drawing characters in place + of ASCII punctuation to display more readable tables. + </para> + + <para> + This option is useful for overriding the default table + style, for example to force the use of + only <acronym>ASCII</acronym> characters when extended + character sets such as <acronym>UTF-8</acronym> are + inappropriate. This might be the case if preserving output + compatibility with older psql versions is important (prior + to 8.5.0). + </para> + + <para> + <quote>UTF8</quote> use Unicode <acronym>UTF-8</acronym> box + drawing characters. + </para> + + <para> + <quote>ASCII</quote> use plain <acronym>ASCII</acronym> characters. + </para> + + </listitem> + </varlistentry> + + <varlistentry> <term><literal>columns</literal></term> <listitem> <para> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index da57eb4..31631cf 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1772,6 +1772,25 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) printf(_("Output format is %s.\n"), _align2string(popt->topt.format)); } + /* set table style */ + else if (strcmp(param, "tablestyle") == 0) + { + if (!value) + ; + else if (pg_strncasecmp("ascii", value, vallen) == 0) + popt->topt.table_style = &asciiformat; + else if (pg_strncasecmp("utf8", value, vallen) == 0) + popt->topt.table_style = &utf8format; + else + { + psql_error("\\pset: allowed table styles are ascii, utf8\n"); + return false; + } + + if (!quiet) + printf(_("Table style is %s.\n"), popt->topt.table_style->name); + } + /* set border style/width */ else if (strcmp(param, "border") == 0) { diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 7505cd4..0264bb1 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -356,38 +356,77 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout) /* Aligned text */ /********************/ +const printTextFormat asciiformat = +{ + "ascii", + { + { "-", "+", "+", "+" }, + { "-", "+", "+", "+" }, + { "-", "+", "+", "+" }, + { "", "|", "|", "|" } + }, + ":", + ";", + " " +}; + +const struct printTextFormat utf8format = +{ + "utf8", + { + /* ─, ┌, ┬, ┐ */ + { "\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220" }, + /* ─, ├, ┼, ┤ */ + { "\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244" }, + /* ─, └, ┴, ┘ */ + { "\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230" }, + /* N/A, │, │, │ */ + { "", "\342\224\202", "\342\224\202", "\342\224\202" } + }, + /* ╎ */ + "\342\225\216", + /* ┊ */ + "\342\224\212", + /* ╷ */ + "\342\225\267" +}; /* draw "line" */ static void _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths, - unsigned short border, FILE *fout) + unsigned short border, printTextRule pos, + const printTextFormat *format, + FILE *fout) { unsigned int i, j; + const printTextLineFormat *lformat = &format->lrule[pos]; + if (border == 1) - fputc('-', fout); + fputs(lformat->hrule, fout); else if (border == 2) - fputs("+-", fout); + fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); for (i = 0; i < ncolumns; i++) { for (j = 0; j < widths[i]; j++) - fputc('-', fout); + fputs(lformat->hrule, fout); if (i < ncolumns - 1) { if (border == 0) fputc(' ', fout); else - fputs("-+-", fout); + fprintf(fout, "%s%s%s", lformat->hrule, + lformat->midvrule, lformat->hrule); } } if (border == 2) - fputs("-+", fout); + fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); else if (border == 1) - fputc('-', fout); + fputs(lformat->hrule, fout); fputc('\n', fout); } @@ -403,6 +442,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) bool opt_numeric_locale = cont->opt->numericLocale; int encoding = cont->opt->encoding; unsigned short opt_border = cont->opt->border; + const printTextFormat *format = cont->opt->table_style; unsigned int col_count = 0, cell_count = 0; @@ -431,6 +471,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) int *bytes_output; /* Bytes output for column value */ int output_columns = 0; /* Width of interactive console */ bool is_pager = false; + const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; if (cancel_pressed) return; @@ -709,7 +750,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) int curr_nl_line; if (opt_border == 2) - _print_horizontal_line(col_count, width_wrap, opt_border, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_TOP, format, fout); for (i = 0; i < col_count; i++) pg_wcsformat((unsigned char *) cont->headers[i], @@ -722,7 +763,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) while (more_col_wrapping) { if (opt_border == 2) - fprintf(fout, "|%c", curr_nl_line ? '+' : ' '); + fprintf(fout, "%s%c", dformat->leftvrule, curr_nl_line ? '+' : ' '); else if (opt_border == 1) fputc(curr_nl_line ? '+' : ' ', fout); @@ -753,19 +794,20 @@ print_aligned_text(const printTableContent *cont, FILE *fout) if (opt_border == 0) fputc(curr_nl_line ? '+' : ' ', fout); else - fprintf(fout, " |%c", curr_nl_line ? '+' : ' '); + fprintf(fout, " %s%c", dformat->midvrule, curr_nl_line ? '+' : ' '); } } curr_nl_line++; if (opt_border == 2) - fputs(" |", fout); + fprintf(fout, " %s", + dformat->rightvrule); else if (opt_border == 1) fputc(' ', fout); fputc('\n', fout); } - _print_horizontal_line(col_count, width_wrap, opt_border, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_MIDDLE, format, fout); } } @@ -811,7 +853,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) /* left border */ if (opt_border == 2) - fputs("| ", fout); + fprintf(fout, "%s ", dformat->leftvrule); else if (opt_border == 1) fputc(' ', fout); @@ -884,22 +926,26 @@ print_aligned_text(const printTableContent *cont, FILE *fout) fputc(' ', fout); /* Next value is beyond past newlines? */ else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL) - fputs(" ", fout); + fprintf(fout, " %s ", + format->midvrule_blank); /* In wrapping of value? */ else if (bytes_output[j + 1] != 0) - fputs(" ; ", fout); + fprintf(fout, " %s ", + format->midvrule_wrap); /* After first newline value */ else if (curr_nl_line[j + 1] != 0) - fputs(" : ", fout); + fprintf(fout, " %s ", + format->midvrule_cont); + /* Ordinary line */ else - /* Ordinary line */ - fputs(" | ", fout); + fprintf(fout, " %s ", + dformat->midvrule); } } /* end-of-row border */ if (opt_border == 2) - fputs(" |", fout); + fprintf(fout, " %s", dformat->rightvrule); fputc('\n', fout); } while (more_lines); @@ -908,7 +954,7 @@ print_aligned_text(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) - _print_horizontal_line(col_count, width_wrap, opt_border, fout); + _print_horizontal_line(col_count, width_wrap, opt_border, PRINT_RULE_BOTTOM, format, fout); /* print footers */ if (cont->footers && !opt_tuples_only && !cancel_pressed) @@ -941,6 +987,62 @@ print_aligned_text(const printTableContent *cont, FILE *fout) ClosePager(fout); } +static inline void +print_aligned_vertical_line(const printTableContent *cont, + unsigned long record, + unsigned int hwidth, + unsigned int dwidth, + printTextRule pos, + FILE *fout) +{ + unsigned short opt_border = cont->opt->border; + unsigned int i; + int reclen = 0; + const printTextFormat *format = cont->opt->table_style; + const printTextLineFormat *lformat = &format->lrule[pos]; + + if (opt_border == 2) + fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule); + else if (opt_border == 1) + fputs(lformat->hrule, fout); + + if (record) + { + if (opt_border == 0) + reclen = fprintf(fout, "* Record %lu", record); + else + reclen = fprintf(fout, "[ RECORD %lu ]", record); + } + if (opt_border != 2) + reclen++; + if (reclen < 0) + reclen = 0; + for (i = reclen; i < hwidth; i++) + fputs(opt_border > 0 ? lformat->hrule : " ", fout); + reclen -= hwidth; + + if (opt_border > 0) + { + if (reclen-- <= 0) + fputs(lformat->hrule, fout); + if (reclen-- <= 0) + fputs(lformat->midvrule, fout); + if (reclen-- <= 0) + fputs(lformat->hrule, fout); + } + else + { + if (reclen-- <= 0) + fputc(' ', fout); + } + if (reclen < 0) + reclen = 0; + for (i = reclen; i < dwidth; i++) + fputs(opt_border > 0 ? lformat->hrule : " ", fout); + if (opt_border == 2) + fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule); + fputc('\n', fout); +} static void print_aligned_vertical(const printTableContent *cont, FILE *fout) @@ -948,6 +1050,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) bool opt_tuples_only = cont->opt->tuples_only; bool opt_numeric_locale = cont->opt->numericLocale; unsigned short opt_border = cont->opt->border; + const printTextFormat *format = cont->opt->table_style; int encoding = cont->opt->encoding; unsigned long record = cont->opt->prior_records + 1; const char *const * ptr; @@ -958,9 +1061,9 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) dheight = 1, hformatsize = 0, dformatsize = 0; - char *divider; struct lineptr *hlineptr, *dlineptr; + const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA]; if (cancel_pressed) return; @@ -1026,21 +1129,6 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) dlineptr->ptr = pg_local_malloc(dformatsize); hlineptr->ptr = pg_local_malloc(hformatsize); - /* make horizontal border */ - divider = pg_local_malloc(hwidth + dwidth + 10); - divider[0] = '\0'; - if (opt_border == 2) - strcat(divider, "+-"); - for (i = 0; i < hwidth; i++) - strcat(divider, opt_border > 0 ? "-" : " "); - if (opt_border > 0) - strcat(divider, "-+-"); - else - strcat(divider, " "); - for (i = 0; i < dwidth; i++) - strcat(divider, opt_border > 0 ? "-" : " "); - if (opt_border == 2) - strcat(divider, "-+"); if (cont->opt->start_table) { @@ -1052,40 +1140,25 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) /* print records */ for (i = 0, ptr = cont->cells; *ptr; i++, ptr++) { - int line_count, - dcomplete, - hcomplete; + int line_count, + dcomplete, + hcomplete; + printTextRule pos = PRINT_RULE_MIDDLE; + if (i == 0) + pos = PRINT_RULE_TOP; + else if (!(*(ptr+1))) + pos = PRINT_RULE_BOTTOM; + + if (cancel_pressed) + break; if (i % cont->ncolumns == 0) { - if (cancel_pressed) - break; - if (!opt_tuples_only) - { - char record_str[64]; - size_t record_str_len; - - if (opt_border == 0) - snprintf(record_str, 64, "* Record %lu", record++); - else - snprintf(record_str, 64, "[ RECORD %lu ]", record++); - record_str_len = strlen(record_str); - - if (record_str_len + opt_border > strlen(divider)) - fprintf(fout, "%.*s%s\n", opt_border, divider, record_str); - else - { - char *div_copy = pg_strdup(divider); - - strncpy(div_copy + opt_border, record_str, record_str_len); - fprintf(fout, "%s\n", div_copy); - free(div_copy); - } - } + if (!opt_tuples_only) + print_aligned_vertical_line(cont, record++, hwidth, dwidth, pos, fout); else if (i != 0 || !cont->opt->start_table || opt_border == 2) - fprintf(fout, "%s\n", divider); + print_aligned_vertical_line(cont, 0, hwidth, dwidth, pos, fout); } - /* Format the header */ pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns], strlen(cont->headers[i % cont->ncolumns]), @@ -1099,7 +1172,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) while (!dcomplete || !hcomplete) { if (opt_border == 2) - fputs("| ", fout); + fprintf(fout, "%s ", dformat->leftvrule); if (!hcomplete) { fprintf(fout, "%-s%*s", hlineptr[line_count].ptr, @@ -1112,9 +1185,13 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) fprintf(fout, "%*s", hwidth, ""); if (opt_border > 0) - fprintf(fout, " %c ", (line_count == 0) ? '|' : ':'); + { + fprintf(fout, " %s ", + (line_count == 0) ? + format->midvrule_cont : dformat->midvrule); + } else - fputs(" ", fout); + fputc(' ', fout); if (!dcomplete) { @@ -1125,8 +1202,8 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fprintf(fout, "%s\n", my_cell); else - fprintf(fout, "%-s%*s |\n", my_cell, - (int) (dwidth - strlen(my_cell)), ""); + fprintf(fout, "%-s%*s %s\n", my_cell, + (int) (dwidth - strlen(my_cell)), "", dformat->rightvrule); free(my_cell); } else @@ -1134,8 +1211,8 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fprintf(fout, "%s\n", dlineptr[line_count].ptr); else - fprintf(fout, "%-s%*s |\n", dlineptr[line_count].ptr, - dwidth - dlineptr[line_count].width, ""); + fprintf(fout, "%-s%*s %s\n", dlineptr[line_count].ptr, + dwidth - dlineptr[line_count].width, "", dformat->rightvrule); } if (!dlineptr[line_count + 1].ptr) @@ -1146,7 +1223,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (opt_border < 2) fputc('\n', fout); else - fprintf(fout, "%*s |\n", dwidth, ""); + fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule); } line_count++; } @@ -1155,7 +1232,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) if (cont->opt->stop_table) { if (opt_border == 2 && !cancel_pressed) - fprintf(fout, "%s\n", divider); + print_aligned_vertical_line(cont, 0, hwidth, dwidth, PRINT_RULE_BOTTOM, fout); /* print footers */ if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed) @@ -1171,7 +1248,6 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) fputc('\n', fout); } - free(divider); free(hlineptr->ptr); free(dlineptr->ptr); free(hlineptr); diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 55122d7..960b937 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -23,10 +23,35 @@ enum printFormat /* add your favourite output format here ... */ }; +typedef struct printTextLineFormat +{ + const char *hrule; + const char *leftvrule; + const char *midvrule; + const char *rightvrule; +} printTextLineFormat; + +typedef struct printTextFormat +{ + const char *name; + printTextLineFormat lrule[4]; + const char *midvrule_cont; + const char *midvrule_wrap; + const char *midvrule_blank; +} printTextFormat; + +typedef enum printTextRule +{ + PRINT_RULE_TOP, + PRINT_RULE_MIDDLE, + PRINT_RULE_BOTTOM, + PRINT_RULE_DATA +} printTextRule; typedef struct printTableOpt { enum printFormat format; /* one of the above */ + const printTextFormat *table_style; /* table rule formatting */ bool expanded; /* expanded/vertical output (if supported by * output format) */ unsigned short int border; /* Print a border around the table. 0=none, @@ -95,6 +120,9 @@ typedef struct printQueryOpt * gettext on col i */ } printQueryOpt; +extern const printTextFormat asciiformat; +extern const printTextFormat utf8format; + extern FILE *PageOutput(int lines, unsigned short int pager); extern void ClosePager(FILE *pagerpipe); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 429e5d9..4673b6a 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -19,6 +19,9 @@ #include "getopt_long.h" #include <locale.h> +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif #include "command.h" #include "common.h" @@ -125,6 +128,17 @@ main(int argc, char *argv[]) /* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */ pset.popt.topt.format = PRINT_ALIGNED; + + /* Default table style to plain ASCII */ + pset.popt.topt.table_style = &asciiformat; +#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) + /* If a UTF-8 locale is available, switch to UTF-8 box drawing characters */ + if (pg_strcasecmp(nl_langinfo(CODESET), "UTF-8") == 0 || + pg_strcasecmp(nl_langinfo(CODESET), "utf8") == 0 || + pg_strcasecmp(nl_langinfo(CODESET), "CP65001") == 0) + pset.popt.topt.table_style = &utf8format; +#endif + pset.popt.topt.border = 1; pset.popt.topt.pager = 1; pset.popt.topt.start_table = true; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 3e38b06..3fe6bfa 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -2264,7 +2264,7 @@ psql_completion(char *text, int start, int end) { static const char *const my_list[] = {"format", "border", "expanded", - "null", "fieldsep", "tuples_only", "title", "tableattr", "pager", + "null", "fieldsep", "tuples_only", "title", "tableattr", "tablestyle", "pager", "recordsep", NULL}; COMPLETE_WITH_LIST(my_list); diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c index cb477a6..0cfdb99 100644 --- a/src/test/regress/pg_regress_main.c +++ b/src/test/regress/pg_regress_main.c @@ -59,7 +59,7 @@ psql_start_test(const char *testname, add_stringlist_item(expectfiles, expectfile); snprintf(psql_cmd, sizeof(psql_cmd), - SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE, + SYSTEMQUOTE "\"%s%spsql\" -X -a -q -P tablestyle=ascii -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE, psqldir ? psqldir : "", psqldir ? "/" : "", dblist->str,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers