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-2017, 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.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 */
46 volatile bool cancel_pressed = false;
47
48 static bool always_ignore_sigpipe = false;
49
50 /* info for locale-aware numeric formatting; set up by setDecimalLocale() */
51 static char *decimal_point;
52 static int groupdigits;
53 static char *thousands_sep;
54
55 static char default_footer[100];
56 static printTableFooter default_footer_cell = {default_footer, NULL};
57
58 /* Line style control structures */
59 const 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
80 const 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 */
102 printTextFormat pg_utf8format;
103
104 typedef struct unicodeStyleRowFormat
105 {
106 const char *horizontal;
107 const char *vertical_and_right[2];
108 const char *vertical_and_left[2];
109 } unicodeStyleRowFormat;
110
111 typedef 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
119 typedef 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
129 typedef 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
143 static 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 */
201 static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
202 static void IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
203 FILE **fout, bool *is_pager);
204
205 static 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 */
210 static int
integer_digits(const char * my_str)211 integer_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 */
221 static int
additional_numeric_locale_len(const char * my_str)222 additional_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 */
246 static char *
format_numeric_locale(const char * my_str)247 format_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 */
317 static void
fputnbytes(FILE * f,const char * str,size_t n)318 fputnbytes(FILE *f, const char *str, size_t n)
319 {
320 while (n-- > 0)
321 fputc(*str++, f);
322 }
323
324
325 static void
print_separator(struct separator sep,FILE * fout)326 print_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 */
344 static printTableFooter *
footers_with_default(const printTableContent * cont)345 footers_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
368 static void
print_unaligned_text(const printTableContent * cont,FILE * fout)369 print_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
459 static void
print_unaligned_vertical(const printTableContent * cont,FILE * fout)460 print_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" */
539 static void
_print_horizontal_line(const unsigned int ncolumns,const unsigned int * widths,unsigned short border,printTextRule pos,const printTextFormat * format,FILE * fout)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 */
581 static void
print_aligned_text(const printTableContent * cont,FILE * fout,bool is_pager)582 print_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
1148 cleanup:
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
1173 static void
print_aligned_vertical_line(const printTextFormat * format,const unsigned short opt_border,unsigned long record,unsigned int hwidth,unsigned int dwidth,printTextRule pos,FILE * fout)1174 print_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
1229 static void
print_aligned_vertical(const printTableContent * cont,FILE * fout,bool is_pager)1230 print_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 /* HTML printing ******/
1741 /**********************/
1742
1743
1744 void
html_escaped_print(const char * in,FILE * fout)1745 html_escaped_print(const char *in, FILE *fout)
1746 {
1747 const char *p;
1748 bool leading_space = true;
1749
1750 for (p = in; *p; p++)
1751 {
1752 switch (*p)
1753 {
1754 case '&':
1755 fputs("&", fout);
1756 break;
1757 case '<':
1758 fputs("<", fout);
1759 break;
1760 case '>':
1761 fputs(">", fout);
1762 break;
1763 case '\n':
1764 fputs("<br />\n", fout);
1765 break;
1766 case '"':
1767 fputs(""", fout);
1768 break;
1769 case ' ':
1770 /* protect leading space, for EXPLAIN output */
1771 if (leading_space)
1772 fputs(" ", fout);
1773 else
1774 fputs(" ", fout);
1775 break;
1776 default:
1777 fputc(*p, fout);
1778 }
1779 if (*p != ' ')
1780 leading_space = false;
1781 }
1782 }
1783
1784
1785 static void
print_html_text(const printTableContent * cont,FILE * fout)1786 print_html_text(const printTableContent *cont, FILE *fout)
1787 {
1788 bool opt_tuples_only = cont->opt->tuples_only;
1789 unsigned short opt_border = cont->opt->border;
1790 const char *opt_table_attr = cont->opt->tableAttr;
1791 unsigned int i;
1792 const char *const *ptr;
1793
1794 if (cancel_pressed)
1795 return;
1796
1797 if (cont->opt->start_table)
1798 {
1799 fprintf(fout, "<table border=\"%d\"", opt_border);
1800 if (opt_table_attr)
1801 fprintf(fout, " %s", opt_table_attr);
1802 fputs(">\n", fout);
1803
1804 /* print title */
1805 if (!opt_tuples_only && cont->title)
1806 {
1807 fputs(" <caption>", fout);
1808 html_escaped_print(cont->title, fout);
1809 fputs("</caption>\n", fout);
1810 }
1811
1812 /* print headers */
1813 if (!opt_tuples_only)
1814 {
1815 fputs(" <tr>\n", fout);
1816 for (ptr = cont->headers; *ptr; ptr++)
1817 {
1818 fputs(" <th align=\"center\">", fout);
1819 html_escaped_print(*ptr, fout);
1820 fputs("</th>\n", fout);
1821 }
1822 fputs(" </tr>\n", fout);
1823 }
1824 }
1825
1826 /* print cells */
1827 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1828 {
1829 if (i % cont->ncolumns == 0)
1830 {
1831 if (cancel_pressed)
1832 break;
1833 fputs(" <tr valign=\"top\">\n", fout);
1834 }
1835
1836 fprintf(fout, " <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
1837 /* is string only whitespace? */
1838 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
1839 fputs(" ", fout);
1840 else
1841 html_escaped_print(*ptr, fout);
1842
1843 fputs("</td>\n", fout);
1844
1845 if ((i + 1) % cont->ncolumns == 0)
1846 fputs(" </tr>\n", fout);
1847 }
1848
1849 if (cont->opt->stop_table)
1850 {
1851 printTableFooter *footers = footers_with_default(cont);
1852
1853 fputs("</table>\n", fout);
1854
1855 /* print footers */
1856 if (!opt_tuples_only && footers != NULL && !cancel_pressed)
1857 {
1858 printTableFooter *f;
1859
1860 fputs("<p>", fout);
1861 for (f = footers; f; f = f->next)
1862 {
1863 html_escaped_print(f->data, fout);
1864 fputs("<br />\n", fout);
1865 }
1866 fputs("</p>", fout);
1867 }
1868
1869 fputc('\n', fout);
1870 }
1871 }
1872
1873
1874 static void
print_html_vertical(const printTableContent * cont,FILE * fout)1875 print_html_vertical(const printTableContent *cont, FILE *fout)
1876 {
1877 bool opt_tuples_only = cont->opt->tuples_only;
1878 unsigned short opt_border = cont->opt->border;
1879 const char *opt_table_attr = cont->opt->tableAttr;
1880 unsigned long record = cont->opt->prior_records + 1;
1881 unsigned int i;
1882 const char *const *ptr;
1883
1884 if (cancel_pressed)
1885 return;
1886
1887 if (cont->opt->start_table)
1888 {
1889 fprintf(fout, "<table border=\"%d\"", opt_border);
1890 if (opt_table_attr)
1891 fprintf(fout, " %s", opt_table_attr);
1892 fputs(">\n", fout);
1893
1894 /* print title */
1895 if (!opt_tuples_only && cont->title)
1896 {
1897 fputs(" <caption>", fout);
1898 html_escaped_print(cont->title, fout);
1899 fputs("</caption>\n", fout);
1900 }
1901 }
1902
1903 /* print records */
1904 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1905 {
1906 if (i % cont->ncolumns == 0)
1907 {
1908 if (cancel_pressed)
1909 break;
1910 if (!opt_tuples_only)
1911 fprintf(fout,
1912 "\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
1913 record++);
1914 else
1915 fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout);
1916 }
1917 fputs(" <tr valign=\"top\">\n"
1918 " <th>", fout);
1919 html_escaped_print(cont->headers[i % cont->ncolumns], fout);
1920 fputs("</th>\n", fout);
1921
1922 fprintf(fout, " <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
1923 /* is string only whitespace? */
1924 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
1925 fputs(" ", fout);
1926 else
1927 html_escaped_print(*ptr, fout);
1928
1929 fputs("</td>\n </tr>\n", fout);
1930 }
1931
1932 if (cont->opt->stop_table)
1933 {
1934 fputs("</table>\n", fout);
1935
1936 /* print footers */
1937 if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
1938 {
1939 printTableFooter *f;
1940
1941 fputs("<p>", fout);
1942 for (f = cont->footers; f; f = f->next)
1943 {
1944 html_escaped_print(f->data, fout);
1945 fputs("<br />\n", fout);
1946 }
1947 fputs("</p>", fout);
1948 }
1949
1950 fputc('\n', fout);
1951 }
1952 }
1953
1954
1955 /*************************/
1956 /* ASCIIDOC */
1957 /*************************/
1958
1959 static void
asciidoc_escaped_print(const char * in,FILE * fout)1960 asciidoc_escaped_print(const char *in, FILE *fout)
1961 {
1962 const char *p;
1963
1964 for (p = in; *p; p++)
1965 {
1966 switch (*p)
1967 {
1968 case '|':
1969 fputs("\\|", fout);
1970 break;
1971 default:
1972 fputc(*p, fout);
1973 }
1974 }
1975 }
1976
1977 static void
print_asciidoc_text(const printTableContent * cont,FILE * fout)1978 print_asciidoc_text(const printTableContent *cont, FILE *fout)
1979 {
1980 bool opt_tuples_only = cont->opt->tuples_only;
1981 unsigned short opt_border = cont->opt->border;
1982 unsigned int i;
1983 const char *const *ptr;
1984
1985 if (cancel_pressed)
1986 return;
1987
1988 if (cont->opt->start_table)
1989 {
1990 /* print table in new paragraph - enforce preliminary new line */
1991 fputs("\n", fout);
1992
1993 /* print title */
1994 if (!opt_tuples_only && cont->title)
1995 {
1996 fputs(".", fout);
1997 fputs(cont->title, fout);
1998 fputs("\n", fout);
1999 }
2000
2001 /* print table [] header definition */
2002 fprintf(fout, "[%scols=\"", !opt_tuples_only ? "options=\"header\"," : "");
2003 for (i = 0; i < cont->ncolumns; i++)
2004 {
2005 if (i != 0)
2006 fputs(",", fout);
2007 fprintf(fout, "%s", cont->aligns[(i) % cont->ncolumns] == 'r' ? ">l" : "<l");
2008 }
2009 fputs("\"", fout);
2010 switch (opt_border)
2011 {
2012 case 0:
2013 fputs(",frame=\"none\",grid=\"none\"", fout);
2014 break;
2015 case 1:
2016 fputs(",frame=\"none\"", fout);
2017 break;
2018 case 2:
2019 fputs(",frame=\"all\",grid=\"all\"", fout);
2020 break;
2021 }
2022 fputs("]\n", fout);
2023 fputs("|====\n", fout);
2024
2025 /* print headers */
2026 if (!opt_tuples_only)
2027 {
2028 for (ptr = cont->headers; *ptr; ptr++)
2029 {
2030 if (ptr != cont->headers)
2031 fputs(" ", fout);
2032 fputs("^l|", fout);
2033 asciidoc_escaped_print(*ptr, fout);
2034 }
2035 fputs("\n", fout);
2036 }
2037 }
2038
2039 /* print cells */
2040 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2041 {
2042 if (i % cont->ncolumns == 0)
2043 {
2044 if (cancel_pressed)
2045 break;
2046 }
2047
2048 if (i % cont->ncolumns != 0)
2049 fputs(" ", fout);
2050 fputs("|", fout);
2051
2052 /* protect against needless spaces */
2053 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
2054 {
2055 if ((i + 1) % cont->ncolumns != 0)
2056 fputs(" ", fout);
2057 }
2058 else
2059 asciidoc_escaped_print(*ptr, fout);
2060
2061 if ((i + 1) % cont->ncolumns == 0)
2062 fputs("\n", fout);
2063 }
2064
2065 fputs("|====\n", fout);
2066
2067 if (cont->opt->stop_table)
2068 {
2069 printTableFooter *footers = footers_with_default(cont);
2070
2071 /* print footers */
2072 if (!opt_tuples_only && footers != NULL && !cancel_pressed)
2073 {
2074 printTableFooter *f;
2075
2076 fputs("\n....\n", fout);
2077 for (f = footers; f; f = f->next)
2078 {
2079 fputs(f->data, fout);
2080 fputs("\n", fout);
2081 }
2082 fputs("....\n", fout);
2083 }
2084 }
2085 }
2086
2087 static void
print_asciidoc_vertical(const printTableContent * cont,FILE * fout)2088 print_asciidoc_vertical(const printTableContent *cont, FILE *fout)
2089 {
2090 bool opt_tuples_only = cont->opt->tuples_only;
2091 unsigned short opt_border = cont->opt->border;
2092 unsigned long record = cont->opt->prior_records + 1;
2093 unsigned int i;
2094 const char *const *ptr;
2095
2096 if (cancel_pressed)
2097 return;
2098
2099 if (cont->opt->start_table)
2100 {
2101 /* print table in new paragraph - enforce preliminary new line */
2102 fputs("\n", fout);
2103
2104 /* print title */
2105 if (!opt_tuples_only && cont->title)
2106 {
2107 fputs(".", fout);
2108 fputs(cont->title, fout);
2109 fputs("\n", fout);
2110 }
2111
2112 /* print table [] header definition */
2113 fputs("[cols=\"h,l\"", fout);
2114 switch (opt_border)
2115 {
2116 case 0:
2117 fputs(",frame=\"none\",grid=\"none\"", fout);
2118 break;
2119 case 1:
2120 fputs(",frame=\"none\"", fout);
2121 break;
2122 case 2:
2123 fputs(",frame=\"all\",grid=\"all\"", fout);
2124 break;
2125 }
2126 fputs("]\n", fout);
2127 fputs("|====\n", fout);
2128 }
2129
2130 /* print records */
2131 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2132 {
2133 if (i % cont->ncolumns == 0)
2134 {
2135 if (cancel_pressed)
2136 break;
2137 if (!opt_tuples_only)
2138 fprintf(fout,
2139 "2+^|Record %lu\n",
2140 record++);
2141 else
2142 fputs("2+|\n", fout);
2143 }
2144
2145 fputs("<l|", fout);
2146 asciidoc_escaped_print(cont->headers[i % cont->ncolumns], fout);
2147
2148 fprintf(fout, " %s|", cont->aligns[i % cont->ncolumns] == 'r' ? ">l" : "<l");
2149 /* is string only whitespace? */
2150 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
2151 fputs(" ", fout);
2152 else
2153 asciidoc_escaped_print(*ptr, fout);
2154 fputs("\n", fout);
2155 }
2156
2157 fputs("|====\n", fout);
2158
2159 if (cont->opt->stop_table)
2160 {
2161 /* print footers */
2162 if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
2163 {
2164 printTableFooter *f;
2165
2166 fputs("\n....\n", fout);
2167 for (f = cont->footers; f; f = f->next)
2168 {
2169 fputs(f->data, fout);
2170 fputs("\n", fout);
2171 }
2172 fputs("....\n", fout);
2173 }
2174 }
2175 }
2176
2177 /*************************/
2178 /* LaTeX */
2179 /*************************/
2180
2181
2182 static void
latex_escaped_print(const char * in,FILE * fout)2183 latex_escaped_print(const char *in, FILE *fout)
2184 {
2185 const char *p;
2186
2187 for (p = in; *p; p++)
2188 switch (*p)
2189 {
2190 /*
2191 * We convert ASCII characters per the recommendations in
2192 * Scott Pakin's "The Comprehensive LATEX Symbol List",
2193 * available from CTAN. For non-ASCII, you're on your own.
2194 */
2195 case '#':
2196 fputs("\\#", fout);
2197 break;
2198 case '$':
2199 fputs("\\$", fout);
2200 break;
2201 case '%':
2202 fputs("\\%", fout);
2203 break;
2204 case '&':
2205 fputs("\\&", fout);
2206 break;
2207 case '<':
2208 fputs("\\textless{}", fout);
2209 break;
2210 case '>':
2211 fputs("\\textgreater{}", fout);
2212 break;
2213 case '\\':
2214 fputs("\\textbackslash{}", fout);
2215 break;
2216 case '^':
2217 fputs("\\^{}", fout);
2218 break;
2219 case '_':
2220 fputs("\\_", fout);
2221 break;
2222 case '{':
2223 fputs("\\{", fout);
2224 break;
2225 case '|':
2226 fputs("\\textbar{}", fout);
2227 break;
2228 case '}':
2229 fputs("\\}", fout);
2230 break;
2231 case '~':
2232 fputs("\\~{}", fout);
2233 break;
2234 case '\n':
2235 /* This is not right, but doing it right seems too hard */
2236 fputs("\\\\", fout);
2237 break;
2238 default:
2239 fputc(*p, fout);
2240 }
2241 }
2242
2243
2244 static void
print_latex_text(const printTableContent * cont,FILE * fout)2245 print_latex_text(const printTableContent *cont, FILE *fout)
2246 {
2247 bool opt_tuples_only = cont->opt->tuples_only;
2248 unsigned short opt_border = cont->opt->border;
2249 unsigned int i;
2250 const char *const *ptr;
2251
2252 if (cancel_pressed)
2253 return;
2254
2255 if (opt_border > 3)
2256 opt_border = 3;
2257
2258 if (cont->opt->start_table)
2259 {
2260 /* print title */
2261 if (!opt_tuples_only && cont->title)
2262 {
2263 fputs("\\begin{center}\n", fout);
2264 latex_escaped_print(cont->title, fout);
2265 fputs("\n\\end{center}\n\n", fout);
2266 }
2267
2268 /* begin environment and set alignments and borders */
2269 fputs("\\begin{tabular}{", fout);
2270
2271 if (opt_border >= 2)
2272 fputs("| ", fout);
2273 for (i = 0; i < cont->ncolumns; i++)
2274 {
2275 fputc(*(cont->aligns + i), fout);
2276 if (opt_border != 0 && i < cont->ncolumns - 1)
2277 fputs(" | ", fout);
2278 }
2279 if (opt_border >= 2)
2280 fputs(" |", fout);
2281
2282 fputs("}\n", fout);
2283
2284 if (!opt_tuples_only && opt_border >= 2)
2285 fputs("\\hline\n", fout);
2286
2287 /* print headers */
2288 if (!opt_tuples_only)
2289 {
2290 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
2291 {
2292 if (i != 0)
2293 fputs(" & ", fout);
2294 fputs("\\textit{", fout);
2295 latex_escaped_print(*ptr, fout);
2296 fputc('}', fout);
2297 }
2298 fputs(" \\\\\n", fout);
2299 fputs("\\hline\n", fout);
2300 }
2301 }
2302
2303 /* print cells */
2304 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2305 {
2306 latex_escaped_print(*ptr, fout);
2307
2308 if ((i + 1) % cont->ncolumns == 0)
2309 {
2310 fputs(" \\\\\n", fout);
2311 if (opt_border == 3)
2312 fputs("\\hline\n", fout);
2313 if (cancel_pressed)
2314 break;
2315 }
2316 else
2317 fputs(" & ", fout);
2318 }
2319
2320 if (cont->opt->stop_table)
2321 {
2322 printTableFooter *footers = footers_with_default(cont);
2323
2324 if (opt_border == 2)
2325 fputs("\\hline\n", fout);
2326
2327 fputs("\\end{tabular}\n\n\\noindent ", fout);
2328
2329 /* print footers */
2330 if (footers && !opt_tuples_only && !cancel_pressed)
2331 {
2332 printTableFooter *f;
2333
2334 for (f = footers; f; f = f->next)
2335 {
2336 latex_escaped_print(f->data, fout);
2337 fputs(" \\\\\n", fout);
2338 }
2339 }
2340
2341 fputc('\n', fout);
2342 }
2343 }
2344
2345
2346 static void
print_latex_longtable_text(const printTableContent * cont,FILE * fout)2347 print_latex_longtable_text(const printTableContent *cont, FILE *fout)
2348 {
2349 bool opt_tuples_only = cont->opt->tuples_only;
2350 unsigned short opt_border = cont->opt->border;
2351 unsigned int i;
2352 const char *opt_table_attr = cont->opt->tableAttr;
2353 const char *next_opt_table_attr_char = opt_table_attr;
2354 const char *last_opt_table_attr_char = NULL;
2355 const char *const *ptr;
2356
2357 if (cancel_pressed)
2358 return;
2359
2360 if (opt_border > 3)
2361 opt_border = 3;
2362
2363 if (cont->opt->start_table)
2364 {
2365 /* begin environment and set alignments and borders */
2366 fputs("\\begin{longtable}{", fout);
2367
2368 if (opt_border >= 2)
2369 fputs("| ", fout);
2370
2371 for (i = 0; i < cont->ncolumns; i++)
2372 {
2373 /* longtable supports either a width (p) or an alignment (l/r) */
2374 /* Are we left-justified and was a proportional width specified? */
2375 if (*(cont->aligns + i) == 'l' && opt_table_attr)
2376 {
2377 #define LONGTABLE_WHITESPACE " \t\n"
2378
2379 /* advance over whitespace */
2380 next_opt_table_attr_char += strspn(next_opt_table_attr_char,
2381 LONGTABLE_WHITESPACE);
2382 /* We have a value? */
2383 if (next_opt_table_attr_char[0] != '\0')
2384 {
2385 fputs("p{", fout);
2386 fwrite(next_opt_table_attr_char, strcspn(next_opt_table_attr_char,
2387 LONGTABLE_WHITESPACE), 1, fout);
2388 last_opt_table_attr_char = next_opt_table_attr_char;
2389 next_opt_table_attr_char += strcspn(next_opt_table_attr_char,
2390 LONGTABLE_WHITESPACE);
2391 fputs("\\textwidth}", fout);
2392 }
2393 /* use previous value */
2394 else if (last_opt_table_attr_char != NULL)
2395 {
2396 fputs("p{", fout);
2397 fwrite(last_opt_table_attr_char, strcspn(last_opt_table_attr_char,
2398 LONGTABLE_WHITESPACE), 1, fout);
2399 fputs("\\textwidth}", fout);
2400 }
2401 else
2402 fputc('l', fout);
2403 }
2404 else
2405 fputc(*(cont->aligns + i), fout);
2406
2407 if (opt_border != 0 && i < cont->ncolumns - 1)
2408 fputs(" | ", fout);
2409 }
2410
2411 if (opt_border >= 2)
2412 fputs(" |", fout);
2413
2414 fputs("}\n", fout);
2415
2416 /* print headers */
2417 if (!opt_tuples_only)
2418 {
2419 /* firsthead */
2420 if (opt_border >= 2)
2421 fputs("\\toprule\n", fout);
2422 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
2423 {
2424 if (i != 0)
2425 fputs(" & ", fout);
2426 fputs("\\small\\textbf{\\textit{", fout);
2427 latex_escaped_print(*ptr, fout);
2428 fputs("}}", fout);
2429 }
2430 fputs(" \\\\\n", fout);
2431 fputs("\\midrule\n\\endfirsthead\n", fout);
2432
2433 /* secondary heads */
2434 if (opt_border >= 2)
2435 fputs("\\toprule\n", fout);
2436 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
2437 {
2438 if (i != 0)
2439 fputs(" & ", fout);
2440 fputs("\\small\\textbf{\\textit{", fout);
2441 latex_escaped_print(*ptr, fout);
2442 fputs("}}", fout);
2443 }
2444 fputs(" \\\\\n", fout);
2445 /* If the line under the row already appeared, don't do another */
2446 if (opt_border != 3)
2447 fputs("\\midrule\n", fout);
2448 fputs("\\endhead\n", fout);
2449
2450 /* table name, caption? */
2451 if (!opt_tuples_only && cont->title)
2452 {
2453 /* Don't output if we are printing a line under each row */
2454 if (opt_border == 2)
2455 fputs("\\bottomrule\n", fout);
2456 fputs("\\caption[", fout);
2457 latex_escaped_print(cont->title, fout);
2458 fputs(" (Continued)]{", fout);
2459 latex_escaped_print(cont->title, fout);
2460 fputs("}\n\\endfoot\n", fout);
2461 if (opt_border == 2)
2462 fputs("\\bottomrule\n", fout);
2463 fputs("\\caption[", fout);
2464 latex_escaped_print(cont->title, fout);
2465 fputs("]{", fout);
2466 latex_escaped_print(cont->title, fout);
2467 fputs("}\n\\endlastfoot\n", fout);
2468 }
2469 /* output bottom table line? */
2470 else if (opt_border >= 2)
2471 {
2472 fputs("\\bottomrule\n\\endfoot\n", fout);
2473 fputs("\\bottomrule\n\\endlastfoot\n", fout);
2474 }
2475 }
2476 }
2477
2478 /* print cells */
2479 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2480 {
2481 /* Add a line under each row? */
2482 if (i != 0 && i % cont->ncolumns != 0)
2483 fputs("\n&\n", fout);
2484 fputs("\\raggedright{", fout);
2485 latex_escaped_print(*ptr, fout);
2486 fputc('}', fout);
2487 if ((i + 1) % cont->ncolumns == 0)
2488 {
2489 fputs(" \\tabularnewline\n", fout);
2490 if (opt_border == 3)
2491 fputs(" \\hline\n", fout);
2492 }
2493 if (cancel_pressed)
2494 break;
2495 }
2496
2497 if (cont->opt->stop_table)
2498 fputs("\\end{longtable}\n", fout);
2499 }
2500
2501
2502 static void
print_latex_vertical(const printTableContent * cont,FILE * fout)2503 print_latex_vertical(const printTableContent *cont, FILE *fout)
2504 {
2505 bool opt_tuples_only = cont->opt->tuples_only;
2506 unsigned short opt_border = cont->opt->border;
2507 unsigned long record = cont->opt->prior_records + 1;
2508 unsigned int i;
2509 const char *const *ptr;
2510
2511 if (cancel_pressed)
2512 return;
2513
2514 if (opt_border > 2)
2515 opt_border = 2;
2516
2517 if (cont->opt->start_table)
2518 {
2519 /* print title */
2520 if (!opt_tuples_only && cont->title)
2521 {
2522 fputs("\\begin{center}\n", fout);
2523 latex_escaped_print(cont->title, fout);
2524 fputs("\n\\end{center}\n\n", fout);
2525 }
2526
2527 /* begin environment and set alignments and borders */
2528 fputs("\\begin{tabular}{", fout);
2529 if (opt_border == 0)
2530 fputs("cl", fout);
2531 else if (opt_border == 1)
2532 fputs("c|l", fout);
2533 else if (opt_border == 2)
2534 fputs("|c|l|", fout);
2535 fputs("}\n", fout);
2536 }
2537
2538 /* print records */
2539 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2540 {
2541 /* new record */
2542 if (i % cont->ncolumns == 0)
2543 {
2544 if (cancel_pressed)
2545 break;
2546 if (!opt_tuples_only)
2547 {
2548 if (opt_border == 2)
2549 {
2550 fputs("\\hline\n", fout);
2551 fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
2552 }
2553 else
2554 fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
2555 }
2556 if (opt_border >= 1)
2557 fputs("\\hline\n", fout);
2558 }
2559
2560 latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
2561 fputs(" & ", fout);
2562 latex_escaped_print(*ptr, fout);
2563 fputs(" \\\\\n", fout);
2564 }
2565
2566 if (cont->opt->stop_table)
2567 {
2568 if (opt_border == 2)
2569 fputs("\\hline\n", fout);
2570
2571 fputs("\\end{tabular}\n\n\\noindent ", fout);
2572
2573 /* print footers */
2574 if (cont->footers && !opt_tuples_only && !cancel_pressed)
2575 {
2576 printTableFooter *f;
2577
2578 for (f = cont->footers; f; f = f->next)
2579 {
2580 latex_escaped_print(f->data, fout);
2581 fputs(" \\\\\n", fout);
2582 }
2583 }
2584
2585 fputc('\n', fout);
2586 }
2587 }
2588
2589
2590 /*************************/
2591 /* Troff -ms */
2592 /*************************/
2593
2594
2595 static void
troff_ms_escaped_print(const char * in,FILE * fout)2596 troff_ms_escaped_print(const char *in, FILE *fout)
2597 {
2598 const char *p;
2599
2600 for (p = in; *p; p++)
2601 switch (*p)
2602 {
2603 case '\\':
2604 fputs("\\(rs", fout);
2605 break;
2606 default:
2607 fputc(*p, fout);
2608 }
2609 }
2610
2611
2612 static void
print_troff_ms_text(const printTableContent * cont,FILE * fout)2613 print_troff_ms_text(const printTableContent *cont, FILE *fout)
2614 {
2615 bool opt_tuples_only = cont->opt->tuples_only;
2616 unsigned short opt_border = cont->opt->border;
2617 unsigned int i;
2618 const char *const *ptr;
2619
2620 if (cancel_pressed)
2621 return;
2622
2623 if (opt_border > 2)
2624 opt_border = 2;
2625
2626 if (cont->opt->start_table)
2627 {
2628 /* print title */
2629 if (!opt_tuples_only && cont->title)
2630 {
2631 fputs(".LP\n.DS C\n", fout);
2632 troff_ms_escaped_print(cont->title, fout);
2633 fputs("\n.DE\n", fout);
2634 }
2635
2636 /* begin environment and set alignments and borders */
2637 fputs(".LP\n.TS\n", fout);
2638 if (opt_border == 2)
2639 fputs("center box;\n", fout);
2640 else
2641 fputs("center;\n", fout);
2642
2643 for (i = 0; i < cont->ncolumns; i++)
2644 {
2645 fputc(*(cont->aligns + i), fout);
2646 if (opt_border > 0 && i < cont->ncolumns - 1)
2647 fputs(" | ", fout);
2648 }
2649 fputs(".\n", fout);
2650
2651 /* print headers */
2652 if (!opt_tuples_only)
2653 {
2654 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
2655 {
2656 if (i != 0)
2657 fputc('\t', fout);
2658 fputs("\\fI", fout);
2659 troff_ms_escaped_print(*ptr, fout);
2660 fputs("\\fP", fout);
2661 }
2662 fputs("\n_\n", fout);
2663 }
2664 }
2665
2666 /* print cells */
2667 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2668 {
2669 troff_ms_escaped_print(*ptr, fout);
2670
2671 if ((i + 1) % cont->ncolumns == 0)
2672 {
2673 fputc('\n', fout);
2674 if (cancel_pressed)
2675 break;
2676 }
2677 else
2678 fputc('\t', fout);
2679 }
2680
2681 if (cont->opt->stop_table)
2682 {
2683 printTableFooter *footers = footers_with_default(cont);
2684
2685 fputs(".TE\n.DS L\n", fout);
2686
2687 /* print footers */
2688 if (footers && !opt_tuples_only && !cancel_pressed)
2689 {
2690 printTableFooter *f;
2691
2692 for (f = footers; f; f = f->next)
2693 {
2694 troff_ms_escaped_print(f->data, fout);
2695 fputc('\n', fout);
2696 }
2697 }
2698
2699 fputs(".DE\n", fout);
2700 }
2701 }
2702
2703
2704 static void
print_troff_ms_vertical(const printTableContent * cont,FILE * fout)2705 print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
2706 {
2707 bool opt_tuples_only = cont->opt->tuples_only;
2708 unsigned short opt_border = cont->opt->border;
2709 unsigned long record = cont->opt->prior_records + 1;
2710 unsigned int i;
2711 const char *const *ptr;
2712 unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
2713
2714 if (cancel_pressed)
2715 return;
2716
2717 if (opt_border > 2)
2718 opt_border = 2;
2719
2720 if (cont->opt->start_table)
2721 {
2722 /* print title */
2723 if (!opt_tuples_only && cont->title)
2724 {
2725 fputs(".LP\n.DS C\n", fout);
2726 troff_ms_escaped_print(cont->title, fout);
2727 fputs("\n.DE\n", fout);
2728 }
2729
2730 /* begin environment and set alignments and borders */
2731 fputs(".LP\n.TS\n", fout);
2732 if (opt_border == 2)
2733 fputs("center box;\n", fout);
2734 else
2735 fputs("center;\n", fout);
2736
2737 /* basic format */
2738 if (opt_tuples_only)
2739 fputs("c l;\n", fout);
2740 }
2741 else
2742 current_format = 2; /* assume tuples printed already */
2743
2744 /* print records */
2745 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2746 {
2747 /* new record */
2748 if (i % cont->ncolumns == 0)
2749 {
2750 if (cancel_pressed)
2751 break;
2752 if (!opt_tuples_only)
2753 {
2754 if (current_format != 1)
2755 {
2756 if (opt_border == 2 && record > 1)
2757 fputs("_\n", fout);
2758 if (current_format != 0)
2759 fputs(".T&\n", fout);
2760 fputs("c s.\n", fout);
2761 current_format = 1;
2762 }
2763 fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
2764 }
2765 if (opt_border >= 1)
2766 fputs("_\n", fout);
2767 }
2768
2769 if (!opt_tuples_only)
2770 {
2771 if (current_format != 2)
2772 {
2773 if (current_format != 0)
2774 fputs(".T&\n", fout);
2775 if (opt_border != 1)
2776 fputs("c l.\n", fout);
2777 else
2778 fputs("c | l.\n", fout);
2779 current_format = 2;
2780 }
2781 }
2782
2783 troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
2784 fputc('\t', fout);
2785 troff_ms_escaped_print(*ptr, fout);
2786
2787 fputc('\n', fout);
2788 }
2789
2790 if (cont->opt->stop_table)
2791 {
2792 fputs(".TE\n.DS L\n", fout);
2793
2794 /* print footers */
2795 if (cont->footers && !opt_tuples_only && !cancel_pressed)
2796 {
2797 printTableFooter *f;
2798
2799 for (f = cont->footers; f; f = f->next)
2800 {
2801 troff_ms_escaped_print(f->data, fout);
2802 fputc('\n', fout);
2803 }
2804 }
2805
2806 fputs(".DE\n", fout);
2807 }
2808 }
2809
2810
2811 /********************************/
2812 /* Public functions */
2813 /********************************/
2814
2815
2816 /*
2817 * disable_sigpipe_trap
2818 *
2819 * Turn off SIGPIPE interrupt --- call this before writing to a temporary
2820 * query output file that is a pipe.
2821 *
2822 * No-op on Windows, where there's no SIGPIPE interrupts.
2823 */
2824 void
disable_sigpipe_trap(void)2825 disable_sigpipe_trap(void)
2826 {
2827 #ifndef WIN32
2828 pqsignal(SIGPIPE, SIG_IGN);
2829 #endif
2830 }
2831
2832 /*
2833 * restore_sigpipe_trap
2834 *
2835 * Restore normal SIGPIPE interrupt --- call this when done writing to a
2836 * temporary query output file that was (or might have been) a pipe.
2837 *
2838 * Note: within psql, we enable SIGPIPE interrupts unless the permanent query
2839 * output file is a pipe, in which case they should be kept off. This
2840 * approach works only because psql is not currently complicated enough to
2841 * have nested usages of short-lived output files. Otherwise we'd probably
2842 * need a genuine save-and-restore-state approach; but for now, that would be
2843 * useless complication. In non-psql programs, this always enables SIGPIPE.
2844 *
2845 * No-op on Windows, where there's no SIGPIPE interrupts.
2846 */
2847 void
restore_sigpipe_trap(void)2848 restore_sigpipe_trap(void)
2849 {
2850 #ifndef WIN32
2851 pqsignal(SIGPIPE, always_ignore_sigpipe ? SIG_IGN : SIG_DFL);
2852 #endif
2853 }
2854
2855 /*
2856 * set_sigpipe_trap_state
2857 *
2858 * Set the trap state that restore_sigpipe_trap should restore to.
2859 */
2860 void
set_sigpipe_trap_state(bool ignore)2861 set_sigpipe_trap_state(bool ignore)
2862 {
2863 always_ignore_sigpipe = ignore;
2864 }
2865
2866
2867 /*
2868 * PageOutput
2869 *
2870 * Tests if pager is needed and returns appropriate FILE pointer.
2871 *
2872 * If the topt argument is NULL no pager is used.
2873 */
2874 FILE *
PageOutput(int lines,const printTableOpt * topt)2875 PageOutput(int lines, const printTableOpt *topt)
2876 {
2877 /* check whether we need / can / are supposed to use pager */
2878 if (topt && topt->pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
2879 {
2880 #ifdef TIOCGWINSZ
2881 unsigned short int pager = topt->pager;
2882 int min_lines = topt->pager_min_lines;
2883 int result;
2884 struct winsize screen_size;
2885
2886 result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
2887
2888 /* >= accounts for a one-line prompt */
2889 if (result == -1
2890 || (lines >= screen_size.ws_row && lines >= min_lines)
2891 || pager > 1)
2892 #endif
2893 {
2894 const char *pagerprog;
2895 FILE *pagerpipe;
2896
2897 pagerprog = getenv("PAGER");
2898 if (!pagerprog)
2899 pagerprog = DEFAULT_PAGER;
2900 else
2901 {
2902 /* if PAGER is empty or all-white-space, don't use pager */
2903 if (strspn(pagerprog, " \t\r\n") == strlen(pagerprog))
2904 return stdout;
2905 }
2906 disable_sigpipe_trap();
2907 pagerpipe = popen(pagerprog, "w");
2908 if (pagerpipe)
2909 return pagerpipe;
2910 /* if popen fails, silently proceed without pager */
2911 restore_sigpipe_trap();
2912 }
2913 }
2914
2915 return stdout;
2916 }
2917
2918 /*
2919 * ClosePager
2920 *
2921 * Close previously opened pager pipe, if any
2922 */
2923 void
ClosePager(FILE * pagerpipe)2924 ClosePager(FILE *pagerpipe)
2925 {
2926 if (pagerpipe && pagerpipe != stdout)
2927 {
2928 /*
2929 * If printing was canceled midstream, warn about it.
2930 *
2931 * Some pagers like less use Ctrl-C as part of their command set. Even
2932 * so, we abort our processing and warn the user what we did. If the
2933 * pager quit as a result of the SIGINT, this message won't go
2934 * anywhere ...
2935 */
2936 if (cancel_pressed)
2937 fprintf(pagerpipe, _("Interrupted\n"));
2938
2939 pclose(pagerpipe);
2940 restore_sigpipe_trap();
2941 }
2942 }
2943
2944 /*
2945 * Initialise a table contents struct.
2946 * Must be called before any other printTable method is used.
2947 *
2948 * The title is not duplicated; the caller must ensure that the buffer
2949 * is available for the lifetime of the printTableContent struct.
2950 *
2951 * If you call this, you must call printTableCleanup once you're done with the
2952 * table.
2953 */
2954 void
printTableInit(printTableContent * const content,const printTableOpt * opt,const char * title,const int ncolumns,const int nrows)2955 printTableInit(printTableContent *const content, const printTableOpt *opt,
2956 const char *title, const int ncolumns, const int nrows)
2957 {
2958 content->opt = opt;
2959 content->title = title;
2960 content->ncolumns = ncolumns;
2961 content->nrows = nrows;
2962
2963 content->headers = pg_malloc0((ncolumns + 1) * sizeof(*content->headers));
2964
2965 content->cells = pg_malloc0((ncolumns * nrows + 1) * sizeof(*content->cells));
2966
2967 content->cellmustfree = NULL;
2968 content->footers = NULL;
2969
2970 content->aligns = pg_malloc0((ncolumns + 1) * sizeof(*content->align));
2971
2972 content->header = content->headers;
2973 content->cell = content->cells;
2974 content->footer = content->footers;
2975 content->align = content->aligns;
2976 content->cellsadded = 0;
2977 }
2978
2979 /*
2980 * Add a header to the table.
2981 *
2982 * Headers are not duplicated; you must ensure that the header string is
2983 * available for the lifetime of the printTableContent struct.
2984 *
2985 * If translate is true, the function will pass the header through gettext.
2986 * Otherwise, the header will not be translated.
2987 *
2988 * align is either 'l' or 'r', and specifies the alignment for cells in this
2989 * column.
2990 */
2991 void
printTableAddHeader(printTableContent * const content,char * header,const bool translate,const char align)2992 printTableAddHeader(printTableContent *const content, char *header,
2993 const bool translate, const char align)
2994 {
2995 #ifndef ENABLE_NLS
2996 (void) translate; /* unused parameter */
2997 #endif
2998
2999 if (content->header >= content->headers + content->ncolumns)
3000 {
3001 fprintf(stderr, _("Cannot add header to table content: "
3002 "column count of %d exceeded.\n"),
3003 content->ncolumns);
3004 exit(EXIT_FAILURE);
3005 }
3006
3007 *content->header = (char *) mbvalidate((unsigned char *) header,
3008 content->opt->encoding);
3009 #ifdef ENABLE_NLS
3010 if (translate)
3011 *content->header = _(*content->header);
3012 #endif
3013 content->header++;
3014
3015 *content->align = align;
3016 content->align++;
3017 }
3018
3019 /*
3020 * Add a cell to the table.
3021 *
3022 * Cells are not duplicated; you must ensure that the cell string is available
3023 * for the lifetime of the printTableContent struct.
3024 *
3025 * If translate is true, the function will pass the cell through gettext.
3026 * Otherwise, the cell will not be translated.
3027 *
3028 * If mustfree is true, the cell string is freed by printTableCleanup().
3029 * Note: Automatic freeing of translatable strings is not supported.
3030 */
3031 void
printTableAddCell(printTableContent * const content,char * cell,const bool translate,const bool mustfree)3032 printTableAddCell(printTableContent *const content, char *cell,
3033 const bool translate, const bool mustfree)
3034 {
3035 #ifndef ENABLE_NLS
3036 (void) translate; /* unused parameter */
3037 #endif
3038
3039 if (content->cellsadded >= content->ncolumns * content->nrows)
3040 {
3041 fprintf(stderr, _("Cannot add cell to table content: "
3042 "total cell count of %d exceeded.\n"),
3043 content->ncolumns * content->nrows);
3044 exit(EXIT_FAILURE);
3045 }
3046
3047 *content->cell = (char *) mbvalidate((unsigned char *) cell,
3048 content->opt->encoding);
3049
3050 #ifdef ENABLE_NLS
3051 if (translate)
3052 *content->cell = _(*content->cell);
3053 #endif
3054
3055 if (mustfree)
3056 {
3057 if (content->cellmustfree == NULL)
3058 content->cellmustfree =
3059 pg_malloc0((content->ncolumns * content->nrows + 1) * sizeof(bool));
3060
3061 content->cellmustfree[content->cellsadded] = true;
3062 }
3063 content->cell++;
3064 content->cellsadded++;
3065 }
3066
3067 /*
3068 * Add a footer to the table.
3069 *
3070 * Footers are added as elements of a singly-linked list, and the content is
3071 * strdup'd, so there is no need to keep the original footer string around.
3072 *
3073 * Footers are never translated by the function. If you want the footer
3074 * translated you must do so yourself, before calling printTableAddFooter. The
3075 * reason this works differently to headers and cells is that footers tend to
3076 * be made of up individually translated components, rather than being
3077 * translated as a whole.
3078 */
3079 void
printTableAddFooter(printTableContent * const content,const char * footer)3080 printTableAddFooter(printTableContent *const content, const char *footer)
3081 {
3082 printTableFooter *f;
3083
3084 f = pg_malloc0(sizeof(*f));
3085 f->data = pg_strdup(footer);
3086
3087 if (content->footers == NULL)
3088 content->footers = f;
3089 else
3090 content->footer->next = f;
3091
3092 content->footer = f;
3093 }
3094
3095 /*
3096 * Change the content of the last-added footer.
3097 *
3098 * The current contents of the last-added footer are freed, and replaced by the
3099 * content given in *footer. If there was no previous footer, add a new one.
3100 *
3101 * The content is strdup'd, so there is no need to keep the original string
3102 * around.
3103 */
3104 void
printTableSetFooter(printTableContent * const content,const char * footer)3105 printTableSetFooter(printTableContent *const content, const char *footer)
3106 {
3107 if (content->footers != NULL)
3108 {
3109 free(content->footer->data);
3110 content->footer->data = pg_strdup(footer);
3111 }
3112 else
3113 printTableAddFooter(content, footer);
3114 }
3115
3116 /*
3117 * Free all memory allocated to this struct.
3118 *
3119 * Once this has been called, the struct is unusable unless you pass it to
3120 * printTableInit() again.
3121 */
3122 void
printTableCleanup(printTableContent * const content)3123 printTableCleanup(printTableContent *const content)
3124 {
3125 if (content->cellmustfree)
3126 {
3127 int i;
3128
3129 for (i = 0; i < content->nrows * content->ncolumns; i++)
3130 {
3131 if (content->cellmustfree[i])
3132 free((char *) content->cells[i]);
3133 }
3134 free(content->cellmustfree);
3135 content->cellmustfree = NULL;
3136 }
3137 free(content->headers);
3138 free(content->cells);
3139 free(content->aligns);
3140
3141 content->opt = NULL;
3142 content->title = NULL;
3143 content->headers = NULL;
3144 content->cells = NULL;
3145 content->aligns = NULL;
3146 content->header = NULL;
3147 content->cell = NULL;
3148 content->align = NULL;
3149
3150 if (content->footers)
3151 {
3152 for (content->footer = content->footers; content->footer;)
3153 {
3154 printTableFooter *f;
3155
3156 f = content->footer;
3157 content->footer = f->next;
3158 free(f->data);
3159 free(f);
3160 }
3161 }
3162 content->footers = NULL;
3163 content->footer = NULL;
3164 }
3165
3166 /*
3167 * IsPagerNeeded
3168 *
3169 * Setup pager if required
3170 */
3171 static void
IsPagerNeeded(const printTableContent * cont,int extra_lines,bool expanded,FILE ** fout,bool * is_pager)3172 IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
3173 FILE **fout, bool *is_pager)
3174 {
3175 if (*fout == stdout)
3176 {
3177 int lines;
3178
3179 if (expanded)
3180 lines = (cont->ncolumns + 1) * cont->nrows;
3181 else
3182 lines = cont->nrows + 1;
3183
3184 if (!cont->opt->tuples_only)
3185 {
3186 printTableFooter *f;
3187
3188 /*
3189 * FIXME -- this is slightly bogus: it counts the number of
3190 * footers, not the number of lines in them.
3191 */
3192 for (f = cont->footers; f; f = f->next)
3193 lines++;
3194 }
3195
3196 *fout = PageOutput(lines + extra_lines, cont->opt);
3197 *is_pager = (*fout != stdout);
3198 }
3199 else
3200 *is_pager = false;
3201 }
3202
3203 /*
3204 * Use this to print any table in the supported formats.
3205 *
3206 * cont: table data and formatting options
3207 * fout: where to print to
3208 * is_pager: true if caller has already redirected fout to be a pager pipe
3209 * flog: if not null, also print the table there (for --log-file option)
3210 */
3211 void
printTable(const printTableContent * cont,FILE * fout,bool is_pager,FILE * flog)3212 printTable(const printTableContent *cont,
3213 FILE *fout, bool is_pager, FILE *flog)
3214 {
3215 bool is_local_pager = false;
3216
3217 if (cancel_pressed)
3218 return;
3219
3220 if (cont->opt->format == PRINT_NOTHING)
3221 return;
3222
3223 /* print_aligned_*() handle the pager themselves */
3224 if (!is_pager &&
3225 cont->opt->format != PRINT_ALIGNED &&
3226 cont->opt->format != PRINT_WRAPPED)
3227 {
3228 IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager);
3229 is_local_pager = is_pager;
3230 }
3231
3232 /* print the stuff */
3233
3234 if (flog)
3235 print_aligned_text(cont, flog, false);
3236
3237 switch (cont->opt->format)
3238 {
3239 case PRINT_UNALIGNED:
3240 if (cont->opt->expanded == 1)
3241 print_unaligned_vertical(cont, fout);
3242 else
3243 print_unaligned_text(cont, fout);
3244 break;
3245 case PRINT_ALIGNED:
3246 case PRINT_WRAPPED:
3247
3248 /*
3249 * In expanded-auto mode, force vertical if a pager is passed in;
3250 * else we may make different decisions for different hunks of the
3251 * query result.
3252 */
3253 if (cont->opt->expanded == 1 ||
3254 (cont->opt->expanded == 2 && is_pager))
3255 print_aligned_vertical(cont, fout, is_pager);
3256 else
3257 print_aligned_text(cont, fout, is_pager);
3258 break;
3259 case PRINT_HTML:
3260 if (cont->opt->expanded == 1)
3261 print_html_vertical(cont, fout);
3262 else
3263 print_html_text(cont, fout);
3264 break;
3265 case PRINT_ASCIIDOC:
3266 if (cont->opt->expanded == 1)
3267 print_asciidoc_vertical(cont, fout);
3268 else
3269 print_asciidoc_text(cont, fout);
3270 break;
3271 case PRINT_LATEX:
3272 if (cont->opt->expanded == 1)
3273 print_latex_vertical(cont, fout);
3274 else
3275 print_latex_text(cont, fout);
3276 break;
3277 case PRINT_LATEX_LONGTABLE:
3278 if (cont->opt->expanded == 1)
3279 print_latex_vertical(cont, fout);
3280 else
3281 print_latex_longtable_text(cont, fout);
3282 break;
3283 case PRINT_TROFF_MS:
3284 if (cont->opt->expanded == 1)
3285 print_troff_ms_vertical(cont, fout);
3286 else
3287 print_troff_ms_text(cont, fout);
3288 break;
3289 default:
3290 fprintf(stderr, _("invalid output format (internal error): %d"),
3291 cont->opt->format);
3292 exit(EXIT_FAILURE);
3293 }
3294
3295 if (is_local_pager)
3296 ClosePager(fout);
3297 }
3298
3299 /*
3300 * Use this to print query results
3301 *
3302 * result: result of a successful query
3303 * opt: formatting options
3304 * fout: where to print to
3305 * is_pager: true if caller has already redirected fout to be a pager pipe
3306 * flog: if not null, also print the data there (for --log-file option)
3307 */
3308 void
printQuery(const PGresult * result,const printQueryOpt * opt,FILE * fout,bool is_pager,FILE * flog)3309 printQuery(const PGresult *result, const printQueryOpt *opt,
3310 FILE *fout, bool is_pager, FILE *flog)
3311 {
3312 printTableContent cont;
3313 int i,
3314 r,
3315 c;
3316
3317 if (cancel_pressed)
3318 return;
3319
3320 printTableInit(&cont, &opt->topt, opt->title,
3321 PQnfields(result), PQntuples(result));
3322
3323 /* Assert caller supplied enough translate_columns[] entries */
3324 Assert(opt->translate_columns == NULL ||
3325 opt->n_translate_columns >= cont.ncolumns);
3326
3327 for (i = 0; i < cont.ncolumns; i++)
3328 {
3329 printTableAddHeader(&cont, PQfname(result, i),
3330 opt->translate_header,
3331 column_type_alignment(PQftype(result, i)));
3332 }
3333
3334 /* set cells */
3335 for (r = 0; r < cont.nrows; r++)
3336 {
3337 for (c = 0; c < cont.ncolumns; c++)
3338 {
3339 char *cell;
3340 bool mustfree = false;
3341 bool translate;
3342
3343 if (PQgetisnull(result, r, c))
3344 cell = opt->nullPrint ? opt->nullPrint : "";
3345 else
3346 {
3347 cell = PQgetvalue(result, r, c);
3348 if (cont.aligns[c] == 'r' && opt->topt.numericLocale)
3349 {
3350 cell = format_numeric_locale(cell);
3351 mustfree = true;
3352 }
3353 }
3354
3355 translate = (opt->translate_columns && opt->translate_columns[c]);
3356 printTableAddCell(&cont, cell, translate, mustfree);
3357 }
3358 }
3359
3360 /* set footers */
3361 if (opt->footers)
3362 {
3363 char **footer;
3364
3365 for (footer = opt->footers; *footer; footer++)
3366 printTableAddFooter(&cont, *footer);
3367 }
3368
3369 printTable(&cont, fout, is_pager, flog);
3370 printTableCleanup(&cont);
3371 }
3372
3373 char
column_type_alignment(Oid ftype)3374 column_type_alignment(Oid ftype)
3375 {
3376 char align;
3377
3378 switch (ftype)
3379 {
3380 case INT2OID:
3381 case INT4OID:
3382 case INT8OID:
3383 case FLOAT4OID:
3384 case FLOAT8OID:
3385 case NUMERICOID:
3386 case OIDOID:
3387 case XIDOID:
3388 case CIDOID:
3389 case CASHOID:
3390 align = 'r';
3391 break;
3392 default:
3393 align = 'l';
3394 break;
3395 }
3396 return align;
3397 }
3398
3399 void
setDecimalLocale(void)3400 setDecimalLocale(void)
3401 {
3402 struct lconv *extlconv;
3403
3404 extlconv = localeconv();
3405
3406 /* Don't accept an empty decimal_point string */
3407 if (*extlconv->decimal_point)
3408 decimal_point = pg_strdup(extlconv->decimal_point);
3409 else
3410 decimal_point = "."; /* SQL output standard */
3411
3412 /*
3413 * Although the Open Group standard allows locales to supply more than one
3414 * group width, we consider only the first one, and we ignore any attempt
3415 * to suppress grouping by specifying CHAR_MAX. As in the backend's
3416 * cash.c, we must apply a range check to avoid being fooled by variant
3417 * CHAR_MAX values.
3418 */
3419 groupdigits = *extlconv->grouping;
3420 if (groupdigits <= 0 || groupdigits > 6)
3421 groupdigits = 3; /* most common */
3422
3423 /* Don't accept an empty thousands_sep string, either */
3424 /* similar code exists in formatting.c */
3425 if (*extlconv->thousands_sep)
3426 thousands_sep = pg_strdup(extlconv->thousands_sep);
3427 /* Make sure thousands separator doesn't match decimal point symbol. */
3428 else if (strcmp(decimal_point, ",") != 0)
3429 thousands_sep = ",";
3430 else
3431 thousands_sep = ".";
3432 }
3433
3434 /* get selected or default line style */
3435 const printTextFormat *
get_line_style(const printTableOpt * opt)3436 get_line_style(const printTableOpt *opt)
3437 {
3438 /*
3439 * Note: this function mainly exists to preserve the convention that a
3440 * printTableOpt struct can be initialized to zeroes to get default
3441 * behavior.
3442 */
3443 if (opt->line_style != NULL)
3444 return opt->line_style;
3445 else
3446 return &pg_asciiformat;
3447 }
3448
3449 void
refresh_utf8format(const printTableOpt * opt)3450 refresh_utf8format(const printTableOpt *opt)
3451 {
3452 printTextFormat *popt = &pg_utf8format;
3453
3454 const unicodeStyleBorderFormat *border;
3455 const unicodeStyleRowFormat *header;
3456 const unicodeStyleColumnFormat *column;
3457
3458 popt->name = "unicode";
3459
3460 border = &unicode_style.border_style[opt->unicode_border_linestyle];
3461 header = &unicode_style.row_style[opt->unicode_header_linestyle];
3462 column = &unicode_style.column_style[opt->unicode_column_linestyle];
3463
3464 popt->lrule[PRINT_RULE_TOP].hrule = border->horizontal;
3465 popt->lrule[PRINT_RULE_TOP].leftvrule = border->down_and_right;
3466 popt->lrule[PRINT_RULE_TOP].midvrule = column->down_and_horizontal[opt->unicode_border_linestyle];
3467 popt->lrule[PRINT_RULE_TOP].rightvrule = border->down_and_left;
3468
3469 popt->lrule[PRINT_RULE_MIDDLE].hrule = header->horizontal;
3470 popt->lrule[PRINT_RULE_MIDDLE].leftvrule = header->vertical_and_right[opt->unicode_border_linestyle];
3471 popt->lrule[PRINT_RULE_MIDDLE].midvrule = column->vertical_and_horizontal[opt->unicode_header_linestyle];
3472 popt->lrule[PRINT_RULE_MIDDLE].rightvrule = header->vertical_and_left[opt->unicode_border_linestyle];
3473
3474 popt->lrule[PRINT_RULE_BOTTOM].hrule = border->horizontal;
3475 popt->lrule[PRINT_RULE_BOTTOM].leftvrule = border->up_and_right;
3476 popt->lrule[PRINT_RULE_BOTTOM].midvrule = column->up_and_horizontal[opt->unicode_border_linestyle];
3477 popt->lrule[PRINT_RULE_BOTTOM].rightvrule = border->left_and_right;
3478
3479 /* N/A */
3480 popt->lrule[PRINT_RULE_DATA].hrule = "";
3481 popt->lrule[PRINT_RULE_DATA].leftvrule = border->vertical;
3482 popt->lrule[PRINT_RULE_DATA].midvrule = column->vertical;
3483 popt->lrule[PRINT_RULE_DATA].rightvrule = border->vertical;
3484
3485 popt->midvrule_nl = column->vertical;
3486 popt->midvrule_wrap = column->vertical;
3487 popt->midvrule_blank = column->vertical;
3488
3489 /* Same for all unicode today */
3490 popt->header_nl_left = unicode_style.header_nl_left;
3491 popt->header_nl_right = unicode_style.header_nl_right;
3492 popt->nl_left = unicode_style.nl_left;
3493 popt->nl_right = unicode_style.nl_right;
3494 popt->wrap_left = unicode_style.wrap_left;
3495 popt->wrap_right = unicode_style.wrap_right;
3496 popt->wrap_right_border = unicode_style.wrap_right_border;
3497
3498 return;
3499 }
3500
3501 /*
3502 * Compute the byte distance to the end of the string or *target_width
3503 * display character positions, whichever comes first. Update *target_width
3504 * to be the number of display character positions actually filled.
3505 */
3506 static int
strlen_max_width(unsigned char * str,int * target_width,int encoding)3507 strlen_max_width(unsigned char *str, int *target_width, int encoding)
3508 {
3509 unsigned char *start = str;
3510 unsigned char *end = str + strlen((char *) str);
3511 int curr_width = 0;
3512
3513 while (str < end)
3514 {
3515 int char_width = PQdsplen((char *) str, encoding);
3516
3517 /*
3518 * If the display width of the new character causes the string to
3519 * exceed its target width, skip it and return. However, if this is
3520 * the first character of the string (curr_width == 0), we have to
3521 * accept it.
3522 */
3523 if (*target_width < curr_width + char_width && curr_width != 0)
3524 break;
3525
3526 curr_width += char_width;
3527
3528 str += PQmblen((char *) str, encoding);
3529
3530 if (str > end) /* Don't overrun invalid string */
3531 str = end;
3532 }
3533
3534 *target_width = curr_width;
3535
3536 return str - start;
3537 }
3538