LCOV - code coverage report
Current view: top level - src/fe_utils - print.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 89.1 % 1981 1765
Test Date: 2026-01-26 10:56:24 Functions: 95.9 % 49 47
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 77.5 % 1358 1053

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

Generated by: LCOV version 2.3.2-1