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