From a2dabf0e1dda93c860b10bff7b73617e7b090108 Mon Sep 17 00:00:00 2001 From: Stephen Frost Date: Fri, 12 Sep 2014 12:04:37 -0400 Subject: [PATCH] Add unicode_{column|header|border}_style to psql With the unicode linestyle, this adds support to control if the column, header, or border style should be single or double line unicode characters. The default remains 'single'. In passing, clean up the border documentation and address some minor formatting/spelling issues. Pavel Stehule, with some additional changes by me. --- doc/src/sgml/ref/psql-ref.sgml | 40 ++++++- src/bin/psql/command.c | 96 +++++++++++++++++ src/bin/psql/help.c | 3 +- src/bin/psql/print.c | 168 ++++++++++++++++++++++++----- src/bin/psql/print.h | 10 ++ src/bin/psql/startup.c | 8 ++ src/bin/psql/tab-complete.c | 13 ++- src/test/regress/expected/psql.out | 3 + 8 files changed, 307 insertions(+), 34 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index aa71674731..e7fcc734b5 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1996,12 +1996,12 @@ lo_import 152801 the number the more borders and lines the tables will have, but this depends on the particular format. In HTML format, this will translate directly - into the border=... attribute; in the - other formats only values 0 (no border), 1 (internal dividing lines), - and 2 (table frame) make sense. + into the border=... attribute; in latex and latex-longtable - also support a border value of 3 which adds - a dividing line between each row. + formats, a value of 3 will add a dividing line between each row; in + the other formats only values 0 (no border), 1 (internal dividing + lines), and 2 (table frame) make sense and values above 2 will be + treated the same as border = 2. @@ -2306,6 +2306,36 @@ lo_import 152801 + + + unicode_border_style + + + Sets the border drawing style for the unicode linestyle to one + of single or double. + + + + + + unicode_column_style + + + Sets the column drawing style for the unicode linestyle to one + of single or double. + + + + + + unicode_header_style + + + Sets the header drawing style for the unicode linestyle to one + of single or double. + + + diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 5d90ca28ed..2227db476a 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1054,6 +1054,9 @@ exec_command(const char *cmd, "footer", "format", "linestyle", "null", "numericlocale", "pager", "recordsep", "tableattr", "title", "tuples_only", + "unicode_border_linestyle", + "unicode_column_linestyle", + "unicode_header_linestyle", NULL }; @@ -2248,6 +2251,41 @@ _align2string(enum printFormat in) return "unknown"; } +/* + * Parse entered unicode linestyle. Returns true, when entered string is + * known linestyle: single, double else returns false. + */ +static bool +set_unicode_line_style(printQueryOpt *popt, const char *value, size_t vallen, + unicode_linestyle *linestyle) +{ + if (pg_strncasecmp("single", value, vallen) == 0) + *linestyle = UNICODE_LINESTYLE_SINGLE; + else if (pg_strncasecmp("double", value, vallen) == 0) + *linestyle = UNICODE_LINESTYLE_DOUBLE; + else + return false; + + /* input is ok, generate new unicode style */ + refresh_utf8format(&(popt->topt)); + + return true; +} + +static const char * +_unicode_linestyle2string(int linestyle) +{ + switch (linestyle) + { + case UNICODE_LINESTYLE_SINGLE: + return "single"; + break; + case UNICODE_LINESTYLE_DOUBLE: + return "double"; + break; + } + return "unknown"; +} bool do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) @@ -2305,6 +2343,45 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet) } + /* set unicode border line style */ + else if (strcmp(param, "unicode_border_linestyle") == 0) + { + if (!value) + ; + else if (!set_unicode_line_style(popt, value, vallen, + &popt->topt.unicode_border_linestyle)) + { + psql_error("\\pset: allowed unicode border linestyle are single, double\n"); + return false; + } + } + + /* set unicode column line style */ + else if (strcmp(param, "unicode_column_linestyle") == 0) + { + if (!value) + ; + else if (!set_unicode_line_style(popt, value, vallen, + &popt->topt.unicode_column_linestyle)) + { + psql_error("\\pset: allowed unicode column linestyle are single, double\n"); + return false; + } + } + + /* set unicode header line style */ + else if (strcmp(param, "unicode_header_linestyle") == 0) + { + if (!value) + ; + else if (!set_unicode_line_style(popt, value, vallen, + &popt->topt.unicode_header_linestyle)) + { + psql_error("\\pset: allowed unicode header linestyle are single, double\n"); + return false; + } + } + /* set border style/width */ else if (strcmp(param, "border") == 0) { @@ -2601,6 +2678,25 @@ printPsetInfo(const char *param, struct printQueryOpt *popt) printf(_("Tuples only (%s) is off.\n"), param); } + /* unicode style formatting */ + else if (strcmp(param, "unicode_border_linestyle") == 0) + { + printf(_("Unicode border linestyle is \"%s\".\n"), + _unicode_linestyle2string(popt->topt.unicode_border_linestyle)); + } + + else if (strcmp(param, "unicode_column_linestyle") == 0) + { + printf(_("Unicode column linestyle is \"%s\".\n"), + _unicode_linestyle2string(popt->topt.unicode_column_linestyle)); + } + + else if (strcmp(param, "unicode_header_linestyle") == 0) + { + printf(_("Unicode border linestyle is \"%s\".\n"), + _unicode_linestyle2string(popt->topt.unicode_header_linestyle)); + } + else { psql_error("\\pset: unknown option: %s\n", param); diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index ef35696339..6035a7771a 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -249,7 +249,8 @@ slashUsage(unsigned short int pager) ON(pset.popt.topt.format == PRINT_HTML)); fprintf(output, _(" \\pset [NAME [VALUE]] set table output option\n" " (NAME := {format|border|expanded|fieldsep|fieldsep_zero|footer|null|\n" - " numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager})\n")); + " numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager|\n" + " unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle})\n")); fprintf(output, _(" \\t [on|off] 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 0ada8a4eb5..3b3c3b73d9 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -89,35 +89,97 @@ const printTextFormat pg_asciiformat_old = false }; -const printTextFormat pg_utf8format = -{ - "unicode", - { - /* ─, ┌, ┬, ┐ */ - {"\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"} +/* Default unicode linestyle format */ +const printTextFormat pg_utf8format; + +typedef struct unicodeStyleRowFormat { + const char *horizontal; + const char *vertical_and_right[2]; + const char *vertical_and_left[2]; +} unicodeStyleRowFormat; + +typedef struct unicodeStyleColumnFormat { + const char *vertical; + const char *vertical_and_horizontal[2]; + const char *up_and_horizontal[2]; + const char *down_and_horizontal[2]; +} unicodeStyleColumnFormat; + +typedef struct unicodeStyleBorderFormat { + const char *up_and_right; + const char *vertical; + const char *down_and_right; + const char *horizontal; + const char *down_and_left; + const char *left_and_right; +} unicodeStyleBorderFormat; + +typedef struct unicodeStyleFormat { + unicodeStyleRowFormat row_style[2]; + unicodeStyleColumnFormat column_style[2]; + unicodeStyleBorderFormat border_style[2]; + const char *header_nl_left; + const char *header_nl_right; + const char *nl_left; + const char *nl_right; + const char *wrap_left; + const char *wrap_right; + bool wrap_right_border; +} unicodeStyleFormat; + +const unicodeStyleFormat unicode_style = { + { + { + /* ─ */ + "\342\224\200", + /* ├╟ */ + {"\342\224\234", "\342\225\237"}, + /* ┤╢ */ + {"\342\224\244", "\342\225\242"}, + }, + { + /* ═ */ + "\342\225\220", + /* ╞╠ */ + {"\342\225\236", "\342\225\240"}, + /* ╡╣ */ + {"\342\225\241", "\342\225\243"}, + }, + }, + { + { + /* │ */ + "\342\224\202", + /* ┼╪ */ + {"\342\224\274", "\342\225\252"}, + /* ┴╧ */ + {"\342\224\264", "\342\225\247"}, + /* ┬╤ */ + {"\342\224\254", "\342\225\244"}, + }, + { + /* ║ */ + "\342\225\221", + /* ╫╬ */ + {"\342\225\253", "\342\225\254"}, + /* ╨╩ */ + {"\342\225\250", "\342\225\251"}, + /* ╥╦ */ + {"\342\225\245", "\342\225\246"}, + }, + }, + { + /* └│┌─┐┘ */ + {"\342\224\224", "\342\224\202", "\342\224\214", "\342\224\200", "\342\224\220", "\342\224\230"}, + /* ╚║╔═╗╝ */ + {"\342\225\232", "\342\225\221", "\342\225\224", "\342\225\220", "\342\225\227", "\342\225\235"}, }, - /* │ */ - "\342\224\202", - /* │ */ - "\342\224\202", - /* │ */ - "\342\224\202", " ", - /* ↵ */ - "\342\206\265", + "\342\206\265", /* ↵ */ " ", - /* ↵ */ - "\342\206\265", - /* … */ - "\342\200\246", - /* … */ - "\342\200\246", + "\342\206\265", /* ↵ */ + "\342\200\246", /* … */ + "\342\200\246", /* … */ true }; @@ -1289,7 +1351,7 @@ print_aligned_vertical(const printTableContent *cont, FILE *fout) } else /* - * For border = 2, two more for the pipes (|) at the begging and + * For border = 2, two more for the pipes (|) at the beginning and * at the end of the lines. */ swidth = 7; @@ -2952,6 +3014,58 @@ get_line_style(const printTableOpt *opt) return &pg_asciiformat; } +void +refresh_utf8format(const printTableOpt *opt) +{ + printTextFormat *popt = (printTextFormat *) &pg_utf8format; + + const unicodeStyleBorderFormat *border; + const unicodeStyleRowFormat *header; + const unicodeStyleColumnFormat *column; + + popt->name = "unicode"; + + border = &unicode_style.border_style[opt->unicode_border_linestyle]; + header = &unicode_style.row_style[opt->unicode_header_linestyle]; + column = &unicode_style.column_style[opt->unicode_column_linestyle]; + + popt->lrule[PRINT_RULE_TOP].hrule = border->horizontal; + popt->lrule[PRINT_RULE_TOP].leftvrule = border->down_and_right; + popt->lrule[PRINT_RULE_TOP].midvrule = column->down_and_horizontal[opt->unicode_border_linestyle]; + popt->lrule[PRINT_RULE_TOP].rightvrule = border->down_and_left; + + popt->lrule[PRINT_RULE_MIDDLE].hrule = header->horizontal; + popt->lrule[PRINT_RULE_MIDDLE].leftvrule = header->vertical_and_right[opt->unicode_border_linestyle]; + popt->lrule[PRINT_RULE_MIDDLE].midvrule = column->vertical_and_horizontal[opt->unicode_header_linestyle]; + popt->lrule[PRINT_RULE_MIDDLE].rightvrule = header->vertical_and_left[opt->unicode_border_linestyle]; + + popt->lrule[PRINT_RULE_BOTTOM].hrule = border->horizontal; + popt->lrule[PRINT_RULE_BOTTOM].leftvrule = border->up_and_right; + popt->lrule[PRINT_RULE_BOTTOM].midvrule = column->up_and_horizontal[opt->unicode_border_linestyle]; + popt->lrule[PRINT_RULE_BOTTOM].rightvrule = border->left_and_right; + + /* N/A */ + popt->lrule[PRINT_RULE_DATA].hrule = ""; + popt->lrule[PRINT_RULE_DATA].leftvrule = border->vertical; + popt->lrule[PRINT_RULE_DATA].midvrule = column->vertical; + popt->lrule[PRINT_RULE_DATA].rightvrule = border->vertical; + + popt->midvrule_nl = column->vertical; + popt->midvrule_wrap = column->vertical; + popt->midvrule_blank = column->vertical; + + /* Same for all unicode today */ + popt->header_nl_left = unicode_style.header_nl_left; + popt->header_nl_right = unicode_style.header_nl_right; + popt->nl_left = unicode_style.nl_left; + popt->nl_right = unicode_style.nl_right; + popt->wrap_left = unicode_style.wrap_left; + popt->wrap_right = unicode_style.wrap_right; + popt->wrap_right_border = unicode_style.wrap_right_border; + + return; +} + /* * Compute the byte distance to the end of the string or *target_width * display character positions, whichever comes first. Update *target_width diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 87b2856282..f668b23295 100644 --- a/src/bin/psql/print.h +++ b/src/bin/psql/print.h @@ -68,6 +68,12 @@ typedef struct printTextFormat * marks when border=0? */ } printTextFormat; +typedef enum unicode_linestyle +{ + UNICODE_LINESTYLE_SINGLE = 0, + UNICODE_LINESTYLE_DOUBLE +} unicode_linestyle; + struct separator { char *separator; @@ -97,6 +103,9 @@ typedef struct printTableOpt int encoding; /* character encoding */ int env_columns; /* $COLUMNS on psql start, 0 is unset */ int columns; /* target width for wrapped format */ + unicode_linestyle unicode_border_linestyle; + unicode_linestyle unicode_column_linestyle; + unicode_linestyle unicode_header_linestyle; } printTableOpt; /* @@ -178,6 +187,7 @@ extern void printQuery(const PGresult *result, const printQueryOpt *opt, extern void setDecimalLocale(void); extern const printTextFormat *get_line_style(const printTableOpt *opt); +extern void refresh_utf8format(const printTableOpt *opt); #ifndef __CYGWIN__ #define DEFAULT_PAGER "more" diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index b879d0c35d..11a159a164 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -26,6 +26,7 @@ #include "help.h" #include "input.h" #include "mainloop.h" +#include "print.h" #include "settings.h" @@ -131,6 +132,13 @@ main(int argc, char *argv[]) pset.popt.topt.start_table = true; pset.popt.topt.stop_table = true; pset.popt.topt.default_footer = true; + + pset.popt.topt.unicode_border_linestyle = UNICODE_LINESTYLE_SINGLE; + pset.popt.topt.unicode_column_linestyle = UNICODE_LINESTYLE_SINGLE; + pset.popt.topt.unicode_header_linestyle = UNICODE_LINESTYLE_SINGLE; + + refresh_utf8format(&(pset.popt.topt)); + /* We must get COLUMNS here before readline() sets it */ pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0; diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 4ce47e99ef..b80fe13168 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3622,7 +3622,8 @@ psql_completion(const char *text, int start, int end) {"border", "columns", "expanded", "fieldsep", "fieldsep_zero", "footer", "format", "linestyle", "null", "numericlocale", "pager", "recordsep", "recordsep_zero", "tableattr", "title", - "tuples_only", NULL}; + "tuples_only", "unicode_border_linestyle", + "unicode_column_linestyle", "unicode_header_linestyle", NULL}; COMPLETE_WITH_LIST_CS(my_list); } @@ -3643,6 +3644,16 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST_CS(my_list); } + else if (strcmp(prev_wd, "unicode_border_linestyle") == 0 || + strcmp(prev_wd, "unicode_column_linestyle") == 0 || + strcmp(prev_wd, "unicode_header_linestyle") == 0) + { + static const char *const my_list[] = + {"single", "double", NULL}; + + COMPLETE_WITH_LIST_CS(my_list); + + } } else if (strcmp(prev_wd, "\\unset") == 0) { diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index 199036d595..3764127558 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -68,6 +68,9 @@ Record separator (recordsep) is . Table attributes (tableattr) unset. Title (title) unset. Tuples only (tuples_only) is off. +Unicode border linestyle is "single". +Unicode column linestyle is "single". +Unicode border linestyle is "single". -- test multi-line headers, wrapping, and newline indicators prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab -- 2.40.0