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