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

Generated by: LCOV version 1.11