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