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