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