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