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