LCOV - code coverage report
Current view: top level - src/fe_utils - print.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 1408 1516 92.9 %
Date: 2019-11-21 13:06:38 Functions: 47 48 97.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * Query-result printing support for frontend code
       4             :  *
       5             :  * This file used to be part of psql, but now it's separated out to allow
       6             :  * other frontend programs to use it.  Because the printing code needs
       7             :  * access to the cancel_pressed flag as well as SIGPIPE trapping and
       8             :  * pager open/close functions, all that stuff came with it.
       9             :  *
      10             :  *
      11             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
      12             :  * Portions Copyright (c) 1994, Regents of the University of California
      13             :  *
      14             :  * src/fe_utils/print.c
      15             :  *
      16             :  *-------------------------------------------------------------------------
      17             :  */
      18             : #include "postgres_fe.h"
      19             : 
      20             : #include <limits.h>
      21             : #include <math.h>
      22             : #include <signal.h>
      23             : #include <unistd.h>
      24             : 
      25             : #ifndef WIN32
      26             : #include <sys/ioctl.h>            /* for ioctl() */
      27             : #endif
      28             : 
      29             : #ifdef HAVE_TERMIOS_H
      30             : #include <termios.h>
      31             : #endif
      32             : 
      33             : #include "catalog/pg_type_d.h"
      34             : #include "fe_utils/mbprint.h"
      35             : #include "fe_utils/print.h"
      36             : 
      37             : /*
      38             :  * If the calling program doesn't have any mechanism for setting
      39             :  * cancel_pressed, it will have no effect.
      40             :  *
      41             :  * Note: print.c's general strategy for when to check cancel_pressed is to do
      42             :  * so at completion of each row of output.
      43             :  */
      44             : volatile bool cancel_pressed = false;
      45             : 
      46             : static bool always_ignore_sigpipe = false;
      47             : 
      48             : /* info for locale-aware numeric formatting; set up by setDecimalLocale() */
      49             : static char *decimal_point;
      50             : static int  groupdigits;
      51             : static char *thousands_sep;
      52             : 
      53             : static char default_footer[100];
      54             : static printTableFooter default_footer_cell = {default_footer, NULL};
      55             : 
      56             : /* Line style control structures */
      57             : const printTextFormat pg_asciiformat =
      58             : {
      59             :     "ascii",
      60             :     {
      61             :         {"-", "+", "+", "+"},
      62             :         {"-", "+", "+", "+"},
      63             :         {"-", "+", "+", "+"},
      64             :         {"", "|", "|", "|"}
      65             :     },
      66             :     "|",
      67             :     "|",
      68             :     "|",
      69             :     " ",
      70             :     "+",
      71             :     " ",
      72             :     "+",
      73             :     ".",
      74             :     ".",
      75             :     true
      76             : };
      77             : 
      78             : const printTextFormat pg_asciiformat_old =
      79             : {
      80             :     "old-ascii",
      81             :     {
      82             :         {"-", "+", "+", "+"},
      83             :         {"-", "+", "+", "+"},
      84             :         {"-", "+", "+", "+"},
      85             :         {"", "|", "|", "|"}
      86             :     },
      87             :     ":",
      88             :     ";",
      89             :     " ",
      90             :     "+",
      91             :     " ",
      92             :     " ",
      93             :     " ",
      94             :     " ",
      95             :     " ",
      96             :     false
      97             : };
      98             : 
      99             : /* Default unicode linestyle format */
     100             : printTextFormat pg_utf8format;
     101             : 
     102             : typedef struct unicodeStyleRowFormat
     103             : {
     104             :     const char *horizontal;
     105             :     const char *vertical_and_right[2];
     106             :     const char *vertical_and_left[2];
     107             : } unicodeStyleRowFormat;
     108             : 
     109             : typedef struct unicodeStyleColumnFormat
     110             : {
     111             :     const char *vertical;
     112             :     const char *vertical_and_horizontal[2];
     113             :     const char *up_and_horizontal[2];
     114             :     const char *down_and_horizontal[2];
     115             : } unicodeStyleColumnFormat;
     116             : 
     117             : typedef struct unicodeStyleBorderFormat
     118             : {
     119             :     const char *up_and_right;
     120             :     const char *vertical;
     121             :     const char *down_and_right;
     122             :     const char *horizontal;
     123             :     const char *down_and_left;
     124             :     const char *left_and_right;
     125             : } unicodeStyleBorderFormat;
     126             : 
     127             : typedef struct unicodeStyleFormat
     128             : {
     129             :     unicodeStyleRowFormat row_style[2];
     130             :     unicodeStyleColumnFormat column_style[2];
     131             :     unicodeStyleBorderFormat border_style[2];
     132             :     const char *header_nl_left;
     133             :     const char *header_nl_right;
     134             :     const char *nl_left;
     135             :     const char *nl_right;
     136             :     const char *wrap_left;
     137             :     const char *wrap_right;
     138             :     bool        wrap_right_border;
     139             : } unicodeStyleFormat;
     140             : 
     141             : static const unicodeStyleFormat unicode_style = {
     142             :     {
     143             :         {
     144             :             /* ─ */
     145             :             "\342\224\200",
     146             :             /* ├╟ */
     147             :             {"\342\224\234", "\342\225\237"},
     148             :             /* ┤╢ */
     149             :             {"\342\224\244", "\342\225\242"},
     150             :         },
     151             :         {
     152             :             /* ═ */
     153             :             "\342\225\220",
     154             :             /* ╞╠ */
     155             :             {"\342\225\236", "\342\225\240"},
     156             :             /* ╡╣ */
     157             :             {"\342\225\241", "\342\225\243"},
     158             :         },
     159             :     },
     160             :     {
     161             :         {
     162             :             /* │ */
     163             :             "\342\224\202",
     164             :             /* ┼╪ */
     165             :             {"\342\224\274", "\342\225\252"},
     166             :             /* ┴╧ */
     167             :             {"\342\224\264", "\342\225\247"},
     168             :             /* ┬╤ */
     169             :             {"\342\224\254", "\342\225\244"},
     170             :         },
     171             :         {
     172             :             /* ║ */
     173             :             "\342\225\221",
     174             :             /* ╫╬ */
     175             :             {"\342\225\253", "\342\225\254"},
     176             :             /* ╨╩ */
     177             :             {"\342\225\250", "\342\225\251"},
     178             :             /* ╥╦ */
     179             :             {"\342\225\245", "\342\225\246"},
     180             :         },
     181             :     },
     182             :     {
     183             :         /* └│┌─┐┘ */
     184             :         {"\342\224\224", "\342\224\202", "\342\224\214", "\342\224\200", "\342\224\220", "\342\224\230"},
     185             :         /* ╚║╔═╗╝ */
     186             :         {"\342\225\232", "\342\225\221", "\342\225\224", "\342\225\220", "\342\225\227", "\342\225\235"},
     187             :     },
     188             :     " ",
     189             :     "\342\206\265",               /* ↵ */
     190             :     " ",
     191             :     "\342\206\265",               /* ↵ */
     192             :     "\342\200\246",               /* … */
     193             :     "\342\200\246",               /* … */
     194             :     true
     195             : };
     196             : 
     197             : 
     198             : /* Local functions */
     199             : static int  strlen_max_width(unsigned char *str, int *target_width, int encoding);
     200             : static void IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
     201             :                           FILE **fout, bool *is_pager);
     202             : 
     203             : static void print_aligned_vertical(const printTableContent *cont,
     204             :                                    FILE *fout, bool is_pager);
     205             : 
     206             : 
     207             : /* Count number of digits in integral part of number */
     208             : static int
     209         128 : integer_digits(const char *my_str)
     210             : {
     211             :     /* ignoring any sign ... */
     212         128 :     if (my_str[0] == '-' || my_str[0] == '+')
     213          24 :         my_str++;
     214             :     /* ... count initial integral digits */
     215         128 :     return strspn(my_str, "0123456789");
     216             : }
     217             : 
     218             : /* Compute additional length required for locale-aware numeric output */
     219             : static int
     220          64 : additional_numeric_locale_len(const char *my_str)
     221             : {
     222          64 :     int         int_len = integer_digits(my_str),
     223          64 :                 len = 0;
     224             : 
     225             :     /* Account for added thousands_sep instances */
     226          64 :     if (int_len > groupdigits)
     227           0 :         len += ((int_len - 1) / groupdigits) * strlen(thousands_sep);
     228             : 
     229             :     /* Account for possible additional length of decimal_point */
     230          64 :     if (strchr(my_str, '.') != NULL)
     231           0 :         len += strlen(decimal_point) - 1;
     232             : 
     233          64 :     return len;
     234             : }
     235             : 
     236             : /*
     237             :  * Format a numeric value per current LC_NUMERIC locale setting
     238             :  *
     239             :  * Returns the appropriately formatted string in a new allocated block,
     240             :  * caller must free.
     241             :  *
     242             :  * setDecimalLocale() must have been called earlier.
     243             :  */
     244             : static char *
     245          64 : format_numeric_locale(const char *my_str)
     246             : {
     247             :     char       *new_str;
     248             :     int         new_len,
     249             :                 int_len,
     250             :                 leading_digits,
     251             :                 i,
     252             :                 new_str_pos;
     253             : 
     254             :     /*
     255             :      * If the string doesn't look like a number, return it unchanged.  This
     256             :      * check is essential to avoid mangling already-localized "money" values.
     257             :      */
     258          64 :     if (strspn(my_str, "0123456789+-.eE") != strlen(my_str))
     259           0 :         return pg_strdup(my_str);
     260             : 
     261          64 :     new_len = strlen(my_str) + additional_numeric_locale_len(my_str);
     262          64 :     new_str = pg_malloc(new_len + 1);
     263          64 :     new_str_pos = 0;
     264          64 :     int_len = integer_digits(my_str);
     265             : 
     266             :     /* number of digits in first thousands group */
     267          64 :     leading_digits = int_len % groupdigits;
     268          64 :     if (leading_digits == 0)
     269          12 :         leading_digits = groupdigits;
     270             : 
     271             :     /* process sign */
     272          64 :     if (my_str[0] == '-' || my_str[0] == '+')
     273             :     {
     274          12 :         new_str[new_str_pos++] = my_str[0];
     275          12 :         my_str++;
     276             :     }
     277             : 
     278             :     /* process integer part of number */
     279         152 :     for (i = 0; i < int_len; i++)
     280             :     {
     281             :         /* Time to insert separator? */
     282          88 :         if (i > 0 && --leading_digits == 0)
     283             :         {
     284           0 :             strcpy(&new_str[new_str_pos], thousands_sep);
     285           0 :             new_str_pos += strlen(thousands_sep);
     286           0 :             leading_digits = groupdigits;
     287             :         }
     288          88 :         new_str[new_str_pos++] = my_str[i];
     289             :     }
     290             : 
     291             :     /* handle decimal point if any */
     292          64 :     if (my_str[i] == '.')
     293             :     {
     294           0 :         strcpy(&new_str[new_str_pos], decimal_point);
     295           0 :         new_str_pos += strlen(decimal_point);
     296           0 :         i++;
     297             :     }
     298             : 
     299             :     /* copy the rest (fractional digits and/or exponent, and \0 terminator) */
     300          64 :     strcpy(&new_str[new_str_pos], &my_str[i]);
     301             : 
     302             :     /* assert we didn't underestimate new_len (an overestimate is OK) */
     303             :     Assert(strlen(new_str) <= new_len);
     304             : 
     305          64 :     return new_str;
     306             : }
     307             : 
     308             : 
     309             : /*
     310             :  * fputnbytes: print exactly N bytes to a file
     311             :  *
     312             :  * We avoid using %.*s here because it can misbehave if the data
     313             :  * is not valid in what libc thinks is the prevailing encoding.
     314             :  */
     315             : static void
     316      587628 : fputnbytes(FILE *f, const char *str, size_t n)
     317             : {
     318     7512042 :     while (n-- > 0)
     319     6336786 :         fputc(*str++, f);
     320      587628 : }
     321             : 
     322             : 
     323             : static void
     324       12048 : print_separator(struct separator sep, FILE *fout)
     325             : {
     326       12048 :     if (sep.separator_zero)
     327           0 :         fputc('\000', fout);
     328       12048 :     else if (sep.separator)
     329       12048 :         fputs(sep.separator, fout);
     330       12048 : }
     331             : 
     332             : 
     333             : /*
     334             :  * Return the list of explicitly-requested footers or, when applicable, the
     335             :  * default "(xx rows)" footer.  Always omit the default footer when given
     336             :  * non-default footers, "\pset footer off", or a specific instruction to that
     337             :  * effect from a calling backslash command.  Vertical formats number each row,
     338             :  * making the default footer redundant; they do not call this function.
     339             :  *
     340             :  * The return value may point to static storage; do not keep it across calls.
     341             :  */
     342             : static printTableFooter *
     343       58644 : footers_with_default(const printTableContent *cont)
     344             : {
     345       58644 :     if (cont->footers == NULL && cont->opt->default_footer)
     346             :     {
     347             :         unsigned long total_records;
     348             : 
     349       57024 :         total_records = cont->opt->prior_records + cont->nrows;
     350       57024 :         snprintf(default_footer, sizeof(default_footer),
     351       57024 :                  ngettext("(%lu row)", "(%lu rows)", total_records),
     352             :                  total_records);
     353             : 
     354       57024 :         return &default_footer_cell;
     355             :     }
     356             :     else
     357        1620 :         return cont->footers;
     358             : }
     359             : 
     360             : 
     361             : /*************************/
     362             : /* Unaligned text        */
     363             : /*************************/
     364             : 
     365             : 
     366             : static void
     367        1198 : print_unaligned_text(const printTableContent *cont, FILE *fout)
     368             : {
     369        1198 :     bool        opt_tuples_only = cont->opt->tuples_only;
     370             :     unsigned int i;
     371             :     const char *const *ptr;
     372        1198 :     bool        need_recordsep = false;
     373             : 
     374        1198 :     if (cancel_pressed)
     375           0 :         return;
     376             : 
     377        1198 :     if (cont->opt->start_table)
     378             :     {
     379             :         /* print title */
     380        1198 :         if (!opt_tuples_only && cont->title)
     381             :         {
     382           4 :             fputs(cont->title, fout);
     383           4 :             print_separator(cont->opt->recordSep, fout);
     384             :         }
     385             : 
     386             :         /* print headers */
     387        1198 :         if (!opt_tuples_only)
     388             :         {
     389         216 :             for (ptr = cont->headers; *ptr; ptr++)
     390             :             {
     391         160 :                 if (ptr != cont->headers)
     392         104 :                     print_separator(cont->opt->fieldSep, fout);
     393         160 :                 fputs(*ptr, fout);
     394             :             }
     395          56 :             need_recordsep = true;
     396             :         }
     397             :     }
     398             :     else
     399             :         /* assume continuing printout */
     400           0 :         need_recordsep = true;
     401             : 
     402             :     /* print cells */
     403       12104 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
     404             :     {
     405       10906 :         if (need_recordsep)
     406             :         {
     407        4528 :             print_separator(cont->opt->recordSep, fout);
     408        4528 :             need_recordsep = false;
     409        4528 :             if (cancel_pressed)
     410           0 :                 break;
     411             :         }
     412       10906 :         fputs(*ptr, fout);
     413             : 
     414       10906 :         if ((i + 1) % cont->ncolumns)
     415        5288 :             print_separator(cont->opt->fieldSep, fout);
     416             :         else
     417        5618 :             need_recordsep = true;
     418             :     }
     419             : 
     420             :     /* print footers */
     421        1198 :     if (cont->opt->stop_table)
     422             :     {
     423        1198 :         printTableFooter *footers = footers_with_default(cont);
     424             : 
     425        1198 :         if (!opt_tuples_only && footers != NULL && !cancel_pressed)
     426             :         {
     427             :             printTableFooter *f;
     428             : 
     429         112 :             for (f = footers; f; f = f->next)
     430             :             {
     431          56 :                 if (need_recordsep)
     432             :                 {
     433          56 :                     print_separator(cont->opt->recordSep, fout);
     434          56 :                     need_recordsep = false;
     435             :                 }
     436          56 :                 fputs(f->data, fout);
     437          56 :                 need_recordsep = true;
     438             :             }
     439             :         }
     440             : 
     441             :         /*
     442             :          * The last record is terminated by a newline, independent of the set
     443             :          * record separator.  But when the record separator is a zero byte, we
     444             :          * use that (compatible with find -print0 and xargs).
     445             :          */
     446        1198 :         if (need_recordsep)
     447             :         {
     448        1146 :             if (cont->opt->recordSep.separator_zero)
     449           0 :                 print_separator(cont->opt->recordSep, fout);
     450             :             else
     451        1146 :                 fputc('\n', fout);
     452             :         }
     453             :     }
     454             : }
     455             : 
     456             : 
     457             : static void
     458          68 : print_unaligned_vertical(const printTableContent *cont, FILE *fout)
     459             : {
     460          68 :     bool        opt_tuples_only = cont->opt->tuples_only;
     461             :     unsigned int i;
     462             :     const char *const *ptr;
     463          68 :     bool        need_recordsep = false;
     464             : 
     465          68 :     if (cancel_pressed)
     466           0 :         return;
     467             : 
     468          68 :     if (cont->opt->start_table)
     469             :     {
     470             :         /* print title */
     471          68 :         if (!opt_tuples_only && cont->title)
     472             :         {
     473           4 :             fputs(cont->title, fout);
     474           4 :             need_recordsep = true;
     475             :         }
     476             :     }
     477             :     else
     478             :         /* assume continuing printout */
     479           0 :         need_recordsep = true;
     480             : 
     481             :     /* print records */
     482         952 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
     483             :     {
     484         884 :         if (need_recordsep)
     485             :         {
     486             :             /* record separator is 2 occurrences of recordsep in this mode */
     487         356 :             print_separator(cont->opt->recordSep, fout);
     488         356 :             print_separator(cont->opt->recordSep, fout);
     489         356 :             need_recordsep = false;
     490         356 :             if (cancel_pressed)
     491           0 :                 break;
     492             :         }
     493             : 
     494         884 :         fputs(cont->headers[i % cont->ncolumns], fout);
     495         884 :         print_separator(cont->opt->fieldSep, fout);
     496         884 :         fputs(*ptr, fout);
     497             : 
     498         884 :         if ((i + 1) % cont->ncolumns)
     499         464 :             print_separator(cont->opt->recordSep, fout);
     500             :         else
     501         420 :             need_recordsep = true;
     502             :     }
     503             : 
     504          68 :     if (cont->opt->stop_table)
     505             :     {
     506             :         /* print footers */
     507          68 :         if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
     508             :         {
     509             :             printTableFooter *f;
     510             : 
     511           4 :             print_separator(cont->opt->recordSep, fout);
     512           8 :             for (f = cont->footers; f; f = f->next)
     513             :             {
     514           4 :                 print_separator(cont->opt->recordSep, fout);
     515           4 :                 fputs(f->data, fout);
     516             :             }
     517             :         }
     518             : 
     519             :         /* see above in print_unaligned_text() */
     520          68 :         if (need_recordsep)
     521             :         {
     522          68 :             if (cont->opt->recordSep.separator_zero)
     523           0 :                 print_separator(cont->opt->recordSep, fout);
     524             :             else
     525          68 :                 fputc('\n', fout);
     526             :         }
     527             :     }
     528             : }
     529             : 
     530             : 
     531             : /********************/
     532             : /* Aligned text     */
     533             : /********************/
     534             : 
     535             : 
     536             : /* draw "line" */
     537             : static void
     538       57404 : _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths,
     539             :                        unsigned short border, printTextRule pos,
     540             :                        const printTextFormat *format,
     541             :                        FILE *fout)
     542             : {
     543       57404 :     const printTextLineFormat *lformat = &format->lrule[pos];
     544             :     unsigned int i,
     545             :                 j;
     546             : 
     547       57404 :     if (border == 1)
     548       57276 :         fputs(lformat->hrule, fout);
     549         128 :     else if (border == 2)
     550          96 :         fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
     551             : 
     552      165774 :     for (i = 0; i < ncolumns; i++)
     553             :     {
     554     1825250 :         for (j = 0; j < widths[i]; j++)
     555     1716880 :             fputs(lformat->hrule, fout);
     556             : 
     557      108370 :         if (i < ncolumns - 1)
     558             :         {
     559       51034 :             if (border == 0)
     560          32 :                 fputc(' ', fout);
     561             :             else
     562       51002 :                 fprintf(fout, "%s%s%s", lformat->hrule,
     563             :                         lformat->midvrule, lformat->hrule);
     564             :         }
     565             :     }
     566             : 
     567       57404 :     if (border == 2)
     568          96 :         fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
     569       57308 :     else if (border == 1)
     570       57276 :         fputs(lformat->hrule, fout);
     571             : 
     572       57404 :     fputc('\n', fout);
     573       57404 : }
     574             : 
     575             : 
     576             : /*
     577             :  *  Print pretty boxes around cells.
     578             :  */
     579             : static void
     580       57392 : print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
     581             : {
     582       57392 :     bool        opt_tuples_only = cont->opt->tuples_only;
     583       57392 :     int         encoding = cont->opt->encoding;
     584       57392 :     unsigned short opt_border = cont->opt->border;
     585       57392 :     const printTextFormat *format = get_line_style(cont->opt);
     586       57392 :     const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
     587             : 
     588       57392 :     unsigned int col_count = 0,
     589       57392 :                 cell_count = 0;
     590             : 
     591             :     unsigned int i,
     592             :                 j;
     593             : 
     594             :     unsigned int *width_header,
     595             :                *max_width,
     596             :                *width_wrap,
     597             :                *width_average;
     598             :     unsigned int *max_nl_lines, /* value split by newlines */
     599             :                *curr_nl_line,
     600             :                *max_bytes;
     601             :     unsigned char **format_buf;
     602             :     unsigned int width_total;
     603             :     unsigned int total_header_width;
     604       57392 :     unsigned int extra_row_output_lines = 0;
     605       57392 :     unsigned int extra_output_lines = 0;
     606             : 
     607             :     const char *const *ptr;
     608             : 
     609             :     struct lineptr **col_lineptrs;  /* pointers to line pointer per column */
     610             : 
     611             :     bool       *header_done;    /* Have all header lines been output? */
     612             :     int        *bytes_output;   /* Bytes output for column value */
     613             :     printTextLineWrap *wrap;    /* Wrap status for each column */
     614       57392 :     int         output_columns = 0; /* Width of interactive console */
     615       57392 :     bool        is_local_pager = false;
     616             : 
     617       57392 :     if (cancel_pressed)
     618           0 :         return;
     619             : 
     620       57392 :     if (opt_border > 2)
     621           0 :         opt_border = 2;
     622             : 
     623       57392 :     if (cont->ncolumns > 0)
     624             :     {
     625       57324 :         col_count = cont->ncolumns;
     626       57324 :         width_header = pg_malloc0(col_count * sizeof(*width_header));
     627       57324 :         width_average = pg_malloc0(col_count * sizeof(*width_average));
     628       57324 :         max_width = pg_malloc0(col_count * sizeof(*max_width));
     629       57324 :         width_wrap = pg_malloc0(col_count * sizeof(*width_wrap));
     630       57324 :         max_nl_lines = pg_malloc0(col_count * sizeof(*max_nl_lines));
     631       57324 :         curr_nl_line = pg_malloc0(col_count * sizeof(*curr_nl_line));
     632       57324 :         col_lineptrs = pg_malloc0(col_count * sizeof(*col_lineptrs));
     633       57324 :         max_bytes = pg_malloc0(col_count * sizeof(*max_bytes));
     634       57324 :         format_buf = pg_malloc0(col_count * sizeof(*format_buf));
     635       57324 :         header_done = pg_malloc0(col_count * sizeof(*header_done));
     636       57324 :         bytes_output = pg_malloc0(col_count * sizeof(*bytes_output));
     637       57324 :         wrap = pg_malloc0(col_count * sizeof(*wrap));
     638             :     }
     639             :     else
     640             :     {
     641          68 :         width_header = NULL;
     642          68 :         width_average = NULL;
     643          68 :         max_width = NULL;
     644          68 :         width_wrap = NULL;
     645          68 :         max_nl_lines = NULL;
     646          68 :         curr_nl_line = NULL;
     647          68 :         col_lineptrs = NULL;
     648          68 :         max_bytes = NULL;
     649          68 :         format_buf = NULL;
     650          68 :         header_done = NULL;
     651          68 :         bytes_output = NULL;
     652          68 :         wrap = NULL;
     653             :     }
     654             : 
     655             :     /* scan all column headers, find maximum width and max max_nl_lines */
     656      165742 :     for (i = 0; i < col_count; i++)
     657             :     {
     658             :         int         width,
     659             :                     nl_lines,
     660             :                     bytes_required;
     661             : 
     662      108350 :         pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
     663             :                    encoding, &width, &nl_lines, &bytes_required);
     664      108350 :         if (width > max_width[i])
     665      108326 :             max_width[i] = width;
     666      108350 :         if (nl_lines > max_nl_lines[i])
     667      108350 :             max_nl_lines[i] = nl_lines;
     668      108350 :         if (bytes_required > max_bytes[i])
     669      108350 :             max_bytes[i] = bytes_required;
     670      108350 :         if (nl_lines > extra_row_output_lines)
     671       57324 :             extra_row_output_lines = nl_lines;
     672             : 
     673      108350 :         width_header[i] = width;
     674             :     }
     675             :     /* Add height of tallest header column */
     676       57392 :     extra_output_lines += extra_row_output_lines;
     677       57392 :     extra_row_output_lines = 0;
     678             : 
     679             :     /* scan all cells, find maximum width, compute cell_count */
     680      631630 :     for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
     681             :     {
     682             :         int         width,
     683             :                     nl_lines,
     684             :                     bytes_required;
     685             : 
     686      574238 :         pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
     687             :                    &width, &nl_lines, &bytes_required);
     688             : 
     689      574238 :         if (width > max_width[i % col_count])
     690       60744 :             max_width[i % col_count] = width;
     691      574238 :         if (nl_lines > max_nl_lines[i % col_count])
     692         842 :             max_nl_lines[i % col_count] = nl_lines;
     693      574238 :         if (bytes_required > max_bytes[i % col_count])
     694       60814 :             max_bytes[i % col_count] = bytes_required;
     695             : 
     696      574238 :         width_average[i % col_count] += width;
     697             :     }
     698             : 
     699             :     /* If we have rows, compute average */
     700       57392 :     if (col_count != 0 && cell_count != 0)
     701             :     {
     702       53814 :         int         rows = cell_count / col_count;
     703             : 
     704      153556 :         for (i = 0; i < col_count; i++)
     705       99742 :             width_average[i] /= rows;
     706             :     }
     707             : 
     708             :     /* adjust the total display width based on border style */
     709       57392 :     if (opt_border == 0)
     710          32 :         width_total = col_count;
     711       57360 :     else if (opt_border == 1)
     712       57328 :         width_total = col_count * 3 - ((col_count > 0) ? 1 : 0);
     713             :     else
     714          32 :         width_total = col_count * 3 + 1;
     715       57392 :     total_header_width = width_total;
     716             : 
     717      165742 :     for (i = 0; i < col_count; i++)
     718             :     {
     719      108350 :         width_total += max_width[i];
     720      108350 :         total_header_width += width_header[i];
     721             :     }
     722             : 
     723             :     /*
     724             :      * At this point: max_width[] contains the max width of each column,
     725             :      * max_nl_lines[] contains the max number of lines in each column,
     726             :      * max_bytes[] contains the maximum storage space for formatting strings,
     727             :      * width_total contains the giant width sum.  Now we allocate some memory
     728             :      * for line pointers.
     729             :      */
     730      165742 :     for (i = 0; i < col_count; i++)
     731             :     {
     732             :         /* Add entry for ptr == NULL array termination */
     733      108350 :         col_lineptrs[i] = pg_malloc0((max_nl_lines[i] + 1) *
     734             :                                      sizeof(**col_lineptrs));
     735             : 
     736      108350 :         format_buf[i] = pg_malloc(max_bytes[i] + 1);
     737             : 
     738      108350 :         col_lineptrs[i]->ptr = format_buf[i];
     739             :     }
     740             : 
     741             :     /* Default word wrap to the full width, i.e. no word wrap */
     742      165742 :     for (i = 0; i < col_count; i++)
     743      108350 :         width_wrap[i] = max_width[i];
     744             : 
     745             :     /*
     746             :      * Choose target output width: \pset columns, or $COLUMNS, or ioctl
     747             :      */
     748       57392 :     if (cont->opt->columns > 0)
     749         236 :         output_columns = cont->opt->columns;
     750       57156 :     else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
     751             :     {
     752          52 :         if (cont->opt->env_columns > 0)
     753           0 :             output_columns = cont->opt->env_columns;
     754             : #ifdef TIOCGWINSZ
     755             :         else
     756             :         {
     757             :             struct winsize screen_size;
     758             : 
     759          52 :             if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
     760           0 :                 output_columns = screen_size.ws_col;
     761             :         }
     762             : #endif
     763             :     }
     764             : 
     765       57392 :     if (cont->opt->format == PRINT_WRAPPED)
     766             :     {
     767             :         /*
     768             :          * Optional optimized word wrap. Shrink columns with a high max/avg
     769             :          * ratio.  Slightly bias against wider columns. (Increases chance a
     770             :          * narrow column will fit in its cell.)  If available columns is
     771             :          * positive...  and greater than the width of the unshrinkable column
     772             :          * headers
     773             :          */
     774          72 :         if (output_columns > 0 && output_columns >= total_header_width)
     775             :         {
     776             :             /* While there is still excess width... */
     777         224 :             while (width_total > output_columns)
     778             :             {
     779         128 :                 double      max_ratio = 0;
     780         128 :                 int         worst_col = -1;
     781             : 
     782             :                 /*
     783             :                  * Find column that has the highest ratio of its maximum width
     784             :                  * compared to its average width.  This tells us which column
     785             :                  * will produce the fewest wrapped values if shortened.
     786             :                  * width_wrap starts as equal to max_width.
     787             :                  */
     788         384 :                 for (i = 0; i < col_count; i++)
     789             :                 {
     790         256 :                     if (width_average[i] && width_wrap[i] > width_header[i])
     791             :                     {
     792             :                         /* Penalize wide columns by 1% of their width */
     793             :                         double      ratio;
     794             : 
     795         512 :                         ratio = (double) width_wrap[i] / width_average[i] +
     796         256 :                             max_width[i] * 0.01;
     797         256 :                         if (ratio > max_ratio)
     798             :                         {
     799         168 :                             max_ratio = ratio;
     800         168 :                             worst_col = i;
     801             :                         }
     802             :                     }
     803             :                 }
     804             : 
     805             :                 /* Exit loop if we can't squeeze any more. */
     806         128 :                 if (worst_col == -1)
     807           0 :                     break;
     808             : 
     809             :                 /* Decrease width of target column by one. */
     810         128 :                 width_wrap[worst_col]--;
     811         128 :                 width_total--;
     812             :             }
     813             :         }
     814             :     }
     815             : 
     816             :     /*
     817             :      * If in expanded auto mode, we have now calculated the expected width, so
     818             :      * we can now escape to vertical mode if necessary.  If the output has
     819             :      * only one column, the expanded format would be wider than the regular
     820             :      * format, so don't use it in that case.
     821             :      */
     822       57392 :     if (cont->opt->expanded == 2 && output_columns > 0 && cont->ncolumns > 1 &&
     823           0 :         (output_columns < total_header_width || output_columns < width_total))
     824             :     {
     825           0 :         print_aligned_vertical(cont, fout, is_pager);
     826           0 :         goto cleanup;
     827             :     }
     828             : 
     829             :     /* If we wrapped beyond the display width, use the pager */
     830       57616 :     if (!is_pager && fout == stdout && output_columns > 0 &&
     831         392 :         (output_columns < total_header_width || output_columns < width_total))
     832             :     {
     833         108 :         fout = PageOutput(INT_MAX, cont->opt);   /* force pager */
     834         108 :         is_pager = is_local_pager = true;
     835             :     }
     836             : 
     837             :     /* Check if newlines or our wrapping now need the pager */
     838       57392 :     if (!is_pager && fout == stdout)
     839             :     {
     840             :         /* scan all cells, find maximum width, compute cell_count */
     841      629998 :         for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
     842             :         {
     843             :             int         width,
     844             :                         nl_lines,
     845             :                         bytes_required;
     846             : 
     847      572778 :             pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
     848             :                        &width, &nl_lines, &bytes_required);
     849             : 
     850             :             /*
     851             :              * A row can have both wrapping and newlines that cause it to
     852             :              * display across multiple lines.  We check for both cases below.
     853             :              */
     854      572778 :             if (width > 0 && width_wrap[i])
     855             :             {
     856             :                 unsigned int extra_lines;
     857             : 
     858             :                 /* don't count the first line of nl_lines - it's not "extra" */
     859      501798 :                 extra_lines = ((width - 1) / width_wrap[i]) + nl_lines - 1;
     860      501798 :                 if (extra_lines > extra_row_output_lines)
     861         880 :                     extra_row_output_lines = extra_lines;
     862             :             }
     863             : 
     864             :             /* i is the current column number: increment with wrap */
     865      572778 :             if (++i >= col_count)
     866             :             {
     867      233502 :                 i = 0;
     868             :                 /* At last column of each row, add tallest column height */
     869      233502 :                 extra_output_lines += extra_row_output_lines;
     870      233502 :                 extra_row_output_lines = 0;
     871             :             }
     872             :         }
     873       57220 :         IsPagerNeeded(cont, extra_output_lines, false, &fout, &is_pager);
     874       57220 :         is_local_pager = is_pager;
     875             :     }
     876             : 
     877             :     /* time to output */
     878       57392 :     if (cont->opt->start_table)
     879             :     {
     880             :         /* print title */
     881       57356 :         if (cont->title && !opt_tuples_only)
     882             :         {
     883             :             int         width,
     884             :                         height;
     885             : 
     886        1946 :             pg_wcssize((const unsigned char *) cont->title, strlen(cont->title),
     887             :                        encoding, &width, &height, NULL);
     888        1946 :             if (width >= width_total)
     889             :                 /* Aligned */
     890          98 :                 fprintf(fout, "%s\n", cont->title);
     891             :             else
     892             :                 /* Centered */
     893        1848 :                 fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
     894             :                         cont->title);
     895             :         }
     896             : 
     897             :         /* print headers */
     898       57356 :         if (!opt_tuples_only)
     899             :         {
     900             :             int         more_col_wrapping;
     901             :             int         curr_nl_line;
     902             : 
     903       57340 :             if (opt_border == 2)
     904          32 :                 _print_horizontal_line(col_count, width_wrap, opt_border,
     905             :                                        PRINT_RULE_TOP, format, fout);
     906             : 
     907      165582 :             for (i = 0; i < col_count; i++)
     908      324726 :                 pg_wcsformat((const unsigned char *) cont->headers[i],
     909      108242 :                              strlen(cont->headers[i]), encoding,
     910      216484 :                              col_lineptrs[i], max_nl_lines[i]);
     911             : 
     912       57340 :             more_col_wrapping = col_count;
     913       57340 :             curr_nl_line = 0;
     914       57340 :             memset(header_done, false, col_count * sizeof(bool));
     915      172048 :             while (more_col_wrapping)
     916             :             {
     917       57368 :                 if (opt_border == 2)
     918          64 :                     fputs(dformat->leftvrule, fout);
     919             : 
     920      165802 :                 for (i = 0; i < cont->ncolumns; i++)
     921             :                 {
     922      108434 :                     struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
     923             :                     unsigned int nbspace;
     924             : 
     925      108562 :                     if (opt_border != 0 ||
     926         192 :                         (!format->wrap_right_border && i > 0))
     927      108338 :                         fputs(curr_nl_line ? format->header_nl_left : " ",
     928             :                               fout);
     929             : 
     930      108434 :                     if (!header_done[i])
     931             :                     {
     932      108386 :                         nbspace = width_wrap[i] - this_line->width;
     933             : 
     934             :                         /* centered */
     935      108386 :                         fprintf(fout, "%-*s%s%-*s",
     936      108386 :                                 nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
     937             : 
     938      108386 :                         if (!(this_line + 1)->ptr)
     939             :                         {
     940      108242 :                             more_col_wrapping--;
     941      108242 :                             header_done[i] = 1;
     942             :                         }
     943             :                     }
     944             :                     else
     945          48 :                         fprintf(fout, "%*s", width_wrap[i], "");
     946             : 
     947      108434 :                     if (opt_border != 0 || format->wrap_right_border)
     948      108370 :                         fputs(!header_done[i] ? format->header_nl_right : " ",
     949             :                               fout);
     950             : 
     951      108434 :                     if (opt_border != 0 && col_count > 0 && i < col_count - 1)
     952       51002 :                         fputs(dformat->midvrule, fout);
     953             :                 }
     954       57368 :                 curr_nl_line++;
     955             : 
     956       57368 :                 if (opt_border == 2)
     957          64 :                     fputs(dformat->rightvrule, fout);
     958       57368 :                 fputc('\n', fout);
     959             :             }
     960             : 
     961       57340 :             _print_horizontal_line(col_count, width_wrap, opt_border,
     962             :                                    PRINT_RULE_MIDDLE, format, fout);
     963             :         }
     964             :     }
     965             : 
     966             :     /* print cells, one loop per row */
     967      291386 :     for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
     968             :     {
     969             :         bool        more_lines;
     970             : 
     971      233994 :         if (cancel_pressed)
     972           0 :             break;
     973             : 
     974             :         /*
     975             :          * Format each cell.
     976             :          */
     977      808232 :         for (j = 0; j < col_count; j++)
     978             :         {
     979     1148476 :             pg_wcsformat((const unsigned char *) ptr[j], strlen(ptr[j]), encoding,
     980     1148476 :                          col_lineptrs[j], max_nl_lines[j]);
     981      574238 :             curr_nl_line[j] = 0;
     982             :         }
     983             : 
     984      233994 :         memset(bytes_output, 0, col_count * sizeof(int));
     985             : 
     986             :         /*
     987             :          * Each time through this loop, one display line is output. It can
     988             :          * either be a full value or a partial value if embedded newlines
     989             :          * exist or if 'format=wrapping' mode is enabled.
     990             :          */
     991             :         do
     992             :         {
     993      242090 :             more_lines = false;
     994             : 
     995             :             /* left border */
     996      242090 :             if (opt_border == 2)
     997         368 :                 fputs(dformat->leftvrule, fout);
     998             : 
     999             :             /* for each column */
    1000      826696 :             for (j = 0; j < col_count; j++)
    1001             :             {
    1002             :                 /* We have a valid array element, so index it */
    1003      584606 :                 struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
    1004             :                 int         bytes_to_output;
    1005      584606 :                 int         chars_to_output = width_wrap[j];
    1006      927490 :                 bool        finalspaces = (opt_border == 2 ||
    1007      583870 :                                            (col_count > 0 && j < col_count - 1));
    1008             : 
    1009             :                 /* Print left-hand wrap or newline mark */
    1010      584606 :                 if (opt_border != 0)
    1011             :                 {
    1012      583966 :                     if (wrap[j] == PRINT_LINE_WRAP_WRAP)
    1013          80 :                         fputs(format->wrap_left, fout);
    1014      583886 :                     else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
    1015        8148 :                         fputs(format->nl_left, fout);
    1016             :                     else
    1017      575738 :                         fputc(' ', fout);
    1018             :                 }
    1019             : 
    1020      584606 :                 if (!this_line->ptr)
    1021             :                 {
    1022             :                     /* Past newline lines so just pad for other columns */
    1023        1884 :                     if (finalspaces)
    1024        1588 :                         fprintf(fout, "%*s", chars_to_output, "");
    1025             :                 }
    1026             :                 else
    1027             :                 {
    1028             :                     /* Get strlen() of the characters up to width_wrap */
    1029      582722 :                     bytes_to_output =
    1030      582722 :                         strlen_max_width(this_line->ptr + bytes_output[j],
    1031             :                                          &chars_to_output, encoding);
    1032             : 
    1033             :                     /*
    1034             :                      * If we exceeded width_wrap, it means the display width
    1035             :                      * of a single character was wider than our target width.
    1036             :                      * In that case, we have to pretend we are only printing
    1037             :                      * the target display width and make the best of it.
    1038             :                      */
    1039      582722 :                     if (chars_to_output > width_wrap[j])
    1040           0 :                         chars_to_output = width_wrap[j];
    1041             : 
    1042      582722 :                     if (cont->aligns[j] == 'r') /* Right aligned cell */
    1043             :                     {
    1044             :                         /* spaces first */
    1045      224848 :                         fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
    1046      449696 :                         fputnbytes(fout,
    1047      224848 :                                    (char *) (this_line->ptr + bytes_output[j]),
    1048             :                                    bytes_to_output);
    1049             :                     }
    1050             :                     else        /* Left aligned cell */
    1051             :                     {
    1052             :                         /* spaces second */
    1053      715748 :                         fputnbytes(fout,
    1054      357874 :                                    (char *) (this_line->ptr + bytes_output[j]),
    1055             :                                    bytes_to_output);
    1056             :                     }
    1057             : 
    1058      582722 :                     bytes_output[j] += bytes_to_output;
    1059             : 
    1060             :                     /* Do we have more text to wrap? */
    1061      582722 :                     if (*(this_line->ptr + bytes_output[j]) != '\0')
    1062          80 :                         more_lines = true;
    1063             :                     else
    1064             :                     {
    1065             :                         /* Advance to next newline line */
    1066      582642 :                         curr_nl_line[j]++;
    1067      582642 :                         if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
    1068        8404 :                             more_lines = true;
    1069      582642 :                         bytes_output[j] = 0;
    1070             :                     }
    1071             :                 }
    1072             : 
    1073             :                 /* Determine next line's wrap status for this column */
    1074      584606 :                 wrap[j] = PRINT_LINE_WRAP_NONE;
    1075      584606 :                 if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
    1076             :                 {
    1077        8484 :                     if (bytes_output[j] != 0)
    1078          80 :                         wrap[j] = PRINT_LINE_WRAP_WRAP;
    1079        8404 :                     else if (curr_nl_line[j] != 0)
    1080        8404 :                         wrap[j] = PRINT_LINE_WRAP_NEWLINE;
    1081             :                 }
    1082             : 
    1083             :                 /*
    1084             :                  * If left-aligned, pad out remaining space if needed (not
    1085             :                  * last column, and/or wrap marks required).
    1086             :                  */
    1087      584606 :                 if (cont->aligns[j] != 'r') /* Left aligned cell */
    1088             :                 {
    1089      527154 :                     if (finalspaces ||
    1090      335088 :                         wrap[j] == PRINT_LINE_WRAP_WRAP ||
    1091      167540 :                         wrap[j] == PRINT_LINE_WRAP_NEWLINE)
    1092      199682 :                         fprintf(fout, "%*s",
    1093      199682 :                                 width_wrap[j] - chars_to_output, "");
    1094             :                 }
    1095             : 
    1096             :                 /* Print right-hand wrap or newline mark */
    1097      584606 :                 if (wrap[j] == PRINT_LINE_WRAP_WRAP)
    1098          80 :                     fputs(format->wrap_right, fout);
    1099      584526 :                 else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
    1100        8404 :                     fputs(format->nl_right, fout);
    1101      576122 :                 else if (opt_border == 2 || (col_count > 0 && j < col_count - 1))
    1102      342024 :                     fputc(' ', fout);
    1103             : 
    1104             :                 /* Print column divider, if not the last column */
    1105      584606 :                 if (opt_border != 0 && (col_count > 0 && j < col_count - 1))
    1106             :                 {
    1107      342196 :                     if (wrap[j + 1] == PRINT_LINE_WRAP_WRAP)
    1108          24 :                         fputs(format->midvrule_wrap, fout);
    1109      342172 :                     else if (wrap[j + 1] == PRINT_LINE_WRAP_NEWLINE)
    1110         648 :                         fputs(format->midvrule_nl, fout);
    1111      341524 :                     else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL)
    1112        1472 :                         fputs(format->midvrule_blank, fout);
    1113             :                     else
    1114      340052 :                         fputs(dformat->midvrule, fout);
    1115             :                 }
    1116             :             }
    1117             : 
    1118             :             /* end-of-row border */
    1119      242090 :             if (opt_border == 2)
    1120         368 :                 fputs(dformat->rightvrule, fout);
    1121      242090 :             fputc('\n', fout);
    1122             : 
    1123      242090 :         } while (more_lines);
    1124             :     }
    1125             : 
    1126       57392 :     if (cont->opt->stop_table)
    1127             :     {
    1128       57352 :         printTableFooter *footers = footers_with_default(cont);
    1129             : 
    1130       57352 :         if (opt_border == 2 && !cancel_pressed)
    1131          32 :             _print_horizontal_line(col_count, width_wrap, opt_border,
    1132             :                                    PRINT_RULE_BOTTOM, format, fout);
    1133             : 
    1134             :         /* print footers */
    1135       57352 :         if (footers && !opt_tuples_only && !cancel_pressed)
    1136             :         {
    1137             :             printTableFooter *f;
    1138             : 
    1139      117078 :             for (f = footers; f; f = f->next)
    1140       59950 :                 fprintf(fout, "%s\n", f->data);
    1141             :         }
    1142             : 
    1143       57352 :         fputc('\n', fout);
    1144             :     }
    1145             : 
    1146             : cleanup:
    1147             :     /* clean up */
    1148      165742 :     for (i = 0; i < col_count; i++)
    1149             :     {
    1150      108350 :         free(col_lineptrs[i]);
    1151      108350 :         free(format_buf[i]);
    1152             :     }
    1153       57392 :     free(width_header);
    1154       57392 :     free(width_average);
    1155       57392 :     free(max_width);
    1156       57392 :     free(width_wrap);
    1157       57392 :     free(max_nl_lines);
    1158       57392 :     free(curr_nl_line);
    1159       57392 :     free(col_lineptrs);
    1160       57392 :     free(max_bytes);
    1161       57392 :     free(format_buf);
    1162       57392 :     free(header_done);
    1163       57392 :     free(bytes_output);
    1164       57392 :     free(wrap);
    1165             : 
    1166       57392 :     if (is_local_pager)
    1167         108 :         ClosePager(fout);
    1168             : }
    1169             : 
    1170             : 
    1171             : static void
    1172        1078 : print_aligned_vertical_line(const printTextFormat *format,
    1173             :                             const unsigned short opt_border,
    1174             :                             unsigned long record,
    1175             :                             unsigned int hwidth,
    1176             :                             unsigned int dwidth,
    1177             :                             printTextRule pos,
    1178             :                             FILE *fout)
    1179             : {
    1180        1078 :     const printTextLineFormat *lformat = &format->lrule[pos];
    1181             :     unsigned int i;
    1182        1078 :     int         reclen = 0;
    1183             : 
    1184        1078 :     if (opt_border == 2)
    1185         312 :         fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
    1186         766 :     else if (opt_border == 1)
    1187         494 :         fputs(lformat->hrule, fout);
    1188             : 
    1189        1078 :     if (record)
    1190             :     {
    1191        1030 :         if (opt_border == 0)
    1192         272 :             reclen = fprintf(fout, "* Record %lu", record);
    1193             :         else
    1194         758 :             reclen = fprintf(fout, "[ RECORD %lu ]", record);
    1195             :     }
    1196        1078 :     if (opt_border != 2)
    1197         766 :         reclen++;
    1198        1078 :     if (reclen < 0)
    1199           0 :         reclen = 0;
    1200        5092 :     for (i = reclen; i < hwidth; i++)
    1201        4014 :         fputs(opt_border > 0 ? lformat->hrule : " ", fout);
    1202        1078 :     reclen -= hwidth;
    1203             : 
    1204        1078 :     if (opt_border > 0)
    1205             :     {
    1206         806 :         if (reclen-- <= 0)
    1207         702 :             fputs(lformat->hrule, fout);
    1208         806 :         if (reclen-- <= 0)
    1209         702 :             fputs(lformat->midvrule, fout);
    1210         806 :         if (reclen-- <= 0)
    1211         702 :             fputs(lformat->hrule, fout);
    1212             :     }
    1213             :     else
    1214             :     {
    1215         272 :         if (reclen-- <= 0)
    1216         240 :             fputc(' ', fout);
    1217             :     }
    1218        1078 :     if (reclen < 0)
    1219         942 :         reclen = 0;
    1220       18584 :     for (i = reclen; i < dwidth; i++)
    1221       17506 :         fputs(opt_border > 0 ? lformat->hrule : " ", fout);
    1222        1078 :     if (opt_border == 2)
    1223         312 :         fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
    1224        1078 :     fputc('\n', fout);
    1225        1078 : }
    1226             : 
    1227             : static void
    1228         360 : print_aligned_vertical(const printTableContent *cont,
    1229             :                        FILE *fout, bool is_pager)
    1230             : {
    1231         360 :     bool        opt_tuples_only = cont->opt->tuples_only;
    1232         360 :     unsigned short opt_border = cont->opt->border;
    1233         360 :     const printTextFormat *format = get_line_style(cont->opt);
    1234         360 :     const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
    1235         360 :     int         encoding = cont->opt->encoding;
    1236         360 :     unsigned long record = cont->opt->prior_records + 1;
    1237             :     const char *const *ptr;
    1238             :     unsigned int i,
    1239         360 :                 hwidth = 0,
    1240         360 :                 dwidth = 0,
    1241         360 :                 hheight = 1,
    1242         360 :                 dheight = 1,
    1243         360 :                 hformatsize = 0,
    1244         360 :                 dformatsize = 0;
    1245             :     struct lineptr *hlineptr,
    1246             :                *dlineptr;
    1247         360 :     bool        is_local_pager = false,
    1248         360 :                 hmultiline = false,
    1249         360 :                 dmultiline = false;
    1250         360 :     int         output_columns = 0; /* Width of interactive console */
    1251             : 
    1252         360 :     if (cancel_pressed)
    1253           0 :         return;
    1254             : 
    1255         360 :     if (opt_border > 2)
    1256           0 :         opt_border = 2;
    1257             : 
    1258         370 :     if (cont->cells[0] == NULL && cont->opt->start_table &&
    1259          10 :         cont->opt->stop_table)
    1260             :     {
    1261          10 :         printTableFooter *footers = footers_with_default(cont);
    1262             : 
    1263          10 :         if (!opt_tuples_only && !cancel_pressed && footers)
    1264             :         {
    1265             :             printTableFooter *f;
    1266             : 
    1267          20 :             for (f = footers; f; f = f->next)
    1268          10 :                 fprintf(fout, "%s\n", f->data);
    1269             :         }
    1270             : 
    1271          10 :         fputc('\n', fout);
    1272             : 
    1273          10 :         return;
    1274             :     }
    1275             : 
    1276             :     /*
    1277             :      * Deal with the pager here instead of in printTable(), because we could
    1278             :      * get here via print_aligned_text() in expanded auto mode, and so we have
    1279             :      * to recalculate the pager requirement based on vertical output.
    1280             :      */
    1281         350 :     if (!is_pager)
    1282             :     {
    1283         334 :         IsPagerNeeded(cont, 0, true, &fout, &is_pager);
    1284         334 :         is_local_pager = is_pager;
    1285             :     }
    1286             : 
    1287             :     /* Find the maximum dimensions for the headers */
    1288        2128 :     for (i = 0; i < cont->ncolumns; i++)
    1289             :     {
    1290             :         int         width,
    1291             :                     height,
    1292             :                     fs;
    1293             : 
    1294        1778 :         pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
    1295             :                    encoding, &width, &height, &fs);
    1296        1778 :         if (width > hwidth)
    1297         422 :             hwidth = width;
    1298        1778 :         if (height > hheight)
    1299             :         {
    1300          48 :             hheight = height;
    1301          48 :             hmultiline = true;
    1302             :         }
    1303        1778 :         if (fs > hformatsize)
    1304         422 :             hformatsize = fs;
    1305             :     }
    1306             : 
    1307             :     /* find longest data cell */
    1308        3544 :     for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
    1309             :     {
    1310             :         int         width,
    1311             :                     height,
    1312             :                     fs;
    1313             : 
    1314        3194 :         pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
    1315             :                    &width, &height, &fs);
    1316        3194 :         if (width > dwidth)
    1317         638 :             dwidth = width;
    1318        3194 :         if (height > dheight)
    1319             :         {
    1320          52 :             dheight = height;
    1321          52 :             dmultiline = true;
    1322             :         }
    1323        3194 :         if (fs > dformatsize)
    1324         638 :             dformatsize = fs;
    1325             :     }
    1326             : 
    1327             :     /*
    1328             :      * We now have all the information we need to setup the formatting
    1329             :      * structures
    1330             :      */
    1331         350 :     dlineptr = pg_malloc((sizeof(*dlineptr)) * (dheight + 1));
    1332         350 :     hlineptr = pg_malloc((sizeof(*hlineptr)) * (hheight + 1));
    1333             : 
    1334         350 :     dlineptr->ptr = pg_malloc(dformatsize);
    1335         350 :     hlineptr->ptr = pg_malloc(hformatsize);
    1336             : 
    1337         350 :     if (cont->opt->start_table)
    1338             :     {
    1339             :         /* print title */
    1340         342 :         if (!opt_tuples_only && cont->title)
    1341           8 :             fprintf(fout, "%s\n", cont->title);
    1342             :     }
    1343             : 
    1344             :     /*
    1345             :      * Choose target output width: \pset columns, or $COLUMNS, or ioctl
    1346             :      */
    1347         350 :     if (cont->opt->columns > 0)
    1348         136 :         output_columns = cont->opt->columns;
    1349         214 :     else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
    1350             :     {
    1351          16 :         if (cont->opt->env_columns > 0)
    1352           0 :             output_columns = cont->opt->env_columns;
    1353             : #ifdef TIOCGWINSZ
    1354             :         else
    1355             :         {
    1356             :             struct winsize screen_size;
    1357             : 
    1358          16 :             if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
    1359           0 :                 output_columns = screen_size.ws_col;
    1360             :         }
    1361             : #endif
    1362             :     }
    1363             : 
    1364             :     /*
    1365             :      * Calculate available width for data in wrapped mode
    1366             :      */
    1367         350 :     if (cont->opt->format == PRINT_WRAPPED)
    1368             :     {
    1369             :         unsigned int swidth,
    1370          68 :                     rwidth = 0,
    1371             :                     newdwidth;
    1372             : 
    1373          68 :         if (opt_border == 0)
    1374             :         {
    1375             :             /*
    1376             :              * For border = 0, one space in the middle.  (If we discover we
    1377             :              * need to wrap, the spacer column will be replaced by a wrap
    1378             :              * marker, and we'll make room below for another wrap marker at
    1379             :              * the end of the line.  But for now, assume no wrap is needed.)
    1380             :              */
    1381          20 :             swidth = 1;
    1382             : 
    1383             :             /* We might need a column for header newline markers, too */
    1384          20 :             if (hmultiline)
    1385           8 :                 swidth++;
    1386             :         }
    1387          48 :         else if (opt_border == 1)
    1388             :         {
    1389             :             /*
    1390             :              * For border = 1, two spaces and a vrule in the middle.  (As
    1391             :              * above, we might need one more column for a wrap marker.)
    1392             :              */
    1393          28 :             swidth = 3;
    1394             : 
    1395             :             /* We might need a column for left header newline markers, too */
    1396          28 :             if (hmultiline && (format == &pg_asciiformat_old))
    1397           4 :                 swidth++;
    1398             :         }
    1399             :         else
    1400             :         {
    1401             :             /*
    1402             :              * For border = 2, two more for the vrules at the beginning and
    1403             :              * end of the lines, plus spacer columns adjacent to these.  (We
    1404             :              * won't need extra columns for wrap/newline markers, we'll just
    1405             :              * repurpose the spacers.)
    1406             :              */
    1407          20 :             swidth = 7;
    1408             :         }
    1409             : 
    1410             :         /* Reserve a column for data newline indicators, too, if needed */
    1411          68 :         if (dmultiline &&
    1412          16 :             opt_border < 2 && format != &pg_asciiformat_old)
    1413           8 :             swidth++;
    1414             : 
    1415             :         /* Determine width required for record header lines */
    1416          68 :         if (!opt_tuples_only)
    1417             :         {
    1418          64 :             if (cont->nrows > 0)
    1419          64 :                 rwidth = 1 + (int) log10(cont->nrows);
    1420          64 :             if (opt_border == 0)
    1421          20 :                 rwidth += 9;    /* "* RECORD " */
    1422          44 :             else if (opt_border == 1)
    1423          24 :                 rwidth += 12;   /* "-[ RECORD  ]" */
    1424             :             else
    1425          20 :                 rwidth += 15;   /* "+-[ RECORD  ]-+" */
    1426             :         }
    1427             : 
    1428             :         /* We might need to do the rest of the calculation twice */
    1429             :         for (;;)
    1430          16 :         {
    1431             :             unsigned int width;
    1432             : 
    1433             :             /* Total width required to not wrap data */
    1434          84 :             width = hwidth + swidth + dwidth;
    1435             :             /* ... and not the header lines, either */
    1436          84 :             if (width < rwidth)
    1437           0 :                 width = rwidth;
    1438             : 
    1439          84 :             if (output_columns > 0)
    1440             :             {
    1441             :                 unsigned int min_width;
    1442             : 
    1443             :                 /* Minimum acceptable width: room for just 3 columns of data */
    1444          84 :                 min_width = hwidth + swidth + 3;
    1445             :                 /* ... but not less than what the record header lines need */
    1446          84 :                 if (min_width < rwidth)
    1447          24 :                     min_width = rwidth;
    1448             : 
    1449          84 :                 if (output_columns >= width)
    1450             :                 {
    1451             :                     /* Plenty of room, use native data width */
    1452             :                     /* (but at least enough for the record header lines) */
    1453          16 :                     newdwidth = width - hwidth - swidth;
    1454             :                 }
    1455          68 :                 else if (output_columns < min_width)
    1456             :                 {
    1457             :                     /* Set data width to match min_width */
    1458          16 :                     newdwidth = min_width - hwidth - swidth;
    1459             :                 }
    1460             :                 else
    1461             :                 {
    1462             :                     /* Set data width to match output_columns */
    1463          52 :                     newdwidth = output_columns - hwidth - swidth;
    1464             :                 }
    1465             :             }
    1466             :             else
    1467             :             {
    1468             :                 /* Don't know the wrap limit, so use native data width */
    1469             :                 /* (but at least enough for the record header lines) */
    1470           0 :                 newdwidth = width - hwidth - swidth;
    1471             :             }
    1472             : 
    1473             :             /*
    1474             :              * If we will need to wrap data and didn't already allocate a data
    1475             :              * newline/wrap marker column, do so and recompute.
    1476             :              */
    1477          84 :             if (newdwidth < dwidth && !dmultiline &&
    1478          16 :                 opt_border < 2 && format != &pg_asciiformat_old)
    1479             :             {
    1480          16 :                 dmultiline = true;
    1481          16 :                 swidth++;
    1482             :             }
    1483             :             else
    1484             :                 break;
    1485             :         }
    1486             : 
    1487          68 :         dwidth = newdwidth;
    1488             :     }
    1489             : 
    1490             :     /* print records */
    1491        3544 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    1492             :     {
    1493             :         printTextRule pos;
    1494             :         int         dline,
    1495             :                     hline,
    1496             :                     dcomplete,
    1497             :                     hcomplete,
    1498             :                     offset,
    1499             :                     chars_to_output;
    1500             : 
    1501        3194 :         if (cancel_pressed)
    1502           0 :             break;
    1503             : 
    1504        3194 :         if (i == 0)
    1505         342 :             pos = PRINT_RULE_TOP;
    1506             :         else
    1507        2852 :             pos = PRINT_RULE_MIDDLE;
    1508             : 
    1509             :         /* Print record header (e.g. "[ RECORD N ]") above each record */
    1510        3194 :         if (i % cont->ncolumns == 0)
    1511             :         {
    1512        1046 :             unsigned int lhwidth = hwidth;
    1513             : 
    1514        1046 :             if ((opt_border < 2) &&
    1515          64 :                 (hmultiline) &&
    1516             :                 (format == &pg_asciiformat_old))
    1517          32 :                 lhwidth++;      /* for newline indicators */
    1518             : 
    1519        1046 :             if (!opt_tuples_only)
    1520        1030 :                 print_aligned_vertical_line(format, opt_border, record++,
    1521             :                                             lhwidth, dwidth, pos, fout);
    1522          16 :             else if (i != 0 || !cont->opt->start_table || opt_border == 2)
    1523           8 :                 print_aligned_vertical_line(format, opt_border, 0, lhwidth,
    1524             :                                             dwidth, pos, fout);
    1525             :         }
    1526             : 
    1527             :         /* Format the header */
    1528        6388 :         pg_wcsformat((const unsigned char *) cont->headers[i % cont->ncolumns],
    1529        3194 :                      strlen(cont->headers[i % cont->ncolumns]),
    1530             :                      encoding, hlineptr, hheight);
    1531             :         /* Format the data */
    1532        3194 :         pg_wcsformat((const unsigned char *) *ptr, strlen(*ptr), encoding,
    1533             :                      dlineptr, dheight);
    1534             : 
    1535             :         /*
    1536             :          * Loop through header and data in parallel dealing with newlines and
    1537             :          * wrapped lines until they're both exhausted
    1538             :          */
    1539        3194 :         dline = hline = 0;
    1540        3194 :         dcomplete = hcomplete = 0;
    1541        3194 :         offset = 0;
    1542        3194 :         chars_to_output = dlineptr[dline].width;
    1543       11414 :         while (!dcomplete || !hcomplete)
    1544             :         {
    1545             :             /* Left border */
    1546        5026 :             if (opt_border == 2)
    1547        1212 :                 fprintf(fout, "%s", dformat->leftvrule);
    1548             : 
    1549             :             /* Header (never wrapped so just need to deal with newlines) */
    1550        5026 :             if (!hcomplete)
    1551             :             {
    1552        3482 :                 int         swidth = hwidth,
    1553        3482 :                             target_width = hwidth;
    1554             : 
    1555             :                 /*
    1556             :                  * Left spacer or new line indicator
    1557             :                  */
    1558        3482 :                 if ((opt_border == 2) ||
    1559         320 :                     (hmultiline && (format == &pg_asciiformat_old)))
    1560         800 :                     fputs(hline ? format->header_nl_left : " ", fout);
    1561             : 
    1562             :                 /*
    1563             :                  * Header text
    1564             :                  */
    1565        3482 :                 strlen_max_width(hlineptr[hline].ptr, &target_width,
    1566             :                                  encoding);
    1567        3482 :                 fprintf(fout, "%-s", hlineptr[hline].ptr);
    1568             : 
    1569             :                 /*
    1570             :                  * Spacer
    1571             :                  */
    1572        3482 :                 swidth -= target_width;
    1573        3482 :                 if (swidth > 0)
    1574        2168 :                     fprintf(fout, "%*s", swidth, " ");
    1575             : 
    1576             :                 /*
    1577             :                  * New line indicator or separator's space
    1578             :                  */
    1579        3482 :                 if (hlineptr[hline + 1].ptr)
    1580             :                 {
    1581             :                     /* More lines after this one due to a newline */
    1582         288 :                     if ((opt_border > 0) ||
    1583          96 :                         (hmultiline && (format != &pg_asciiformat_old)))
    1584         240 :                         fputs(format->header_nl_right, fout);
    1585         288 :                     hline++;
    1586             :                 }
    1587             :                 else
    1588             :                 {
    1589             :                     /* This was the last line of the header */
    1590        3194 :                     if ((opt_border > 0) ||
    1591          64 :                         (hmultiline && (format != &pg_asciiformat_old)))
    1592        2682 :                         fputs(" ", fout);
    1593        3194 :                     hcomplete = 1;
    1594             :                 }
    1595             :             }
    1596             :             else
    1597             :             {
    1598        1544 :                 unsigned int swidth = hwidth + opt_border;
    1599             : 
    1600        1544 :                 if ((opt_border < 2) &&
    1601         472 :                     (hmultiline) &&
    1602             :                     (format == &pg_asciiformat_old))
    1603         232 :                     swidth++;
    1604             : 
    1605        1544 :                 if ((opt_border == 0) &&
    1606         364 :                     (format != &pg_asciiformat_old) &&
    1607             :                     (hmultiline))
    1608         120 :                     swidth++;
    1609             : 
    1610        1544 :                 fprintf(fout, "%*s", swidth, " ");
    1611             :             }
    1612             : 
    1613             :             /* Separator */
    1614        5026 :             if (opt_border > 0)
    1615             :             {
    1616        3910 :                 if (offset)
    1617         744 :                     fputs(format->midvrule_wrap, fout);
    1618        3166 :                 else if (dline == 0)
    1619        2650 :                     fputs(dformat->midvrule, fout);
    1620             :                 else
    1621         516 :                     fputs(format->midvrule_nl, fout);
    1622             :             }
    1623             : 
    1624             :             /* Data */
    1625        5026 :             if (!dcomplete)
    1626             :             {
    1627        4906 :                 int         target_width = dwidth,
    1628             :                             bytes_to_output,
    1629        4906 :                             swidth = dwidth;
    1630             : 
    1631             :                 /*
    1632             :                  * Left spacer or wrap indicator
    1633             :                  */
    1634        4906 :                 fputs(offset == 0 ? " " : format->wrap_left, fout);
    1635             : 
    1636             :                 /*
    1637             :                  * Data text
    1638             :                  */
    1639        4906 :                 bytes_to_output = strlen_max_width(dlineptr[dline].ptr + offset,
    1640             :                                                    &target_width, encoding);
    1641        4906 :                 fputnbytes(fout, (char *) (dlineptr[dline].ptr + offset),
    1642             :                            bytes_to_output);
    1643             : 
    1644        4906 :                 chars_to_output -= target_width;
    1645        4906 :                 offset += bytes_to_output;
    1646             : 
    1647             :                 /* Spacer */
    1648        4906 :                 swidth -= target_width;
    1649             : 
    1650        4906 :                 if (chars_to_output)
    1651             :                 {
    1652             :                     /* continuing a wrapped column */
    1653         940 :                     if ((opt_border > 1) ||
    1654         568 :                         (dmultiline && (format != &pg_asciiformat_old)))
    1655             :                     {
    1656         908 :                         if (swidth > 0)
    1657           0 :                             fprintf(fout, "%*s", swidth, " ");
    1658         908 :                         fputs(format->wrap_right, fout);
    1659             :                     }
    1660             :                 }
    1661        3966 :                 else if (dlineptr[dline + 1].ptr)
    1662             :                 {
    1663             :                     /* reached a newline in the column */
    1664         772 :                     if ((opt_border > 1) ||
    1665         516 :                         (dmultiline && (format != &pg_asciiformat_old)))
    1666             :                     {
    1667         516 :                         if (swidth > 0)
    1668         504 :                             fprintf(fout, "%*s", swidth, " ");
    1669         516 :                         fputs(format->nl_right, fout);
    1670             :                     }
    1671         772 :                     dline++;
    1672         772 :                     offset = 0;
    1673         772 :                     chars_to_output = dlineptr[dline].width;
    1674             :                 }
    1675             :                 else
    1676             :                 {
    1677             :                     /* reached the end of the cell */
    1678        3194 :                     if (opt_border > 1)
    1679             :                     {
    1680         544 :                         if (swidth > 0)
    1681         492 :                             fprintf(fout, "%*s", swidth, " ");
    1682         544 :                         fputs(" ", fout);
    1683             :                     }
    1684        3194 :                     dcomplete = 1;
    1685             :                 }
    1686             : 
    1687             :                 /* Right border */
    1688        4906 :                 if (opt_border == 2)
    1689        1172 :                     fputs(dformat->rightvrule, fout);
    1690             : 
    1691        4906 :                 fputs("\n", fout);
    1692             :             }
    1693             :             else
    1694             :             {
    1695             :                 /*
    1696             :                  * data exhausted (this can occur if header is longer than the
    1697             :                  * data due to newlines in the header)
    1698             :                  */
    1699         120 :                 if (opt_border < 2)
    1700          80 :                     fputs("\n", fout);
    1701             :                 else
    1702          40 :                     fprintf(fout, "%*s  %s\n", dwidth, "", dformat->rightvrule);
    1703             :             }
    1704             :         }
    1705             :     }
    1706             : 
    1707         350 :     if (cont->opt->stop_table)
    1708             :     {
    1709         342 :         if (opt_border == 2 && !cancel_pressed)
    1710          40 :             print_aligned_vertical_line(format, opt_border, 0, hwidth, dwidth,
    1711             :                                         PRINT_RULE_BOTTOM, fout);
    1712             : 
    1713             :         /* print footers */
    1714         342 :         if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
    1715             :         {
    1716             :             printTableFooter *f;
    1717             : 
    1718           8 :             if (opt_border < 2)
    1719           8 :                 fputc('\n', fout);
    1720          16 :             for (f = cont->footers; f; f = f->next)
    1721           8 :                 fprintf(fout, "%s\n", f->data);
    1722             :         }
    1723             : 
    1724         342 :         fputc('\n', fout);
    1725             :     }
    1726             : 
    1727         350 :     free(hlineptr->ptr);
    1728         350 :     free(dlineptr->ptr);
    1729         350 :     free(hlineptr);
    1730         350 :     free(dlineptr);
    1731             : 
    1732         350 :     if (is_local_pager)
    1733           0 :         ClosePager(fout);
    1734             : }
    1735             : 
    1736             : 
    1737             : /**********************/
    1738             : /* CSV format         */
    1739             : /**********************/
    1740             : 
    1741             : 
    1742             : static void
    1743          72 : csv_escaped_print(const char *str, FILE *fout)
    1744             : {
    1745             :     const char *p;
    1746             : 
    1747          72 :     fputc('"', fout);
    1748         616 :     for (p = str; *p; p++)
    1749             :     {
    1750         544 :         if (*p == '"')
    1751          28 :             fputc('"', fout);  /* double quotes are doubled */
    1752         544 :         fputc(*p, fout);
    1753             :     }
    1754          72 :     fputc('"', fout);
    1755          72 : }
    1756             : 
    1757             : static void
    1758         400 : csv_print_field(const char *str, FILE *fout, char sep)
    1759             : {
    1760             :     /*----------------
    1761             :      * Enclose and escape field contents when one of these conditions is met:
    1762             :      * - the field separator is found in the contents.
    1763             :      * - the field contains a CR or LF.
    1764             :      * - the field contains a double quote.
    1765             :      * - the field is exactly "\.".
    1766             :      * - the field separator is either "\" or ".".
    1767             :      * The last two cases prevent producing a line that the server's COPY
    1768             :      * command would interpret as an end-of-data marker.  We only really
    1769             :      * need to ensure that the complete line isn't exactly "\.", but for
    1770             :      * simplicity we apply stronger restrictions here.
    1771             :      *----------------
    1772             :      */
    1773         792 :     if (strchr(str, sep) != NULL ||
    1774         740 :         strcspn(str, "\r\n\"") != strlen(str) ||
    1775         692 :         strcmp(str, "\\.") == 0 ||
    1776         344 :         sep == '\\' || sep == '.')
    1777          72 :         csv_escaped_print(str, fout);
    1778             :     else
    1779         328 :         fputs(str, fout);
    1780         400 : }
    1781             : 
    1782             : static void
    1783          28 : print_csv_text(const printTableContent *cont, FILE *fout)
    1784             : {
    1785             :     const char *const *ptr;
    1786             :     int         i;
    1787             : 
    1788          28 :     if (cancel_pressed)
    1789           0 :         return;
    1790             : 
    1791             :     /*
    1792             :      * The title and footer are never printed in csv format. The header is
    1793             :      * printed if opt_tuples_only is false.
    1794             :      *
    1795             :      * Despite RFC 4180 saying that end of lines are CRLF, terminate lines
    1796             :      * with '\n', which prints out as the system-dependent EOL string in text
    1797             :      * mode (typically LF on Unix and CRLF on Windows).
    1798             :      */
    1799          28 :     if (cont->opt->start_table && !cont->opt->tuples_only)
    1800             :     {
    1801             :         /* print headers */
    1802          96 :         for (ptr = cont->headers; *ptr; ptr++)
    1803             :         {
    1804          72 :             if (ptr != cont->headers)
    1805          48 :                 fputc(cont->opt->csvFieldSep[0], fout);
    1806          72 :             csv_print_field(*ptr, fout, cont->opt->csvFieldSep[0]);
    1807             :         }
    1808          24 :         fputc('\n', fout);
    1809             :     }
    1810             : 
    1811             :     /* print cells */
    1812         156 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    1813             :     {
    1814         128 :         csv_print_field(*ptr, fout, cont->opt->csvFieldSep[0]);
    1815         128 :         if ((i + 1) % cont->ncolumns)
    1816          92 :             fputc(cont->opt->csvFieldSep[0], fout);
    1817             :         else
    1818          36 :             fputc('\n', fout);
    1819             :     }
    1820             : }
    1821             : 
    1822             : static void
    1823          12 : print_csv_vertical(const printTableContent *cont, FILE *fout)
    1824             : {
    1825             :     const char *const *ptr;
    1826             :     int         i;
    1827             : 
    1828             :     /* print records */
    1829         112 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    1830             :     {
    1831         100 :         if (cancel_pressed)
    1832           0 :             return;
    1833             : 
    1834             :         /* print name of column */
    1835         100 :         csv_print_field(cont->headers[i % cont->ncolumns], fout,
    1836         100 :                         cont->opt->csvFieldSep[0]);
    1837             : 
    1838             :         /* print field separator */
    1839         100 :         fputc(cont->opt->csvFieldSep[0], fout);
    1840             : 
    1841             :         /* print field value */
    1842         100 :         csv_print_field(*ptr, fout, cont->opt->csvFieldSep[0]);
    1843             : 
    1844         100 :         fputc('\n', fout);
    1845             :     }
    1846             : }
    1847             : 
    1848             : 
    1849             : /**********************/
    1850             : /* HTML               */
    1851             : /**********************/
    1852             : 
    1853             : 
    1854             : void
    1855         548 : html_escaped_print(const char *in, FILE *fout)
    1856             : {
    1857             :     const char *p;
    1858         548 :     bool        leading_space = true;
    1859             : 
    1860        4600 :     for (p = in; *p; p++)
    1861             :     {
    1862        4052 :         switch (*p)
    1863             :         {
    1864             :             case '&':
    1865          36 :                 fputs("&amp;", fout);
    1866          36 :                 break;
    1867             :             case '<':
    1868          96 :                 fputs("&lt;", fout);
    1869          96 :                 break;
    1870             :             case '>':
    1871          96 :                 fputs("&gt;", fout);
    1872          96 :                 break;
    1873             :             case '\n':
    1874          48 :                 fputs("<br />\n", fout);
    1875          48 :                 break;
    1876             :             case '"':
    1877          64 :                 fputs("&quot;", fout);
    1878          64 :                 break;
    1879             :             case ' ':
    1880             :                 /* protect leading space, for EXPLAIN output */
    1881         180 :                 if (leading_space)
    1882          96 :                     fputs("&nbsp;", fout);
    1883             :                 else
    1884          84 :                     fputs(" ", fout);
    1885         180 :                 break;
    1886             :             default:
    1887        3532 :                 fputc(*p, fout);
    1888             :         }
    1889        4052 :         if (*p != ' ')
    1890        3872 :             leading_space = false;
    1891             :     }
    1892         548 : }
    1893             : 
    1894             : 
    1895             : static void
    1896          20 : print_html_text(const printTableContent *cont, FILE *fout)
    1897             : {
    1898          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    1899          20 :     unsigned short opt_border = cont->opt->border;
    1900          20 :     const char *opt_table_attr = cont->opt->tableAttr;
    1901             :     unsigned int i;
    1902             :     const char *const *ptr;
    1903             : 
    1904          20 :     if (cancel_pressed)
    1905           0 :         return;
    1906             : 
    1907          20 :     if (cont->opt->start_table)
    1908             :     {
    1909          20 :         fprintf(fout, "<table border=\"%d\"", opt_border);
    1910          20 :         if (opt_table_attr)
    1911           4 :             fprintf(fout, " %s", opt_table_attr);
    1912          20 :         fputs(">\n", fout);
    1913             : 
    1914             :         /* print title */
    1915          20 :         if (!opt_tuples_only && cont->title)
    1916             :         {
    1917           4 :             fputs("  <caption>", fout);
    1918           4 :             html_escaped_print(cont->title, fout);
    1919           4 :             fputs("</caption>\n", fout);
    1920             :         }
    1921             : 
    1922             :         /* print headers */
    1923          20 :         if (!opt_tuples_only)
    1924             :         {
    1925          16 :             fputs("  <tr>\n", fout);
    1926          92 :             for (ptr = cont->headers; *ptr; ptr++)
    1927             :             {
    1928          76 :                 fputs("    <th align=\"center\">", fout);
    1929          76 :                 html_escaped_print(*ptr, fout);
    1930          76 :                 fputs("</th>\n", fout);
    1931             :             }
    1932          16 :             fputs("  </tr>\n", fout);
    1933             :         }
    1934             :     }
    1935             : 
    1936             :     /* print cells */
    1937         184 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    1938             :     {
    1939         164 :         if (i % cont->ncolumns == 0)
    1940             :         {
    1941          36 :             if (cancel_pressed)
    1942           0 :                 break;
    1943          36 :             fputs("  <tr valign=\"top\">\n", fout);
    1944             :         }
    1945             : 
    1946         164 :         fprintf(fout, "    <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
    1947             :         /* is string only whitespace? */
    1948         164 :         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
    1949          24 :             fputs("&nbsp; ", fout);
    1950             :         else
    1951         140 :             html_escaped_print(*ptr, fout);
    1952             : 
    1953         164 :         fputs("</td>\n", fout);
    1954             : 
    1955         164 :         if ((i + 1) % cont->ncolumns == 0)
    1956          36 :             fputs("  </tr>\n", fout);
    1957             :     }
    1958             : 
    1959          20 :     if (cont->opt->stop_table)
    1960             :     {
    1961          20 :         printTableFooter *footers = footers_with_default(cont);
    1962             : 
    1963          20 :         fputs("</table>\n", fout);
    1964             : 
    1965             :         /* print footers */
    1966          20 :         if (!opt_tuples_only && footers != NULL && !cancel_pressed)
    1967             :         {
    1968             :             printTableFooter *f;
    1969             : 
    1970          16 :             fputs("<p>", fout);
    1971          32 :             for (f = footers; f; f = f->next)
    1972             :             {
    1973          16 :                 html_escaped_print(f->data, fout);
    1974          16 :                 fputs("<br />\n", fout);
    1975             :             }
    1976          16 :             fputs("</p>", fout);
    1977             :         }
    1978             : 
    1979          20 :         fputc('\n', fout);
    1980             :     }
    1981             : }
    1982             : 
    1983             : 
    1984             : static void
    1985          20 : print_html_vertical(const printTableContent *cont, FILE *fout)
    1986             : {
    1987          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    1988          20 :     unsigned short opt_border = cont->opt->border;
    1989          20 :     const char *opt_table_attr = cont->opt->tableAttr;
    1990          20 :     unsigned long record = cont->opt->prior_records + 1;
    1991             :     unsigned int i;
    1992             :     const char *const *ptr;
    1993             : 
    1994          20 :     if (cancel_pressed)
    1995           0 :         return;
    1996             : 
    1997          20 :     if (cont->opt->start_table)
    1998             :     {
    1999          20 :         fprintf(fout, "<table border=\"%d\"", opt_border);
    2000          20 :         if (opt_table_attr)
    2001           4 :             fprintf(fout, " %s", opt_table_attr);
    2002          20 :         fputs(">\n", fout);
    2003             : 
    2004             :         /* print title */
    2005          20 :         if (!opt_tuples_only && cont->title)
    2006             :         {
    2007           4 :             fputs("  <caption>", fout);
    2008           4 :             html_escaped_print(cont->title, fout);
    2009           4 :             fputs("</caption>\n", fout);
    2010             :         }
    2011             :     }
    2012             : 
    2013             :     /* print records */
    2014         184 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2015             :     {
    2016         164 :         if (i % cont->ncolumns == 0)
    2017             :         {
    2018          36 :             if (cancel_pressed)
    2019           0 :                 break;
    2020          36 :             if (!opt_tuples_only)
    2021          28 :                 fprintf(fout,
    2022             :                         "\n  <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
    2023             :                         record++);
    2024             :             else
    2025           8 :                 fputs("\n  <tr><td colspan=\"2\">&nbsp;</td></tr>\n", fout);
    2026             :         }
    2027         164 :         fputs("  <tr valign=\"top\">\n"
    2028             :               "    <th>", fout);
    2029         164 :         html_escaped_print(cont->headers[i % cont->ncolumns], fout);
    2030         164 :         fputs("</th>\n", fout);
    2031             : 
    2032         164 :         fprintf(fout, "    <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
    2033             :         /* is string only whitespace? */
    2034         164 :         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
    2035          24 :             fputs("&nbsp; ", fout);
    2036             :         else
    2037         140 :             html_escaped_print(*ptr, fout);
    2038             : 
    2039         164 :         fputs("</td>\n  </tr>\n", fout);
    2040             :     }
    2041             : 
    2042          20 :     if (cont->opt->stop_table)
    2043             :     {
    2044          20 :         fputs("</table>\n", fout);
    2045             : 
    2046             :         /* print footers */
    2047          20 :         if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
    2048             :         {
    2049             :             printTableFooter *f;
    2050             : 
    2051           4 :             fputs("<p>", fout);
    2052           8 :             for (f = cont->footers; f; f = f->next)
    2053             :             {
    2054           4 :                 html_escaped_print(f->data, fout);
    2055           4 :                 fputs("<br />\n", fout);
    2056             :             }
    2057           4 :             fputs("</p>", fout);
    2058             :         }
    2059             : 
    2060          20 :         fputc('\n', fout);
    2061             :     }
    2062             : }
    2063             : 
    2064             : 
    2065             : /*************************/
    2066             : /* ASCIIDOC              */
    2067             : /*************************/
    2068             : 
    2069             : 
    2070             : static void
    2071         436 : asciidoc_escaped_print(const char *in, FILE *fout)
    2072             : {
    2073             :     const char *p;
    2074             : 
    2075        3060 :     for (p = in; *p; p++)
    2076             :     {
    2077        2624 :         switch (*p)
    2078             :         {
    2079             :             case '|':
    2080          84 :                 fputs("\\|", fout);
    2081          84 :                 break;
    2082             :             default:
    2083        2540 :                 fputc(*p, fout);
    2084             :         }
    2085             :     }
    2086         436 : }
    2087             : 
    2088             : static void
    2089          20 : print_asciidoc_text(const printTableContent *cont, FILE *fout)
    2090             : {
    2091          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2092          20 :     unsigned short opt_border = cont->opt->border;
    2093             :     unsigned int i;
    2094             :     const char *const *ptr;
    2095             : 
    2096          20 :     if (cancel_pressed)
    2097           0 :         return;
    2098             : 
    2099          20 :     if (cont->opt->start_table)
    2100             :     {
    2101             :         /* print table in new paragraph - enforce preliminary new line */
    2102          20 :         fputs("\n", fout);
    2103             : 
    2104             :         /* print title */
    2105          20 :         if (!opt_tuples_only && cont->title)
    2106             :         {
    2107           4 :             fputs(".", fout);
    2108           4 :             fputs(cont->title, fout);
    2109           4 :             fputs("\n", fout);
    2110             :         }
    2111             : 
    2112             :         /* print table [] header definition */
    2113          20 :         fprintf(fout, "[%scols=\"", !opt_tuples_only ? "options=\"header\"," : "");
    2114         104 :         for (i = 0; i < cont->ncolumns; i++)
    2115             :         {
    2116          84 :             if (i != 0)
    2117          64 :                 fputs(",", fout);
    2118          84 :             fprintf(fout, "%s", cont->aligns[(i) % cont->ncolumns] == 'r' ? ">l" : "<l");
    2119             :         }
    2120          20 :         fputs("\"", fout);
    2121          20 :         switch (opt_border)
    2122             :         {
    2123             :             case 0:
    2124           4 :                 fputs(",frame=\"none\",grid=\"none\"", fout);
    2125           4 :                 break;
    2126             :             case 1:
    2127          12 :                 fputs(",frame=\"none\"", fout);
    2128          12 :                 break;
    2129             :             case 2:
    2130           4 :                 fputs(",frame=\"all\",grid=\"all\"", fout);
    2131           4 :                 break;
    2132             :         }
    2133          20 :         fputs("]\n", fout);
    2134          20 :         fputs("|====\n", fout);
    2135             : 
    2136             :         /* print headers */
    2137          20 :         if (!opt_tuples_only)
    2138             :         {
    2139          80 :             for (ptr = cont->headers; *ptr; ptr++)
    2140             :             {
    2141          64 :                 if (ptr != cont->headers)
    2142          48 :                     fputs(" ", fout);
    2143          64 :                 fputs("^l|", fout);
    2144          64 :                 asciidoc_escaped_print(*ptr, fout);
    2145             :             }
    2146          16 :             fputs("\n", fout);
    2147             :         }
    2148             :     }
    2149             : 
    2150             :     /* print cells */
    2151         160 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2152             :     {
    2153         140 :         if (i % cont->ncolumns == 0)
    2154             :         {
    2155          36 :             if (cancel_pressed)
    2156           0 :                 break;
    2157             :         }
    2158             : 
    2159         140 :         if (i % cont->ncolumns != 0)
    2160         104 :             fputs(" ", fout);
    2161         140 :         fputs("|", fout);
    2162             : 
    2163             :         /* protect against needless spaces */
    2164         140 :         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
    2165             :         {
    2166          24 :             if ((i + 1) % cont->ncolumns != 0)
    2167          24 :                 fputs(" ", fout);
    2168             :         }
    2169             :         else
    2170         116 :             asciidoc_escaped_print(*ptr, fout);
    2171             : 
    2172         140 :         if ((i + 1) % cont->ncolumns == 0)
    2173          36 :             fputs("\n", fout);
    2174             :     }
    2175             : 
    2176          20 :     fputs("|====\n", fout);
    2177             : 
    2178          20 :     if (cont->opt->stop_table)
    2179             :     {
    2180          20 :         printTableFooter *footers = footers_with_default(cont);
    2181             : 
    2182             :         /* print footers */
    2183          20 :         if (!opt_tuples_only && footers != NULL && !cancel_pressed)
    2184             :         {
    2185             :             printTableFooter *f;
    2186             : 
    2187          16 :             fputs("\n....\n", fout);
    2188          32 :             for (f = footers; f; f = f->next)
    2189             :             {
    2190          16 :                 fputs(f->data, fout);
    2191          16 :                 fputs("\n", fout);
    2192             :             }
    2193          16 :             fputs("....\n", fout);
    2194             :         }
    2195             :     }
    2196             : }
    2197             : 
    2198             : static void
    2199          20 : print_asciidoc_vertical(const printTableContent *cont, FILE *fout)
    2200             : {
    2201          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2202          20 :     unsigned short opt_border = cont->opt->border;
    2203          20 :     unsigned long record = cont->opt->prior_records + 1;
    2204             :     unsigned int i;
    2205             :     const char *const *ptr;
    2206             : 
    2207          20 :     if (cancel_pressed)
    2208           0 :         return;
    2209             : 
    2210          20 :     if (cont->opt->start_table)
    2211             :     {
    2212             :         /* print table in new paragraph - enforce preliminary new line */
    2213          20 :         fputs("\n", fout);
    2214             : 
    2215             :         /* print title */
    2216          20 :         if (!opt_tuples_only && cont->title)
    2217             :         {
    2218           4 :             fputs(".", fout);
    2219           4 :             fputs(cont->title, fout);
    2220           4 :             fputs("\n", fout);
    2221             :         }
    2222             : 
    2223             :         /* print table [] header definition */
    2224          20 :         fputs("[cols=\"h,l\"", fout);
    2225          20 :         switch (opt_border)
    2226             :         {
    2227             :             case 0:
    2228           4 :                 fputs(",frame=\"none\",grid=\"none\"", fout);
    2229           4 :                 break;
    2230             :             case 1:
    2231          12 :                 fputs(",frame=\"none\"", fout);
    2232          12 :                 break;
    2233             :             case 2:
    2234           4 :                 fputs(",frame=\"all\",grid=\"all\"", fout);
    2235           4 :                 break;
    2236             :         }
    2237          20 :         fputs("]\n", fout);
    2238          20 :         fputs("|====\n", fout);
    2239             :     }
    2240             : 
    2241             :     /* print records */
    2242         160 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2243             :     {
    2244         140 :         if (i % cont->ncolumns == 0)
    2245             :         {
    2246          36 :             if (cancel_pressed)
    2247           0 :                 break;
    2248          36 :             if (!opt_tuples_only)
    2249          28 :                 fprintf(fout,
    2250             :                         "2+^|Record %lu\n",
    2251             :                         record++);
    2252             :             else
    2253           8 :                 fputs("2+|\n", fout);
    2254             :         }
    2255             : 
    2256         140 :         fputs("<l|", fout);
    2257         140 :         asciidoc_escaped_print(cont->headers[i % cont->ncolumns], fout);
    2258             : 
    2259         140 :         fprintf(fout, " %s|", cont->aligns[i % cont->ncolumns] == 'r' ? ">l" : "<l");
    2260             :         /* is string only whitespace? */
    2261         140 :         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
    2262          24 :             fputs(" ", fout);
    2263             :         else
    2264         116 :             asciidoc_escaped_print(*ptr, fout);
    2265         140 :         fputs("\n", fout);
    2266             :     }
    2267             : 
    2268          20 :     fputs("|====\n", fout);
    2269             : 
    2270          20 :     if (cont->opt->stop_table)
    2271             :     {
    2272             :         /* print footers */
    2273          20 :         if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
    2274             :         {
    2275             :             printTableFooter *f;
    2276             : 
    2277           4 :             fputs("\n....\n", fout);
    2278           8 :             for (f = cont->footers; f; f = f->next)
    2279             :             {
    2280           4 :                 fputs(f->data, fout);
    2281           4 :                 fputs("\n", fout);
    2282             :             }
    2283           4 :             fputs("....\n", fout);
    2284             :         }
    2285             :     }
    2286             : }
    2287             : 
    2288             : 
    2289             : /*************************/
    2290             : /* LaTeX                 */
    2291             : /*************************/
    2292             : 
    2293             : 
    2294             : static void
    2295        1636 : latex_escaped_print(const char *in, FILE *fout)
    2296             : {
    2297             :     const char *p;
    2298             : 
    2299       14376 :     for (p = in; *p; p++)
    2300       12740 :         switch (*p)
    2301             :         {
    2302             :                 /*
    2303             :                  * We convert ASCII characters per the recommendations in
    2304             :                  * Scott Pakin's "The Comprehensive LATEX Symbol List",
    2305             :                  * available from CTAN.  For non-ASCII, you're on your own.
    2306             :                  */
    2307             :             case '#':
    2308         144 :                 fputs("\\#", fout);
    2309         144 :                 break;
    2310             :             case '$':
    2311         128 :                 fputs("\\$", fout);
    2312         128 :                 break;
    2313             :             case '%':
    2314         144 :                 fputs("\\%", fout);
    2315         144 :                 break;
    2316             :             case '&':
    2317         144 :                 fputs("\\&", fout);
    2318         144 :                 break;
    2319             :             case '<':
    2320         144 :                 fputs("\\textless{}", fout);
    2321         144 :                 break;
    2322             :             case '>':
    2323         144 :                 fputs("\\textgreater{}", fout);
    2324         144 :                 break;
    2325             :             case '\\':
    2326         144 :                 fputs("\\textbackslash{}", fout);
    2327         144 :                 break;
    2328             :             case '^':
    2329         144 :                 fputs("\\^{}", fout);
    2330         144 :                 break;
    2331             :             case '_':
    2332         312 :                 fputs("\\_", fout);
    2333         312 :                 break;
    2334             :             case '{':
    2335         144 :                 fputs("\\{", fout);
    2336         144 :                 break;
    2337             :             case '|':
    2338         144 :                 fputs("\\textbar{}", fout);
    2339         144 :                 break;
    2340             :             case '}':
    2341         144 :                 fputs("\\}", fout);
    2342         144 :                 break;
    2343             :             case '~':
    2344         144 :                 fputs("\\~{}", fout);
    2345         144 :                 break;
    2346             :             case '\n':
    2347             :                 /* This is not right, but doing it right seems too hard */
    2348         144 :                 fputs("\\\\", fout);
    2349         144 :                 break;
    2350             :             default:
    2351       10572 :                 fputc(*p, fout);
    2352             :         }
    2353        1636 : }
    2354             : 
    2355             : 
    2356             : static void
    2357          24 : print_latex_text(const printTableContent *cont, FILE *fout)
    2358             : {
    2359          24 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2360          24 :     unsigned short opt_border = cont->opt->border;
    2361             :     unsigned int i;
    2362             :     const char *const *ptr;
    2363             : 
    2364          24 :     if (cancel_pressed)
    2365           0 :         return;
    2366             : 
    2367          24 :     if (opt_border > 3)
    2368           0 :         opt_border = 3;
    2369             : 
    2370          24 :     if (cont->opt->start_table)
    2371             :     {
    2372             :         /* print title */
    2373          24 :         if (!opt_tuples_only && cont->title)
    2374             :         {
    2375           4 :             fputs("\\begin{center}\n", fout);
    2376           4 :             latex_escaped_print(cont->title, fout);
    2377           4 :             fputs("\n\\end{center}\n\n", fout);
    2378             :         }
    2379             : 
    2380             :         /* begin environment and set alignments and borders */
    2381          24 :         fputs("\\begin{tabular}{", fout);
    2382             : 
    2383          24 :         if (opt_border >= 2)
    2384           8 :             fputs("| ", fout);
    2385         136 :         for (i = 0; i < cont->ncolumns; i++)
    2386             :         {
    2387         112 :             fputc(*(cont->aligns + i), fout);
    2388         112 :             if (opt_border != 0 && i < cont->ncolumns - 1)
    2389          76 :                 fputs(" | ", fout);
    2390             :         }
    2391          24 :         if (opt_border >= 2)
    2392           8 :             fputs(" |", fout);
    2393             : 
    2394          24 :         fputs("}\n", fout);
    2395             : 
    2396          24 :         if (!opt_tuples_only && opt_border >= 2)
    2397           8 :             fputs("\\hline\n", fout);
    2398             : 
    2399             :         /* print headers */
    2400          24 :         if (!opt_tuples_only)
    2401             :         {
    2402         112 :             for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
    2403             :             {
    2404          92 :                 if (i != 0)
    2405          72 :                     fputs(" & ", fout);
    2406          92 :                 fputs("\\textit{", fout);
    2407          92 :                 latex_escaped_print(*ptr, fout);
    2408          92 :                 fputc('}', fout);
    2409             :             }
    2410          20 :             fputs(" \\\\\n", fout);
    2411          20 :             fputs("\\hline\n", fout);
    2412             :         }
    2413             :     }
    2414             : 
    2415             :     /* print cells */
    2416         220 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2417             :     {
    2418         196 :         latex_escaped_print(*ptr, fout);
    2419             : 
    2420         196 :         if ((i + 1) % cont->ncolumns == 0)
    2421             :         {
    2422          44 :             fputs(" \\\\\n", fout);
    2423          44 :             if (opt_border == 3)
    2424           8 :                 fputs("\\hline\n", fout);
    2425          44 :             if (cancel_pressed)
    2426           0 :                 break;
    2427             :         }
    2428             :         else
    2429         152 :             fputs(" & ", fout);
    2430             :     }
    2431             : 
    2432          24 :     if (cont->opt->stop_table)
    2433             :     {
    2434          24 :         printTableFooter *footers = footers_with_default(cont);
    2435             : 
    2436          24 :         if (opt_border == 2)
    2437           4 :             fputs("\\hline\n", fout);
    2438             : 
    2439          24 :         fputs("\\end{tabular}\n\n\\noindent ", fout);
    2440             : 
    2441             :         /* print footers */
    2442          24 :         if (footers && !opt_tuples_only && !cancel_pressed)
    2443             :         {
    2444             :             printTableFooter *f;
    2445             : 
    2446          40 :             for (f = footers; f; f = f->next)
    2447             :             {
    2448          20 :                 latex_escaped_print(f->data, fout);
    2449          20 :                 fputs(" \\\\\n", fout);
    2450             :             }
    2451             :         }
    2452             : 
    2453          24 :         fputc('\n', fout);
    2454             :     }
    2455             : }
    2456             : 
    2457             : 
    2458             : /*************************/
    2459             : /* LaTeX longtable       */
    2460             : /*************************/
    2461             : 
    2462             : 
    2463             : static void
    2464          28 : print_latex_longtable_text(const printTableContent *cont, FILE *fout)
    2465             : {
    2466          28 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2467          28 :     unsigned short opt_border = cont->opt->border;
    2468             :     unsigned int i;
    2469          28 :     const char *opt_table_attr = cont->opt->tableAttr;
    2470          28 :     const char *next_opt_table_attr_char = opt_table_attr;
    2471          28 :     const char *last_opt_table_attr_char = NULL;
    2472             :     const char *const *ptr;
    2473             : 
    2474          28 :     if (cancel_pressed)
    2475           0 :         return;
    2476             : 
    2477          28 :     if (opt_border > 3)
    2478           0 :         opt_border = 3;
    2479             : 
    2480          28 :     if (cont->opt->start_table)
    2481             :     {
    2482             :         /* begin environment and set alignments and borders */
    2483          28 :         fputs("\\begin{longtable}{", fout);
    2484             : 
    2485          28 :         if (opt_border >= 2)
    2486          12 :             fputs("| ", fout);
    2487             : 
    2488         156 :         for (i = 0; i < cont->ncolumns; i++)
    2489             :         {
    2490             :             /* longtable supports either a width (p) or an alignment (l/r) */
    2491             :             /* Are we left-justified and was a proportional width specified? */
    2492         128 :             if (*(cont->aligns + i) == 'l' && opt_table_attr)
    2493             :             {
    2494             : #define LONGTABLE_WHITESPACE    " \t\n"
    2495             : 
    2496             :                 /* advance over whitespace */
    2497          12 :                 next_opt_table_attr_char += strspn(next_opt_table_attr_char,
    2498             :                                                    LONGTABLE_WHITESPACE);
    2499             :                 /* We have a value? */
    2500          24 :                 if (next_opt_table_attr_char[0] != '\0')
    2501             :                 {
    2502           4 :                     fputs("p{", fout);
    2503           4 :                     fwrite(next_opt_table_attr_char, strcspn(next_opt_table_attr_char,
    2504             :                                                              LONGTABLE_WHITESPACE), 1, fout);
    2505           4 :                     last_opt_table_attr_char = next_opt_table_attr_char;
    2506           4 :                     next_opt_table_attr_char += strcspn(next_opt_table_attr_char,
    2507             :                                                         LONGTABLE_WHITESPACE);
    2508           4 :                     fputs("\\textwidth}", fout);
    2509             :                 }
    2510             :                 /* use previous value */
    2511           8 :                 else if (last_opt_table_attr_char != NULL)
    2512             :                 {
    2513           8 :                     fputs("p{", fout);
    2514           8 :                     fwrite(last_opt_table_attr_char, strcspn(last_opt_table_attr_char,
    2515             :                                                              LONGTABLE_WHITESPACE), 1, fout);
    2516           8 :                     fputs("\\textwidth}", fout);
    2517             :                 }
    2518             :                 else
    2519           0 :                     fputc('l', fout);
    2520             :             }
    2521             :             else
    2522         116 :                 fputc(*(cont->aligns + i), fout);
    2523             : 
    2524         128 :             if (opt_border != 0 && i < cont->ncolumns - 1)
    2525          88 :                 fputs(" | ", fout);
    2526             :         }
    2527             : 
    2528          28 :         if (opt_border >= 2)
    2529          12 :             fputs(" |", fout);
    2530             : 
    2531          28 :         fputs("}\n", fout);
    2532             : 
    2533             :         /* print headers */
    2534          28 :         if (!opt_tuples_only)
    2535             :         {
    2536             :             /* firsthead */
    2537          24 :             if (opt_border >= 2)
    2538          12 :                 fputs("\\toprule\n", fout);
    2539         132 :             for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
    2540             :             {
    2541         108 :                 if (i != 0)
    2542          84 :                     fputs(" & ", fout);
    2543         108 :                 fputs("\\small\\textbf{\\textit{", fout);
    2544         108 :                 latex_escaped_print(*ptr, fout);
    2545         108 :                 fputs("}}", fout);
    2546             :             }
    2547          24 :             fputs(" \\\\\n", fout);
    2548          24 :             fputs("\\midrule\n\\endfirsthead\n", fout);
    2549             : 
    2550             :             /* secondary heads */
    2551          24 :             if (opt_border >= 2)
    2552          12 :                 fputs("\\toprule\n", fout);
    2553         132 :             for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
    2554             :             {
    2555         108 :                 if (i != 0)
    2556          84 :                     fputs(" & ", fout);
    2557         108 :                 fputs("\\small\\textbf{\\textit{", fout);
    2558         108 :                 latex_escaped_print(*ptr, fout);
    2559         108 :                 fputs("}}", fout);
    2560             :             }
    2561          24 :             fputs(" \\\\\n", fout);
    2562             :             /* If the line under the row already appeared, don't do another */
    2563          24 :             if (opt_border != 3)
    2564          16 :                 fputs("\\midrule\n", fout);
    2565          24 :             fputs("\\endhead\n", fout);
    2566             : 
    2567             :             /* table name, caption? */
    2568          24 :             if (!opt_tuples_only && cont->title)
    2569             :             {
    2570             :                 /* Don't output if we are printing a line under each row */
    2571           4 :                 if (opt_border == 2)
    2572           0 :                     fputs("\\bottomrule\n", fout);
    2573           4 :                 fputs("\\caption[", fout);
    2574           4 :                 latex_escaped_print(cont->title, fout);
    2575           4 :                 fputs(" (Continued)]{", fout);
    2576           4 :                 latex_escaped_print(cont->title, fout);
    2577           4 :                 fputs("}\n\\endfoot\n", fout);
    2578           4 :                 if (opt_border == 2)
    2579           0 :                     fputs("\\bottomrule\n", fout);
    2580           4 :                 fputs("\\caption[", fout);
    2581           4 :                 latex_escaped_print(cont->title, fout);
    2582           4 :                 fputs("]{", fout);
    2583           4 :                 latex_escaped_print(cont->title, fout);
    2584           4 :                 fputs("}\n\\endlastfoot\n", fout);
    2585             :             }
    2586             :             /* output bottom table line? */
    2587          20 :             else if (opt_border >= 2)
    2588             :             {
    2589          12 :                 fputs("\\bottomrule\n\\endfoot\n", fout);
    2590          12 :                 fputs("\\bottomrule\n\\endlastfoot\n", fout);
    2591             :             }
    2592             :         }
    2593             :     }
    2594             : 
    2595             :     /* print cells */
    2596         256 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2597             :     {
    2598             :         /* Add a line under each row? */
    2599         228 :         if (i != 0 && i % cont->ncolumns != 0)
    2600         176 :             fputs("\n&\n", fout);
    2601         228 :         fputs("\\raggedright{", fout);
    2602         228 :         latex_escaped_print(*ptr, fout);
    2603         228 :         fputc('}', fout);
    2604         228 :         if ((i + 1) % cont->ncolumns == 0)
    2605             :         {
    2606          52 :             fputs(" \\tabularnewline\n", fout);
    2607          52 :             if (opt_border == 3)
    2608          16 :                 fputs(" \\hline\n", fout);
    2609             :         }
    2610         228 :         if (cancel_pressed)
    2611           0 :             break;
    2612             :     }
    2613             : 
    2614          28 :     if (cont->opt->stop_table)
    2615          28 :         fputs("\\end{longtable}\n", fout);
    2616             : }
    2617             : 
    2618             : 
    2619             : static void
    2620          52 : print_latex_vertical(const printTableContent *cont, FILE *fout)
    2621             : {
    2622          52 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2623          52 :     unsigned short opt_border = cont->opt->border;
    2624          52 :     unsigned long record = cont->opt->prior_records + 1;
    2625             :     unsigned int i;
    2626             :     const char *const *ptr;
    2627             : 
    2628          52 :     if (cancel_pressed)
    2629           0 :         return;
    2630             : 
    2631          52 :     if (opt_border > 2)
    2632          12 :         opt_border = 2;
    2633             : 
    2634          52 :     if (cont->opt->start_table)
    2635             :     {
    2636             :         /* print title */
    2637          52 :         if (!opt_tuples_only && cont->title)
    2638             :         {
    2639           8 :             fputs("\\begin{center}\n", fout);
    2640           8 :             latex_escaped_print(cont->title, fout);
    2641           8 :             fputs("\n\\end{center}\n\n", fout);
    2642             :         }
    2643             : 
    2644             :         /* begin environment and set alignments and borders */
    2645          52 :         fputs("\\begin{tabular}{", fout);
    2646          52 :         if (opt_border == 0)
    2647           8 :             fputs("cl", fout);
    2648          44 :         else if (opt_border == 1)
    2649          24 :             fputs("c|l", fout);
    2650          20 :         else if (opt_border == 2)
    2651          20 :             fputs("|c|l|", fout);
    2652          52 :         fputs("}\n", fout);
    2653             :     }
    2654             : 
    2655             :     /* print records */
    2656         476 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2657             :     {
    2658             :         /* new record */
    2659         424 :         if (i % cont->ncolumns == 0)
    2660             :         {
    2661          96 :             if (cancel_pressed)
    2662           0 :                 break;
    2663          96 :             if (!opt_tuples_only)
    2664             :             {
    2665          80 :                 if (opt_border == 2)
    2666             :                 {
    2667          40 :                     fputs("\\hline\n", fout);
    2668          40 :                     fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
    2669             :                 }
    2670             :                 else
    2671          40 :                     fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
    2672             :             }
    2673          96 :             if (opt_border >= 1)
    2674          80 :                 fputs("\\hline\n", fout);
    2675             :         }
    2676             : 
    2677         424 :         latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
    2678         424 :         fputs(" & ", fout);
    2679         424 :         latex_escaped_print(*ptr, fout);
    2680         424 :         fputs(" \\\\\n", fout);
    2681             :     }
    2682             : 
    2683          52 :     if (cont->opt->stop_table)
    2684             :     {
    2685          52 :         if (opt_border == 2)
    2686          20 :             fputs("\\hline\n", fout);
    2687             : 
    2688          52 :         fputs("\\end{tabular}\n\n\\noindent ", fout);
    2689             : 
    2690             :         /* print footers */
    2691          52 :         if (cont->footers && !opt_tuples_only && !cancel_pressed)
    2692             :         {
    2693             :             printTableFooter *f;
    2694             : 
    2695          16 :             for (f = cont->footers; f; f = f->next)
    2696             :             {
    2697           8 :                 latex_escaped_print(f->data, fout);
    2698           8 :                 fputs(" \\\\\n", fout);
    2699             :             }
    2700             :         }
    2701             : 
    2702          52 :         fputc('\n', fout);
    2703             :     }
    2704             : }
    2705             : 
    2706             : 
    2707             : /*************************/
    2708             : /* Troff -ms             */
    2709             : /*************************/
    2710             : 
    2711             : 
    2712             : static void
    2713         596 : troff_ms_escaped_print(const char *in, FILE *fout)
    2714             : {
    2715             :     const char *p;
    2716             : 
    2717        4792 :     for (p = in; *p; p++)
    2718        4196 :         switch (*p)
    2719             :         {
    2720             :             case '\\':
    2721          84 :                 fputs("\\(rs", fout);
    2722          84 :                 break;
    2723             :             default:
    2724        4112 :                 fputc(*p, fout);
    2725             :         }
    2726         596 : }
    2727             : 
    2728             : 
    2729             : static void
    2730          20 : print_troff_ms_text(const printTableContent *cont, FILE *fout)
    2731             : {
    2732          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2733          20 :     unsigned short opt_border = cont->opt->border;
    2734             :     unsigned int i;
    2735             :     const char *const *ptr;
    2736             : 
    2737          20 :     if (cancel_pressed)
    2738           0 :         return;
    2739             : 
    2740          20 :     if (opt_border > 2)
    2741           0 :         opt_border = 2;
    2742             : 
    2743          20 :     if (cont->opt->start_table)
    2744             :     {
    2745             :         /* print title */
    2746          20 :         if (!opt_tuples_only && cont->title)
    2747             :         {
    2748           4 :             fputs(".LP\n.DS C\n", fout);
    2749           4 :             troff_ms_escaped_print(cont->title, fout);
    2750           4 :             fputs("\n.DE\n", fout);
    2751             :         }
    2752             : 
    2753             :         /* begin environment and set alignments and borders */
    2754          20 :         fputs(".LP\n.TS\n", fout);
    2755          20 :         if (opt_border == 2)
    2756           4 :             fputs("center box;\n", fout);
    2757             :         else
    2758          16 :             fputs("center;\n", fout);
    2759             : 
    2760         116 :         for (i = 0; i < cont->ncolumns; i++)
    2761             :         {
    2762          96 :             fputc(*(cont->aligns + i), fout);
    2763          96 :             if (opt_border > 0 && i < cont->ncolumns - 1)
    2764          64 :                 fputs(" | ", fout);
    2765             :         }
    2766          20 :         fputs(".\n", fout);
    2767             : 
    2768             :         /* print headers */
    2769          20 :         if (!opt_tuples_only)
    2770             :         {
    2771          92 :             for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
    2772             :             {
    2773          76 :                 if (i != 0)
    2774          60 :                     fputc('\t', fout);
    2775          76 :                 fputs("\\fI", fout);
    2776          76 :                 troff_ms_escaped_print(*ptr, fout);
    2777          76 :                 fputs("\\fP", fout);
    2778             :             }
    2779          16 :             fputs("\n_\n", fout);
    2780             :         }
    2781             :     }
    2782             : 
    2783             :     /* print cells */
    2784         184 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2785             :     {
    2786         164 :         troff_ms_escaped_print(*ptr, fout);
    2787             : 
    2788         164 :         if ((i + 1) % cont->ncolumns == 0)
    2789             :         {
    2790          36 :             fputc('\n', fout);
    2791          36 :             if (cancel_pressed)
    2792           0 :                 break;
    2793             :         }
    2794             :         else
    2795         128 :             fputc('\t', fout);
    2796             :     }
    2797             : 
    2798          20 :     if (cont->opt->stop_table)
    2799             :     {
    2800          20 :         printTableFooter *footers = footers_with_default(cont);
    2801             : 
    2802          20 :         fputs(".TE\n.DS L\n", fout);
    2803             : 
    2804             :         /* print footers */
    2805          20 :         if (footers && !opt_tuples_only && !cancel_pressed)
    2806             :         {
    2807             :             printTableFooter *f;
    2808             : 
    2809          32 :             for (f = footers; f; f = f->next)
    2810             :             {
    2811          16 :                 troff_ms_escaped_print(f->data, fout);
    2812          16 :                 fputc('\n', fout);
    2813             :             }
    2814             :         }
    2815             : 
    2816          20 :         fputs(".DE\n", fout);
    2817             :     }
    2818             : }
    2819             : 
    2820             : 
    2821             : static void
    2822          20 : print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
    2823             : {
    2824          20 :     bool        opt_tuples_only = cont->opt->tuples_only;
    2825          20 :     unsigned short opt_border = cont->opt->border;
    2826          20 :     unsigned long record = cont->opt->prior_records + 1;
    2827             :     unsigned int i;
    2828             :     const char *const *ptr;
    2829          20 :     unsigned short current_format = 0;  /* 0=none, 1=header, 2=body */
    2830             : 
    2831          20 :     if (cancel_pressed)
    2832           0 :         return;
    2833             : 
    2834          20 :     if (opt_border > 2)
    2835           0 :         opt_border = 2;
    2836             : 
    2837          20 :     if (cont->opt->start_table)
    2838             :     {
    2839             :         /* print title */
    2840          20 :         if (!opt_tuples_only && cont->title)
    2841             :         {
    2842           4 :             fputs(".LP\n.DS C\n", fout);
    2843           4 :             troff_ms_escaped_print(cont->title, fout);
    2844           4 :             fputs("\n.DE\n", fout);
    2845             :         }
    2846             : 
    2847             :         /* begin environment and set alignments and borders */
    2848          20 :         fputs(".LP\n.TS\n", fout);
    2849          20 :         if (opt_border == 2)
    2850           4 :             fputs("center box;\n", fout);
    2851             :         else
    2852          16 :             fputs("center;\n", fout);
    2853             : 
    2854             :         /* basic format */
    2855          20 :         if (opt_tuples_only)
    2856           4 :             fputs("c l;\n", fout);
    2857             :     }
    2858             :     else
    2859           0 :         current_format = 2;     /* assume tuples printed already */
    2860             : 
    2861             :     /* print records */
    2862         184 :     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
    2863             :     {
    2864             :         /* new record */
    2865         164 :         if (i % cont->ncolumns == 0)
    2866             :         {
    2867          36 :             if (cancel_pressed)
    2868           0 :                 break;
    2869          36 :             if (!opt_tuples_only)
    2870             :             {
    2871          28 :                 if (current_format != 1)
    2872             :                 {
    2873          28 :                     if (opt_border == 2 && record > 1)
    2874           4 :                         fputs("_\n", fout);
    2875          28 :                     if (current_format != 0)
    2876          12 :                         fputs(".T&\n", fout);
    2877          28 :                     fputs("c s.\n", fout);
    2878          28 :                     current_format = 1;
    2879             :                 }
    2880          28 :                 fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
    2881             :             }
    2882          36 :             if (opt_border >= 1)
    2883          28 :                 fputs("_\n", fout);
    2884             :         }
    2885             : 
    2886         164 :         if (!opt_tuples_only)
    2887             :         {
    2888         124 :             if (current_format != 2)
    2889             :             {
    2890          28 :                 if (current_format != 0)
    2891          28 :                     fputs(".T&\n", fout);
    2892          28 :                 if (opt_border != 1)
    2893          16 :                     fputs("c l.\n", fout);
    2894             :                 else
    2895          12 :                     fputs("c | l.\n", fout);
    2896          28 :                 current_format = 2;
    2897             :             }
    2898             :         }
    2899             : 
    2900         164 :         troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
    2901         164 :         fputc('\t', fout);
    2902         164 :         troff_ms_escaped_print(*ptr, fout);
    2903             : 
    2904         164 :         fputc('\n', fout);
    2905             :     }
    2906             : 
    2907          20 :     if (cont->opt->stop_table)
    2908             :     {
    2909          20 :         fputs(".TE\n.DS L\n", fout);
    2910             : 
    2911             :         /* print footers */
    2912          20 :         if (cont->footers && !opt_tuples_only && !cancel_pressed)
    2913             :         {
    2914             :             printTableFooter *f;
    2915             : 
    2916           8 :             for (f = cont->footers; f; f = f->next)
    2917             :             {
    2918           4 :                 troff_ms_escaped_print(f->data, fout);
    2919           4 :                 fputc('\n', fout);
    2920             :             }
    2921             :         }
    2922             : 
    2923          20 :         fputs(".DE\n", fout);
    2924             :     }
    2925             : }
    2926             : 
    2927             : 
    2928             : /********************************/
    2929             : /* Public functions             */
    2930             : /********************************/
    2931             : 
    2932             : 
    2933             : /*
    2934             :  * disable_sigpipe_trap
    2935             :  *
    2936             :  * Turn off SIGPIPE interrupt --- call this before writing to a temporary
    2937             :  * query output file that is a pipe.
    2938             :  *
    2939             :  * No-op on Windows, where there's no SIGPIPE interrupts.
    2940             :  */
    2941             : void
    2942           0 : disable_sigpipe_trap(void)
    2943             : {
    2944             : #ifndef WIN32
    2945           0 :     pqsignal(SIGPIPE, SIG_IGN);
    2946             : #endif
    2947           0 : }
    2948             : 
    2949             : /*
    2950             :  * restore_sigpipe_trap
    2951             :  *
    2952             :  * Restore normal SIGPIPE interrupt --- call this when done writing to a
    2953             :  * temporary query output file that was (or might have been) a pipe.
    2954             :  *
    2955             :  * Note: within psql, we enable SIGPIPE interrupts unless the permanent query
    2956             :  * output file is a pipe, in which case they should be kept off.  This
    2957             :  * approach works only because psql is not currently complicated enough to
    2958             :  * have nested usages of short-lived output files.  Otherwise we'd probably
    2959             :  * need a genuine save-and-restore-state approach; but for now, that would be
    2960             :  * useless complication.  In non-psql programs, this always enables SIGPIPE.
    2961             :  *
    2962             :  * No-op on Windows, where there's no SIGPIPE interrupts.
    2963             :  */
    2964             : void
    2965        3810 : restore_sigpipe_trap(void)
    2966             : {
    2967             : #ifndef WIN32
    2968        3810 :     pqsignal(SIGPIPE, always_ignore_sigpipe ? SIG_IGN : SIG_DFL);
    2969             : #endif
    2970        3810 : }
    2971             : 
    2972             : /*
    2973             :  * set_sigpipe_trap_state
    2974             :  *
    2975             :  * Set the trap state that restore_sigpipe_trap should restore to.
    2976             :  */
    2977             : void
    2978        3810 : set_sigpipe_trap_state(bool ignore)
    2979             : {
    2980        3810 :     always_ignore_sigpipe = ignore;
    2981        3810 : }
    2982             : 
    2983             : 
    2984             : /*
    2985             :  * PageOutput
    2986             :  *
    2987             :  * Tests if pager is needed and returns appropriate FILE pointer.
    2988             :  *
    2989             :  * If the topt argument is NULL no pager is used.
    2990             :  */
    2991             : FILE *
    2992       59232 : PageOutput(int lines, const printTableOpt *topt)
    2993             : {
    2994             :     /* check whether we need / can / are supposed to use pager */
    2995       59232 :     if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
    2996             :     {
    2997             : #ifdef TIOCGWINSZ
    2998           0 :         unsigned short int pager = topt->pager;
    2999           0 :         int         min_lines = topt->pager_min_lines;
    3000             :         int         result;
    3001             :         struct winsize screen_size;
    3002             : 
    3003           0 :         result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
    3004             : 
    3005             :         /* >= accounts for a one-line prompt */
    3006           0 :         if (result == -1
    3007           0 :             || (lines >= screen_size.ws_row && lines >= min_lines)
    3008           0 :             || pager > 1)
    3009             : #endif
    3010             :         {
    3011             :             const char *pagerprog;
    3012             :             FILE       *pagerpipe;
    3013             : 
    3014           0 :             pagerprog = getenv("PSQL_PAGER");
    3015           0 :             if (!pagerprog)
    3016           0 :                 pagerprog = getenv("PAGER");
    3017           0 :             if (!pagerprog)
    3018           0 :                 pagerprog = DEFAULT_PAGER;
    3019             :             else
    3020             :             {
    3021             :                 /* if PAGER is empty or all-white-space, don't use pager */
    3022           0 :                 if (strspn(pagerprog, " \t\r\n") == strlen(pagerprog))
    3023           0 :                     return stdout;
    3024             :             }
    3025           0 :             disable_sigpipe_trap();
    3026           0 :             pagerpipe = popen(pagerprog, "w");
    3027           0 :             if (pagerpipe)
    3028           0 :                 return pagerpipe;
    3029             :             /* if popen fails, silently proceed without pager */
    3030           0 :             restore_sigpipe_trap();
    3031             :         }
    3032             :     }
    3033             : 
    3034       59232 :     return stdout;
    3035             : }
    3036             : 
    3037             : /*
    3038             :  * ClosePager
    3039             :  *
    3040             :  * Close previously opened pager pipe, if any
    3041             :  */
    3042             : void
    3043         148 : ClosePager(FILE *pagerpipe)
    3044             : {
    3045         148 :     if (pagerpipe && pagerpipe != stdout)
    3046             :     {
    3047             :         /*
    3048             :          * If printing was canceled midstream, warn about it.
    3049             :          *
    3050             :          * Some pagers like less use Ctrl-C as part of their command set. Even
    3051             :          * so, we abort our processing and warn the user what we did.  If the
    3052             :          * pager quit as a result of the SIGINT, this message won't go
    3053             :          * anywhere ...
    3054             :          */
    3055           0 :         if (cancel_pressed)
    3056           0 :             fprintf(pagerpipe, _("Interrupted\n"));
    3057             : 
    3058           0 :         pclose(pagerpipe);
    3059           0 :         restore_sigpipe_trap();
    3060             :     }
    3061         148 : }
    3062             : 
    3063             : /*
    3064             :  * Initialise a table contents struct.
    3065             :  *      Must be called before any other printTable method is used.
    3066             :  *
    3067             :  * The title is not duplicated; the caller must ensure that the buffer
    3068             :  * is available for the lifetime of the printTableContent struct.
    3069             :  *
    3070             :  * If you call this, you must call printTableCleanup once you're done with the
    3071             :  * table.
    3072             :  */
    3073             : void
    3074       59286 : printTableInit(printTableContent *const content, const printTableOpt *opt,
    3075             :                const char *title, const int ncolumns, const int nrows)
    3076             : {
    3077       59286 :     content->opt = opt;
    3078       59286 :     content->title = title;
    3079       59286 :     content->ncolumns = ncolumns;
    3080       59286 :     content->nrows = nrows;
    3081             : 
    3082       59286 :     content->headers = pg_malloc0((ncolumns + 1) * sizeof(*content->headers));
    3083             : 
    3084       59286 :     content->cells = pg_malloc0((ncolumns * nrows + 1) * sizeof(*content->cells));
    3085             : 
    3086       59286 :     content->cellmustfree = NULL;
    3087       59286 :     content->footers = NULL;
    3088             : 
    3089       59286 :     content->aligns = pg_malloc0((ncolumns + 1) * sizeof(*content->align));
    3090             : 
    3091       59286 :     content->header = content->headers;
    3092       59286 :     content->cell = content->cells;
    3093       59286 :     content->footer = content->footers;
    3094       59286 :     content->align = content->aligns;
    3095       59286 :     content->cellsadded = 0;
    3096       59286 : }
    3097             : 
    3098             : /*
    3099             :  * Add a header to the table.
    3100             :  *
    3101             :  * Headers are not duplicated; you must ensure that the header string is
    3102             :  * available for the lifetime of the printTableContent struct.
    3103             :  *
    3104             :  * If translate is true, the function will pass the header through gettext.
    3105             :  * Otherwise, the header will not be translated.
    3106             :  *
    3107             :  * align is either 'l' or 'r', and specifies the alignment for cells in this
    3108             :  * column.
    3109             :  */
    3110             : void
    3111      113410 : printTableAddHeader(printTableContent *const content, char *header,
    3112             :                     const bool translate, const char align)
    3113             : {
    3114             : #ifndef ENABLE_NLS
    3115             :     (void) translate;           /* unused parameter */
    3116             : #endif
    3117             : 
    3118      113410 :     if (content->header >= content->headers + content->ncolumns)
    3119             :     {
    3120           0 :         fprintf(stderr, _("Cannot add header to table content: "
    3121             :                           "column count of %d exceeded.\n"),
    3122             :                 content->ncolumns);
    3123           0 :         exit(EXIT_FAILURE);
    3124             :     }
    3125             : 
    3126      226820 :     *content->header = (char *) mbvalidate((unsigned char *) header,
    3127      113410 :                                            content->opt->encoding);
    3128             : #ifdef ENABLE_NLS
    3129      113410 :     if (translate)
    3130       12342 :         *content->header = _(*content->header);
    3131             : #endif
    3132      113410 :     content->header++;
    3133             : 
    3134      113410 :     *content->align = align;
    3135      113410 :     content->align++;
    3136      113410 : }
    3137             : 
    3138             : /*
    3139             :  * Add a cell to the table.
    3140             :  *
    3141             :  * Cells are not duplicated; you must ensure that the cell string is available
    3142             :  * for the lifetime of the printTableContent struct.
    3143             :  *
    3144             :  * If translate is true, the function will pass the cell through gettext.
    3145             :  * Otherwise, the cell will not be translated.
    3146             :  *
    3147             :  * If mustfree is true, the cell string is freed by printTableCleanup().
    3148             :  * Note: Automatic freeing of translatable strings is not supported.
    3149             :  */
    3150             : void
    3151      590518 : printTableAddCell(printTableContent *const content, char *cell,
    3152             :                   const bool translate, const bool mustfree)
    3153             : {
    3154             : #ifndef ENABLE_NLS
    3155             :     (void) translate;           /* unused parameter */
    3156             : #endif
    3157             : 
    3158      590518 :     if (content->cellsadded >= content->ncolumns * content->nrows)
    3159             :     {
    3160           0 :         fprintf(stderr, _("Cannot add cell to table content: "
    3161             :                           "total cell count of %d exceeded.\n"),
    3162           0 :                 content->ncolumns * content->nrows);
    3163           0 :         exit(EXIT_FAILURE);
    3164             :     }
    3165             : 
    3166     1181036 :     *content->cell = (char *) mbvalidate((unsigned char *) cell,
    3167      590518 :                                          content->opt->encoding);
    3168             : 
    3169             : #ifdef ENABLE_NLS
    3170      590518 :     if (translate)
    3171         528 :         *content->cell = _(*content->cell);
    3172             : #endif
    3173             : 
    3174      590518 :     if (mustfree)
    3175             :     {
    3176         116 :         if (content->cellmustfree == NULL)
    3177          56 :             content->cellmustfree =
    3178          56 :                 pg_malloc0((content->ncolumns * content->nrows + 1) * sizeof(bool));
    3179             : 
    3180         116 :         content->cellmustfree[content->cellsadded] = true;
    3181             :     }
    3182      590518 :     content->cell++;
    3183      590518 :     content->cellsadded++;
    3184      590518 : }
    3185             : 
    3186             : /*
    3187             :  * Add a footer to the table.
    3188             :  *
    3189             :  * Footers are added as elements of a singly-linked list, and the content is
    3190             :  * strdup'd, so there is no need to keep the original footer string around.
    3191             :  *
    3192             :  * Footers are never translated by the function.  If you want the footer
    3193             :  * translated you must do so yourself, before calling printTableAddFooter.  The
    3194             :  * reason this works differently to headers and cells is that footers tend to
    3195             :  * be made of up individually translated components, rather than being
    3196             :  * translated as a whole.
    3197             :  */
    3198             : void
    3199        4278 : printTableAddFooter(printTableContent *const content, const char *footer)
    3200             : {
    3201             :     printTableFooter *f;
    3202             : 
    3203        4278 :     f = pg_malloc0(sizeof(*f));
    3204        4278 :     f->data = pg_strdup(footer);
    3205             : 
    3206        4278 :     if (content->footers == NULL)
    3207        1456 :         content->footers = f;
    3208             :     else
    3209        2822 :         content->footer->next = f;
    3210             : 
    3211        4278 :     content->footer = f;
    3212        4278 : }
    3213             : 
    3214             : /*
    3215             :  * Change the content of the last-added footer.
    3216             :  *
    3217             :  * The current contents of the last-added footer are freed, and replaced by the
    3218             :  * content given in *footer.  If there was no previous footer, add a new one.
    3219             :  *
    3220             :  * The content is strdup'd, so there is no need to keep the original string
    3221             :  * around.
    3222             :  */
    3223             : void
    3224          20 : printTableSetFooter(printTableContent *const content, const char *footer)
    3225             : {
    3226          20 :     if (content->footers != NULL)
    3227             :     {
    3228          20 :         free(content->footer->data);
    3229          20 :         content->footer->data = pg_strdup(footer);
    3230             :     }
    3231             :     else
    3232           0 :         printTableAddFooter(content, footer);
    3233          20 : }
    3234             : 
    3235             : /*
    3236             :  * Free all memory allocated to this struct.
    3237             :  *
    3238             :  * Once this has been called, the struct is unusable unless you pass it to
    3239             :  * printTableInit() again.
    3240             :  */
    3241             : void
    3242       59286 : printTableCleanup(printTableContent *const content)
    3243             : {
    3244       59286 :     if (content->cellmustfree)
    3245             :     {
    3246             :         int         i;
    3247             : 
    3248         740 :         for (i = 0; i < content->nrows * content->ncolumns; i++)
    3249             :         {
    3250         684 :             if (content->cellmustfree[i])
    3251         116 :                 free(unconstify(char *, content->cells[i]));
    3252             :         }
    3253          56 :         free(content->cellmustfree);
    3254          56 :         content->cellmustfree = NULL;
    3255             :     }
    3256       59286 :     free(content->headers);
    3257       59286 :     free(content->cells);
    3258       59286 :     free(content->aligns);
    3259             : 
    3260       59286 :     content->opt = NULL;
    3261       59286 :     content->title = NULL;
    3262       59286 :     content->headers = NULL;
    3263       59286 :     content->cells = NULL;
    3264       59286 :     content->aligns = NULL;
    3265       59286 :     content->header = NULL;
    3266       59286 :     content->cell = NULL;
    3267       59286 :     content->align = NULL;
    3268             : 
    3269       59286 :     if (content->footers)
    3270             :     {
    3271        7190 :         for (content->footer = content->footers; content->footer;)
    3272             :         {
    3273             :             printTableFooter *f;
    3274             : 
    3275        4278 :             f = content->footer;
    3276        4278 :             content->footer = f->next;
    3277        4278 :             free(f->data);
    3278        4278 :             free(f);
    3279             :         }
    3280             :     }
    3281       59286 :     content->footers = NULL;
    3282       59286 :     content->footer = NULL;
    3283       59286 : }
    3284             : 
    3285             : /*
    3286             :  * IsPagerNeeded
    3287             :  *
    3288             :  * Setup pager if required
    3289             :  */
    3290             : static void
    3291       59084 : IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
    3292             :               FILE **fout, bool *is_pager)
    3293             : {
    3294       59084 :     if (*fout == stdout)
    3295             :     {
    3296             :         int         lines;
    3297             : 
    3298       59084 :         if (expanded)
    3299         526 :             lines = (cont->ncolumns + 1) * cont->nrows;
    3300             :         else
    3301       58558 :             lines = cont->nrows + 1;
    3302             : 
    3303       59084 :         if (!cont->opt->tuples_only)
    3304             :         {
    3305             :             printTableFooter *f;
    3306             : 
    3307             :             /*
    3308             :              * FIXME -- this is slightly bogus: it counts the number of
    3309             :              * footers, not the number of lines in them.
    3310             :              */
    3311       62136 :             for (f = cont->footers; f; f = f->next)
    3312        4262 :                 lines++;
    3313             :         }
    3314             : 
    3315       59084 :         *fout = PageOutput(lines + extra_lines, cont->opt);
    3316       59084 :         *is_pager = (*fout != stdout);
    3317             :     }
    3318             :     else
    3319           0 :         *is_pager = false;
    3320       59084 : }
    3321             : 
    3322             : /*
    3323             :  * Use this to print any table in the supported formats.
    3324             :  *
    3325             :  * cont: table data and formatting options
    3326             :  * fout: where to print to
    3327             :  * is_pager: true if caller has already redirected fout to be a pager pipe
    3328             :  * flog: if not null, also print the table there (for --log-file option)
    3329             :  */
    3330             : void
    3331       59282 : printTable(const printTableContent *cont,
    3332             :            FILE *fout, bool is_pager, FILE *flog)
    3333             : {
    3334       59282 :     bool        is_local_pager = false;
    3335             : 
    3336       59282 :     if (cancel_pressed)
    3337           0 :         return;
    3338             : 
    3339       59282 :     if (cont->opt->format == PRINT_NOTHING)
    3340           0 :         return;
    3341             : 
    3342             :     /* print_aligned_*() handle the pager themselves */
    3343      118484 :     if (!is_pager &&
    3344       60872 :         cont->opt->format != PRINT_ALIGNED &&
    3345        1670 :         cont->opt->format != PRINT_WRAPPED)
    3346             :     {
    3347        1530 :         IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager);
    3348        1530 :         is_local_pager = is_pager;
    3349             :     }
    3350             : 
    3351             :     /* print the stuff */
    3352             : 
    3353       59282 :     if (flog)
    3354           0 :         print_aligned_text(cont, flog, false);
    3355             : 
    3356       59282 :     switch (cont->opt->format)
    3357             :     {
    3358             :         case PRINT_UNALIGNED:
    3359        1266 :             if (cont->opt->expanded == 1)
    3360          68 :                 print_unaligned_vertical(cont, fout);
    3361             :             else
    3362        1198 :                 print_unaligned_text(cont, fout);
    3363        1266 :             break;
    3364             :         case PRINT_ALIGNED:
    3365             :         case PRINT_WRAPPED:
    3366             : 
    3367             :             /*
    3368             :              * In expanded-auto mode, force vertical if a pager is passed in;
    3369             :              * else we may make different decisions for different hunks of the
    3370             :              * query result.
    3371             :              */
    3372      115144 :             if (cont->opt->expanded == 1 ||
    3373       57392 :                 (cont->opt->expanded == 2 && is_pager))
    3374         360 :                 print_aligned_vertical(cont, fout, is_pager);
    3375             :             else
    3376       57392 :                 print_aligned_text(cont, fout, is_pager);
    3377       57752 :             break;
    3378             :         case PRINT_CSV:
    3379          40 :             if (cont->opt->expanded == 1)
    3380          12 :                 print_csv_vertical(cont, fout);
    3381             :             else
    3382          28 :                 print_csv_text(cont, fout);
    3383          40 :             break;
    3384             :         case PRINT_HTML:
    3385          40 :             if (cont->opt->expanded == 1)
    3386          20 :                 print_html_vertical(cont, fout);
    3387             :             else
    3388          20 :                 print_html_text(cont, fout);
    3389          40 :             break;
    3390             :         case PRINT_ASCIIDOC:
    3391          40 :             if (cont->opt->expanded == 1)
    3392          20 :                 print_asciidoc_vertical(cont, fout);
    3393             :             else
    3394          20 :                 print_asciidoc_text(cont, fout);
    3395          40 :             break;
    3396             :         case PRINT_LATEX:
    3397          48 :             if (cont->opt->expanded == 1)
    3398          24 :                 print_latex_vertical(cont, fout);
    3399             :             else
    3400          24 :                 print_latex_text(cont, fout);
    3401          48 :             break;
    3402             :         case PRINT_LATEX_LONGTABLE:
    3403          56 :             if (cont->opt->expanded == 1)
    3404          28 :                 print_latex_vertical(cont, fout);
    3405             :             else
    3406          28 :                 print_latex_longtable_text(cont, fout);
    3407          56 :             break;
    3408             :         case PRINT_TROFF_MS:
    3409          40 :             if (cont->opt->expanded == 1)
    3410          20 :                 print_troff_ms_vertical(cont, fout);
    3411             :             else
    3412          20 :                 print_troff_ms_text(cont, fout);
    3413          40 :             break;
    3414             :         default:
    3415           0 :             fprintf(stderr, _("invalid output format (internal error): %d"),
    3416           0 :                     cont->opt->format);
    3417           0 :             exit(EXIT_FAILURE);
    3418             :     }
    3419             : 
    3420       59282 :     if (is_local_pager)
    3421           0 :         ClosePager(fout);
    3422             : }
    3423             : 
    3424             : /*
    3425             :  * Use this to print query results
    3426             :  *
    3427             :  * result: result of a successful query
    3428             :  * opt: formatting options
    3429             :  * fout: where to print to
    3430             :  * is_pager: true if caller has already redirected fout to be a pager pipe
    3431             :  * flog: if not null, also print the data there (for --log-file option)
    3432             :  */
    3433             : void
    3434       57642 : printQuery(const PGresult *result, const printQueryOpt *opt,
    3435             :            FILE *fout, bool is_pager, FILE *flog)
    3436             : {
    3437             :     printTableContent cont;
    3438             :     int         i,
    3439             :                 r,
    3440             :                 c;
    3441             : 
    3442       57642 :     if (cancel_pressed)
    3443           0 :         return;
    3444             : 
    3445       57642 :     printTableInit(&cont, &opt->topt, opt->title,
    3446             :                    PQnfields(result), PQntuples(result));
    3447             : 
    3448             :     /* Assert caller supplied enough translate_columns[] entries */
    3449             :     Assert(opt->translate_columns == NULL ||
    3450             :            opt->n_translate_columns >= cont.ncolumns);
    3451             : 
    3452      161100 :     for (i = 0; i < cont.ncolumns; i++)
    3453             :     {
    3454      206916 :         printTableAddHeader(&cont, PQfname(result, i),
    3455      103458 :                             opt->translate_header,
    3456      103458 :                             column_type_alignment(PQftype(result, i)));
    3457             :     }
    3458             : 
    3459             :     /* set cells */
    3460      295536 :     for (r = 0; r < cont.nrows; r++)
    3461             :     {
    3462      805064 :         for (c = 0; c < cont.ncolumns; c++)
    3463             :         {
    3464             :             char       *cell;
    3465      567170 :             bool        mustfree = false;
    3466             :             bool        translate;
    3467             : 
    3468      567170 :             if (PQgetisnull(result, r, c))
    3469       27172 :                 cell = opt->nullPrint ? opt->nullPrint : "";
    3470             :             else
    3471             :             {
    3472      539998 :                 cell = PQgetvalue(result, r, c);
    3473      539998 :                 if (cont.aligns[c] == 'r' && opt->topt.numericLocale)
    3474             :                 {
    3475          64 :                     cell = format_numeric_locale(cell);
    3476          64 :                     mustfree = true;
    3477             :                 }
    3478             :             }
    3479             : 
    3480      567170 :             translate = (opt->translate_columns && opt->translate_columns[c]);
    3481      567170 :             printTableAddCell(&cont, cell, translate, mustfree);
    3482             :         }
    3483             :     }
    3484             : 
    3485             :     /* set footers */
    3486       57642 :     if (opt->footers)
    3487             :     {
    3488             :         char      **footer;
    3489             : 
    3490         164 :         for (footer = opt->footers; *footer; footer++)
    3491          80 :             printTableAddFooter(&cont, *footer);
    3492             :     }
    3493             : 
    3494       57642 :     printTable(&cont, fout, is_pager, flog);
    3495       57642 :     printTableCleanup(&cont);
    3496             : }
    3497             : 
    3498             : char
    3499      103570 : column_type_alignment(Oid ftype)
    3500             : {
    3501             :     char        align;
    3502             : 
    3503      103570 :     switch (ftype)
    3504             :     {
    3505             :         case INT2OID:
    3506             :         case INT4OID:
    3507             :         case INT8OID:
    3508             :         case FLOAT4OID:
    3509             :         case FLOAT8OID:
    3510             :         case NUMERICOID:
    3511             :         case OIDOID:
    3512             :         case XIDOID:
    3513             :         case CIDOID:
    3514             :         case CASHOID:
    3515       40948 :             align = 'r';
    3516       40948 :             break;
    3517             :         default:
    3518       62622 :             align = 'l';
    3519       62622 :             break;
    3520             :     }
    3521      103570 :     return align;
    3522             : }
    3523             : 
    3524             : void
    3525        4168 : setDecimalLocale(void)
    3526             : {
    3527             :     struct lconv *extlconv;
    3528             : 
    3529        4168 :     extlconv = localeconv();
    3530             : 
    3531             :     /* Don't accept an empty decimal_point string */
    3532        4168 :     if (*extlconv->decimal_point)
    3533        4168 :         decimal_point = pg_strdup(extlconv->decimal_point);
    3534             :     else
    3535           0 :         decimal_point = ".";  /* SQL output standard */
    3536             : 
    3537             :     /*
    3538             :      * Although the Open Group standard allows locales to supply more than one
    3539             :      * group width, we consider only the first one, and we ignore any attempt
    3540             :      * to suppress grouping by specifying CHAR_MAX.  As in the backend's
    3541             :      * cash.c, we must apply a range check to avoid being fooled by variant
    3542             :      * CHAR_MAX values.
    3543             :      */
    3544        4168 :     groupdigits = *extlconv->grouping;
    3545        4168 :     if (groupdigits <= 0 || groupdigits > 6)
    3546          18 :         groupdigits = 3;        /* most common */
    3547             : 
    3548             :     /* Don't accept an empty thousands_sep string, either */
    3549             :     /* similar code exists in formatting.c */
    3550        4168 :     if (*extlconv->thousands_sep)
    3551        4150 :         thousands_sep = pg_strdup(extlconv->thousands_sep);
    3552             :     /* Make sure thousands separator doesn't match decimal point symbol. */
    3553          18 :     else if (strcmp(decimal_point, ",") != 0)
    3554          18 :         thousands_sep = ",";
    3555             :     else
    3556           0 :         thousands_sep = ".";
    3557        4168 : }
    3558             : 
    3559             : /* get selected or default line style */
    3560             : const printTextFormat *
    3561       57756 : get_line_style(const printTableOpt *opt)
    3562             : {
    3563             :     /*
    3564             :      * Note: this function mainly exists to preserve the convention that a
    3565             :      * printTableOpt struct can be initialized to zeroes to get default
    3566             :      * behavior.
    3567             :      */
    3568       57756 :     if (opt->line_style != NULL)
    3569         376 :         return opt->line_style;
    3570             :     else
    3571       57380 :         return &pg_asciiformat;
    3572             : }
    3573             : 
    3574             : void
    3575        4168 : refresh_utf8format(const printTableOpt *opt)
    3576             : {
    3577        4168 :     printTextFormat *popt = &pg_utf8format;
    3578             : 
    3579             :     const unicodeStyleBorderFormat *border;
    3580             :     const unicodeStyleRowFormat *header;
    3581             :     const unicodeStyleColumnFormat *column;
    3582             : 
    3583        4168 :     popt->name = "unicode";
    3584             : 
    3585        4168 :     border = &unicode_style.border_style[opt->unicode_border_linestyle];
    3586        4168 :     header = &unicode_style.row_style[opt->unicode_header_linestyle];
    3587        4168 :     column = &unicode_style.column_style[opt->unicode_column_linestyle];
    3588             : 
    3589        4168 :     popt->lrule[PRINT_RULE_TOP].hrule = border->horizontal;
    3590        4168 :     popt->lrule[PRINT_RULE_TOP].leftvrule = border->down_and_right;
    3591        4168 :     popt->lrule[PRINT_RULE_TOP].midvrule = column->down_and_horizontal[opt->unicode_border_linestyle];
    3592        4168 :     popt->lrule[PRINT_RULE_TOP].rightvrule = border->down_and_left;
    3593             : 
    3594        4168 :     popt->lrule[PRINT_RULE_MIDDLE].hrule = header->horizontal;
    3595        4168 :     popt->lrule[PRINT_RULE_MIDDLE].leftvrule = header->vertical_and_right[opt->unicode_border_linestyle];
    3596        4168 :     popt->lrule[PRINT_RULE_MIDDLE].midvrule = column->vertical_and_horizontal[opt->unicode_header_linestyle];
    3597        4168 :     popt->lrule[PRINT_RULE_MIDDLE].rightvrule = header->vertical_and_left[opt->unicode_border_linestyle];
    3598             : 
    3599        4168 :     popt->lrule[PRINT_RULE_BOTTOM].hrule = border->horizontal;
    3600        4168 :     popt->lrule[PRINT_RULE_BOTTOM].leftvrule = border->up_and_right;
    3601        4168 :     popt->lrule[PRINT_RULE_BOTTOM].midvrule = column->up_and_horizontal[opt->unicode_border_linestyle];
    3602        4168 :     popt->lrule[PRINT_RULE_BOTTOM].rightvrule = border->left_and_right;
    3603             : 
    3604             :     /* N/A */
    3605        4168 :     popt->lrule[PRINT_RULE_DATA].hrule = "";
    3606        4168 :     popt->lrule[PRINT_RULE_DATA].leftvrule = border->vertical;
    3607        4168 :     popt->lrule[PRINT_RULE_DATA].midvrule = column->vertical;
    3608        4168 :     popt->lrule[PRINT_RULE_DATA].rightvrule = border->vertical;
    3609             : 
    3610        4168 :     popt->midvrule_nl = column->vertical;
    3611        4168 :     popt->midvrule_wrap = column->vertical;
    3612        4168 :     popt->midvrule_blank = column->vertical;
    3613             : 
    3614             :     /* Same for all unicode today */
    3615        4168 :     popt->header_nl_left = unicode_style.header_nl_left;
    3616        4168 :     popt->header_nl_right = unicode_style.header_nl_right;
    3617        4168 :     popt->nl_left = unicode_style.nl_left;
    3618        4168 :     popt->nl_right = unicode_style.nl_right;
    3619        4168 :     popt->wrap_left = unicode_style.wrap_left;
    3620        4168 :     popt->wrap_right = unicode_style.wrap_right;
    3621        4168 :     popt->wrap_right_border = unicode_style.wrap_right_border;
    3622             : 
    3623        4168 :     return;
    3624             : }
    3625             : 
    3626             : /*
    3627             :  * Compute the byte distance to the end of the string or *target_width
    3628             :  * display character positions, whichever comes first.  Update *target_width
    3629             :  * to be the number of display character positions actually filled.
    3630             :  */
    3631             : static int
    3632      591110 : strlen_max_width(unsigned char *str, int *target_width, int encoding)
    3633             : {
    3634      591110 :     unsigned char *start = str;
    3635      591110 :     unsigned char *end = str + strlen((char *) str);
    3636      591110 :     int         curr_width = 0;
    3637             : 
    3638     7556144 :     while (str < end)
    3639             :     {
    3640     6374944 :         int         char_width = PQdsplen((char *) str, encoding);
    3641             : 
    3642             :         /*
    3643             :          * If the display width of the new character causes the string to
    3644             :          * exceed its target width, skip it and return.  However, if this is
    3645             :          * the first character of the string (curr_width == 0), we have to
    3646             :          * accept it.
    3647             :          */
    3648     6374944 :         if (*target_width < curr_width + char_width && curr_width != 0)
    3649        1020 :             break;
    3650             : 
    3651     6373924 :         curr_width += char_width;
    3652             : 
    3653     6373924 :         str += PQmblen((char *) str, encoding);
    3654             :     }
    3655             : 
    3656      591110 :     *target_width = curr_width;
    3657             : 
    3658      591110 :     return str - start;
    3659             : }

Generated by: LCOV version 1.13