Age Owner 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
6478 bruce 278 CBC 96 : integer_digits(const char *my_str)
279 : {
280 : /* ignoring any sign ... */
2754 tgl 281 96 : if (my_str[0] == '-' || my_str[0] == '+')
6482 bruce 282 18 : my_str++;
283 : /* ... count initial integral digits */
2754 tgl 284 96 : return strspn(my_str, "0123456789");
285 : }
286 :
287 : /* Compute additional length required for locale-aware numeric output */
288 : static int
6474 bruce 289 48 : additional_numeric_locale_len(const char *my_str)
290 : {
6385 291 48 : int int_len = integer_digits(my_str),
292 48 : len = 0;
293 :
294 : /* Account for added thousands_sep instances */
2754 tgl 295 48 : if (int_len > groupdigits)
2754 tgl 296 UBC 0 : len += ((int_len - 1) / groupdigits) * strlen(thousands_sep);
297 :
298 : /* Account for possible additional length of decimal_point */
6474 bruce 299 CBC 48 : if (strchr(my_str, '.') != NULL)
2754 tgl 300 UBC 0 : len += strlen(decimal_point) - 1;
301 :
6474 bruce 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 *
6403 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 : */
2753 tgl 327 48 : if (strspn(my_str, "0123456789+-.eE") != strlen(my_str))
2753 tgl 328 UBC 0 : return pg_strdup(my_str);
329 :
2753 tgl 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 */
2754 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];
6403 bruce 344 9 : my_str++;
345 : }
346 :
347 : /* process integer part of number */
2754 tgl 348 114 : for (i = 0; i < int_len; i++)
349 : {
350 : /* Time to insert separator? */
351 66 : if (i > 0 && --leading_digits == 0)
352 : {
2754 tgl 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 : }
2754 tgl 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 : {
2754 tgl 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) */
2754 tgl 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 :
6403 bruce 374 48 : return new_str;
375 : }
376 :
377 :
378 : static void
4077 peter_e 379 3998866 : print_separator(struct separator sep, FILE *fout)
380 : {
381 3998866 : if (sep.separator_zero)
4077 peter_e 382 UBC 0 : fputc('\000', fout);
4077 peter_e 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 *
3995 rhaas 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
5445 alvherre 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;
8397 bruce 427 3588 : bool need_recordsep = false;
428 :
6143 tgl 429 3588 : if (cancel_pressed)
6143 tgl 430 UBC 0 : return;
431 :
5445 alvherre 432 CBC 3588 : if (cont->opt->start_table)
433 : {
434 : /* print title */
435 3588 : if (!opt_tuples_only && cont->title)
436 : {
4077 peter_e 437 3 : fputs(cont->title, fout);
438 3 : print_separator(cont->opt->recordSep, fout);
439 : }
440 :
441 : /* print headers */
6478 bruce 442 3588 : if (!opt_tuples_only)
443 : {
5445 alvherre 444 184 : for (ptr = cont->headers; *ptr; ptr++)
445 : {
446 126 : if (ptr != cont->headers)
4077 peter_e 447 68 : print_separator(cont->opt->fieldSep, fout);
6067 tgl 448 126 : fputs(*ptr, fout);
449 : }
450 58 : need_recordsep = true;
451 : }
452 : }
453 : else
454 : /* assume continuing printout */
8397 bruce 455 UBC 0 : need_recordsep = true;
456 :
457 : /* print cells */
5445 alvherre 458 CBC 4003973 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
459 : {
8397 bruce 460 4000385 : if (need_recordsep)
461 : {
4077 peter_e 462 2022822 : print_separator(cont->opt->recordSep, fout);
8397 bruce 463 2022822 : need_recordsep = false;
6143 tgl 464 2022822 : if (cancel_pressed)
6143 tgl 465 UBC 0 : break;
466 : }
4787 heikki.linnakangas 467 CBC 4000385 : fputs(*ptr, fout);
468 :
5445 alvherre 469 4000385 : if ((i + 1) % cont->ncolumns)
4077 peter_e 470 1974364 : print_separator(cont->opt->fieldSep, fout);
471 : else
8397 bruce 472 2026021 : need_recordsep = true;
473 : }
474 :
475 : /* print footers */
5445 alvherre 476 3588 : if (cont->opt->stop_table)
477 : {
3995 rhaas 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 : {
6067 tgl 486 58 : if (need_recordsep)
487 : {
4077 peter_e 488 58 : print_separator(cont->opt->recordSep, fout);
6067 tgl 489 58 : need_recordsep = false;
490 : }
5445 alvherre 491 58 : fputs(f->data, fout);
6067 tgl 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 : {
4077 peter_e 503 3257 : if (cont->opt->recordSep.separator_zero)
4077 peter_e 504 UBC 0 : print_separator(cont->opt->recordSep, fout);
505 : else
4077 peter_e 506 CBC 3257 : fputc('\n', fout);
507 : }
508 : }
509 : }
510 :
511 :
512 : static void
5445 alvherre 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;
6067 tgl 518 51 : bool need_recordsep = false;
519 :
6143 520 51 : if (cancel_pressed)
6143 tgl 521 UBC 0 : return;
522 :
5445 alvherre 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);
6067 tgl 529 3 : need_recordsep = true;
530 : }
531 : }
532 : else
533 : /* assume continuing printout */
6067 tgl 534 UBC 0 : need_recordsep = true;
535 :
536 : /* print records */
5445 alvherre 537 CBC 714 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
538 : {
6067 tgl 539 663 : if (need_recordsep)
540 : {
541 : /* record separator is 2 occurrences of recordsep in this mode */
4077 peter_e 542 267 : print_separator(cont->opt->recordSep, fout);
543 267 : print_separator(cont->opt->recordSep, fout);
6067 tgl 544 267 : need_recordsep = false;
6143 545 267 : if (cancel_pressed)
6143 tgl 546 UBC 0 : break;
547 : }
548 :
5445 alvherre 549 CBC 663 : fputs(cont->headers[i % cont->ncolumns], fout);
4077 peter_e 550 663 : print_separator(cont->opt->fieldSep, fout);
4787 heikki.linnakangas 551 663 : fputs(*ptr, fout);
552 :
5445 alvherre 553 663 : if ((i + 1) % cont->ncolumns)
4077 peter_e 554 348 : print_separator(cont->opt->recordSep, fout);
555 : else
6067 tgl 556 315 : need_recordsep = true;
557 : }
558 :
5445 alvherre 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 :
4077 peter_e 566 3 : print_separator(cont->opt->recordSep, fout);
5445 alvherre 567 6 : for (f = cont->footers; f; f = f->next)
568 : {
4077 peter_e 569 3 : print_separator(cont->opt->recordSep, fout);
5445 alvherre 570 3 : fputs(f->data, fout);
571 : }
572 : }
573 :
574 : /* see above in print_unaligned_text() */
3711 peter_e 575 51 : if (need_recordsep)
576 : {
577 51 : if (cont->opt->recordSep.separator_zero)
3711 peter_e 578 UBC 0 : print_separator(cont->opt->recordSep, fout);
579 : else
3711 peter_e 580 CBC 51 : fputc('\n', fout);
581 : }
582 : }
583 : }
584 :
585 :
586 : /********************/
587 : /* Aligned text */
588 : /********************/
589 :
590 :
591 : /* draw "line" */
592 : static void
5445 alvherre 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 : {
4926 tgl 598 54624 : const printTextLineFormat *lformat = &format->lrule[pos];
599 : unsigned int i,
600 : j;
601 :
8557 bruce 602 54624 : if (border == 1)
4926 tgl 603 54528 : fputs(lformat->hrule, fout);
8557 bruce 604 96 : else if (border == 2)
4926 tgl 605 72 : fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
606 :
5445 alvherre 607 158002 : for (i = 0; i < ncolumns; i++)
608 : {
8557 bruce 609 1699294 : for (j = 0; j < widths[i]; j++)
4926 tgl 610 1595916 : fputs(lformat->hrule, fout);
611 :
5445 alvherre 612 103378 : if (i < ncolumns - 1)
613 : {
8557 bruce 614 48809 : if (border == 0)
615 24 : fputc(' ', fout);
616 : else
4926 tgl 617 48785 : fprintf(fout, "%s%s%s", lformat->hrule,
618 48785 : lformat->midvrule, lformat->hrule);
619 : }
620 : }
621 :
8557 bruce 622 54624 : if (border == 2)
4926 tgl 623 72 : fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
8557 bruce 624 54552 : else if (border == 1)
4926 tgl 625 54528 : fputs(lformat->hrule, fout);
626 :
8557 bruce 627 54624 : fputc('\n', fout);
628 54624 : }
629 :
630 :
631 : /*
632 : * Print pretty boxes around cells.
633 : */
634 : static void
2685 tgl 635 54613 : print_aligned_text(const printTableContent *cont, FILE *fout, bool is_pager)
636 : {
5445 alvherre 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;
4926 tgl 640 54613 : const printTextFormat *format = get_line_style(cont->opt);
641 54613 : const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
642 :
5050 bruce 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;
5441 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 */
2118 tgl 669 54613 : int output_columns = 0; /* Width of interactive console */
2685 670 54613 : bool is_local_pager = false;
671 :
6143 672 54613 : if (cancel_pressed)
6143 tgl 673 UBC 0 : return;
674 :
6067 tgl 675 CBC 54613 : if (opt_border > 2)
6067 tgl 676 UBC 0 : opt_border = 2;
677 :
5445 alvherre 678 CBC 54613 : if (cont->ncolumns > 0)
679 : {
680 54558 : col_count = cont->ncolumns;
3841 tgl 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 : {
5449 bruce 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;
6267 702 55 : col_lineptrs = NULL;
5449 703 55 : max_bytes = NULL;
6267 704 55 : format_buf = NULL;
5449 705 55 : header_done = NULL;
706 55 : bytes_output = NULL;
4886 tgl 707 55 : wrap = NULL;
708 : }
709 :
710 : /* scan all column headers, find maximum width and max max_nl_lines */
7836 bruce 711 157974 : for (i = 0; i < col_count; i++)
712 : {
713 : int width,
714 : nl_lines,
715 : bytes_required;
716 :
4228 peter_e 717 103361 : pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
718 : encoding, &width, &nl_lines, &bytes_required);
5449 bruce 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;
5441 725 103361 : if (nl_lines > extra_row_output_lines)
726 54558 : extra_row_output_lines = nl_lines;
727 :
5449 728 103361 : width_header[i] = width;
729 : }
730 : /* Add height of tallest header column */
5441 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 */
5445 alvherre 735 543926 : for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
736 : {
737 : int width,
738 : nl_lines,
739 : bytes_required;
740 :
4228 peter_e 741 489313 : pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
742 : &width, &nl_lines, &bytes_required);
743 :
5449 bruce 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 : {
5050 757 51869 : int rows = cell_count / col_count;
758 :
5449 759 148038 : for (i = 0; i < col_count; i++)
5440 tgl 760 96169 : width_average[i] /= rows;
761 : }
762 :
763 : /* adjust the total display width based on border style */
8557 bruce 764 54613 : if (opt_border == 0)
4886 tgl 765 24 : width_total = col_count;
8557 bruce 766 54589 : else if (opt_border == 1)
2938 767 54565 : width_total = col_count * 3 - ((col_count > 0) ? 1 : 0);
768 : else
5449 769 24 : width_total = col_count * 3 + 1;
770 54613 : total_header_width = width_total;
771 :
8557 772 157974 : for (i = 0; i < col_count; i++)
773 : {
5449 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 */
3841 tgl 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 :
5449 bruce 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 : */
5441 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)
5441 bruce 808 UBC 0 : output_columns = cont->opt->env_columns;
809 : #ifdef TIOCGWINSZ
810 : else
811 : {
812 : struct winsize screen_size;
813 :
5441 bruce 814 CBC 39 : if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
5441 bruce 815 UBC 0 : output_columns = screen_size.ws_col;
816 : }
817 : #endif
818 : }
819 :
5441 bruce 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 : */
5449 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 :
5447 tgl 850 192 : ratio = (double) width_wrap[i] / width_average[i] +
851 192 : max_width[i] * 0.01;
5449 bruce 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)
5449 bruce 862 UBC 0 : break;
863 :
864 : /* Decrease width of target column by one. */
5449 bruce 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 : */
2585 rhaas 877 54613 : if (cont->opt->expanded == 2 && output_columns > 0 && cont->ncolumns > 1 &&
4166 peter_e 878 UBC 0 : (output_columns < total_header_width || output_columns < width_total))
879 : {
2685 tgl 880 0 : print_aligned_vertical(cont, fout, is_pager);
4050 peter_e 881 0 : goto cleanup;
882 : }
883 :
884 : /* If we wrapped beyond the display width, use the pager */
5440 bruce 885 CBC 54613 : if (!is_pager && fout == stdout && output_columns > 0 &&
5441 886 615 : (output_columns < total_header_width || output_columns < width_total))
887 : {
2934 andrew 888 282 : fout = PageOutput(INT_MAX, cont->opt); /* force pager */
2685 tgl 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 */
5440 896 540533 : for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
897 : {
898 : int width,
899 : nl_lines,
900 : bytes_required;
901 :
4228 peter_e 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 : */
5440 tgl 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" */
3061 andrew 914 448815 : extra_lines = ((width - 1) / width_wrap[i]) + nl_lines - 1;
5440 tgl 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 */
5441 bruce 924 206821 : extra_output_lines += extra_row_output_lines;
925 206821 : extra_row_output_lines = 0;
926 : }
927 : }
4166 peter_e 928 54259 : IsPagerNeeded(cont, extra_output_lines, false, &fout, &is_pager);
2685 tgl 929 54259 : is_local_pager = is_pager;
930 : }
931 :
932 : /* time to output */
5445 alvherre 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 :
4228 peter_e 941 2423 : pg_wcssize((const unsigned char *) cont->title, strlen(cont->title),
942 : encoding, &width, &height, NULL);
5449 bruce 943 2423 : if (width >= width_total)
944 : /* Aligned */
5445 alvherre 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 */
6067 tgl 953 54586 : if (!opt_tuples_only)
954 : {
955 : int more_col_wrapping;
956 : int curr_nl_line;
957 :
6267 bruce 958 54576 : if (opt_border == 2)
4926 tgl 959 24 : _print_horizontal_line(col_count, width_wrap, opt_border,
960 : PRINT_RULE_TOP, format, fout);
961 :
6267 bruce 962 157858 : for (i = 0; i < col_count; i++)
4228 peter_e 963 103282 : pg_wcsformat((const unsigned char *) cont->headers[i],
5445 alvherre 964 103282 : strlen(cont->headers[i]), encoding,
965 103282 : col_lineptrs[i], max_nl_lines[i]);
966 :
5449 bruce 967 54576 : more_col_wrapping = col_count;
968 54576 : curr_nl_line = 0;
402 tgl 969 54576 : if (col_count > 0)
970 54521 : memset(header_done, false, col_count * sizeof(bool));
5449 bruce 971 109169 : while (more_col_wrapping)
972 : {
6067 tgl 973 54593 : if (opt_border == 2)
4886 974 48 : fputs(dformat->leftvrule, fout);
975 :
5445 alvherre 976 158019 : for (i = 0; i < cont->ncolumns; i++)
977 : {
5449 bruce 978 103426 : struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
979 : unsigned int nbspace;
980 :
4886 tgl 981 103426 : if (opt_border != 0 ||
4529 rhaas 982 96 : (!format->wrap_right_border && i > 0))
4886 tgl 983 103354 : fputs(curr_nl_line ? format->header_nl_left : " ",
984 : fout);
985 :
5449 bruce 986 103426 : if (!header_done[i])
987 : {
988 103390 : nbspace = width_wrap[i] - this_line->width;
989 :
990 : /* centered */
6067 tgl 991 103390 : fprintf(fout, "%-*s%s%-*s",
992 103390 : nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
993 :
5449 bruce 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 :
4529 rhaas 1003 103426 : if (opt_border != 0 || format->wrap_right_border)
4886 tgl 1004 103378 : fputs(!header_done[i] ? format->header_nl_right : " ",
1005 : fout);
1006 :
2938 bruce 1007 103426 : if (opt_border != 0 && col_count > 0 && i < col_count - 1)
4886 tgl 1008 48785 : fputs(dformat->midvrule, fout);
1009 : }
5449 bruce 1010 54593 : curr_nl_line++;
1011 :
6067 tgl 1012 54593 : if (opt_border == 2)
4886 1013 48 : fputs(dformat->rightvrule, fout);
6067 1014 54593 : fputc('\n', fout);
1015 : }
1016 :
4926 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 */
5445 alvherre 1023 262178 : for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
1024 : {
1025 : bool more_lines;
1026 :
6143 tgl 1027 207565 : if (cancel_pressed)
6143 tgl 1028 UBC 0 : break;
1029 :
1030 : /*
1031 : * Format each cell.
1032 : */
6267 bruce 1033 CBC 696878 : for (j = 0; j < col_count; j++)
1034 : {
4228 peter_e 1035 489313 : pg_wcsformat((const unsigned char *) ptr[j], strlen(ptr[j]), encoding,
5447 tgl 1036 489313 : col_lineptrs[j], max_nl_lines[j]);
5449 bruce 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 */
8557 1052 215979 : if (opt_border == 2)
4886 tgl 1053 276 : fputs(dformat->leftvrule, fout);
1054 :
1055 : /* for each column */
6267 bruce 1056 715563 : for (j = 0; j < col_count; j++)
1057 : {
1058 : /* We have a valid array element, so index it */
5449 1059 499584 : struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
1060 : int bytes_to_output;
5050 1061 499584 : int chars_to_output = width_wrap[j];
2938 1062 998616 : bool finalspaces = (opt_border == 2 ||
2118 tgl 1063 499032 : (col_count > 0 && j < col_count - 1));
1064 :
1065 : /* Print left-hand wrap or newline mark */
4886 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 :
5449 bruce 1076 499584 : if (!this_line->ptr)
1077 : {
1078 : /* Past newline lines so just pad for other columns */
5447 tgl 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 : */
5449 bruce 1095 498021 : if (chars_to_output > width_wrap[j])
5449 bruce 1096 UBC 0 : chars_to_output = width_wrap[j];
1097 :
5050 bruce 1098 CBC 498021 : if (cont->aligns[j] == 'r') /* Right aligned cell */
1099 : {
1100 : /* spaces first */
5449 1101 204261 : fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
1014 tgl 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 :
5449 bruce 1112 498021 : bytes_output[j] += bytes_to_output;
1113 :
1114 : /* Do we have more text to wrap? */
5447 tgl 1115 498021 : if (*(this_line->ptr + bytes_output[j]) != '\0')
5449 bruce 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 */
4886 tgl 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 : */
2118 1141 499584 : if (cont->aligns[j] != 'r') /* Left aligned cell */
1142 : {
4886 1143 295166 : if (finalspaces ||
1144 150932 : wrap[j] == PRINT_LINE_WRAP_WRAP ||
4790 bruce 1145 150926 : wrap[j] == PRINT_LINE_WRAP_NEWLINE)
4886 tgl 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);
2938 bruce 1155 490876 : else if (opt_border == 2 || (col_count > 0 && j < col_count - 1))
4886 tgl 1156 283242 : fputc(' ', fout);
1157 :
1158 : /* Print column divider, if not the last column */
2938 bruce 1159 499584 : if (opt_border != 0 && (col_count > 0 && j < col_count - 1))
1160 : {
4790 1161 283365 : if (wrap[j + 1] == PRINT_LINE_WRAP_WRAP)
4886 tgl 1162 18 : fputs(format->midvrule_wrap, fout);
4790 bruce 1163 283347 : else if (wrap[j + 1] == PRINT_LINE_WRAP_NEWLINE)
4886 tgl 1164 586 : fputs(format->midvrule_nl, fout);
5050 bruce 1165 282761 : else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL)
4886 tgl 1166 1157 : fputs(format->midvrule_blank, fout);
1167 : else
1168 281604 : fputs(dformat->midvrule, fout);
1169 : }
1170 : }
1171 :
1172 : /* end-of-row border */
8557 bruce 1173 215979 : if (opt_border == 2)
4886 tgl 1174 276 : fputs(dformat->rightvrule, fout);
8557 bruce 1175 215979 : fputc('\n', fout);
5449 1176 215979 : } while (more_lines);
1177 : }
1178 :
5445 alvherre 1179 54613 : if (cont->opt->stop_table)
1180 : {
3995 rhaas 1181 54583 : printTableFooter *footers = footers_with_default(cont);
1182 :
6067 tgl 1183 54583 : if (opt_border == 2 && !cancel_pressed)
4926 1184 24 : _print_horizontal_line(col_count, width_wrap, opt_border,
1185 : PRINT_RULE_BOTTOM, format, fout);
1186 :
1187 : /* print footers */
3995 rhaas 1188 54583 : if (footers && !opt_tuples_only && !cancel_pressed)
1189 : {
1190 : printTableFooter *f;
1191 :
1192 111453 : for (f = footers; f; f = f->next)
5445 alvherre 1193 57124 : fprintf(fout, "%s\n", f->data);
1194 : }
1195 :
6067 tgl 1196 54583 : fputc('\n', fout);
1197 : }
1198 :
4050 peter_e 1199 30 : cleanup:
1200 : /* clean up */
4817 tgl 1201 157974 : for (i = 0; i < col_count; i++)
1202 : {
1203 103361 : free(col_lineptrs[i]);
1204 103361 : free(format_buf[i]);
1205 : }
5449 bruce 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);
6267 1212 54613 : free(col_lineptrs);
5449 1213 54613 : free(max_bytes);
4817 tgl 1214 54613 : free(format_buf);
5449 bruce 1215 54613 : free(header_done);
1216 54613 : free(bytes_output);
4817 tgl 1217 54613 : free(wrap);
1218 :
2685 1219 54613 : if (is_local_pager)
5441 bruce 1220 282 : ClosePager(fout);
1221 : }
1222 :
1223 :
1224 : static void
258 andrew 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;
4790 bruce 1235 ECB : unsigned int i;
4790 bruce 1236 GIC 790 : int reclen = 0;
4926 tgl 1237 ECB :
4926 tgl 1238 GIC 790 : if (opt_border == 2)
4926 tgl 1239 CBC 234 : fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
1240 556 : else if (opt_border == 1)
1241 352 : fputs(lformat->hrule, fout);
4926 tgl 1242 ECB :
4926 tgl 1243 GIC 790 : if (record)
4926 tgl 1244 ECB : {
4926 tgl 1245 GIC 754 : if (opt_border == 0)
4926 tgl 1246 CBC 204 : reclen = fprintf(fout, "* Record %lu", record);
4926 tgl 1247 ECB : else
4926 tgl 1248 GIC 550 : reclen = fprintf(fout, "[ RECORD %lu ]", record);
4926 tgl 1249 ECB : }
4926 tgl 1250 GIC 790 : if (opt_border != 2)
4926 tgl 1251 CBC 556 : reclen++;
1252 790 : if (reclen < 0)
4926 tgl 1253 LBC 0 : reclen = 0;
4926 tgl 1254 GBC 3718 : for (i = reclen; i < hwidth; i++)
4926 tgl 1255 CBC 2928 : fputs(opt_border > 0 ? lformat->hrule : " ", fout);
1256 790 : reclen -= hwidth;
4926 tgl 1257 ECB :
4926 tgl 1258 GIC 790 : if (opt_border > 0)
4926 tgl 1259 ECB : {
4926 tgl 1260 GIC 586 : if (reclen-- <= 0)
4926 tgl 1261 CBC 502 : fputs(lformat->hrule, fout);
1262 586 : if (reclen-- <= 0)
1263 : {
258 andrew 1264 GNC 502 : if (topt->expanded_header_width_type == PRINT_XHEADER_COLUMN)
1265 : {
258 andrew 1266 UNC 0 : fputs(lformat->rightvrule, fout);
1267 : }
1268 : else
1269 : {
258 andrew 1270 GNC 502 : fputs(lformat->midvrule, fout);
1271 : }
1272 : }
1273 586 : if (reclen-- <= 0
1274 502 : && topt->expanded_header_width_type != PRINT_XHEADER_COLUMN)
4926 tgl 1275 CBC 502 : fputs(lformat->hrule, fout);
1276 : }
4926 tgl 1277 EUB : else
1278 : {
4926 tgl 1279 GIC 204 : if (reclen-- <= 0)
1280 180 : fputc(' ', fout);
4926 tgl 1281 ECB : }
1282 :
258 andrew 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 : {
258 andrew 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 :
258 andrew 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 :
4926 tgl 1319 GIC 790 : fputc('\n', fout);
1320 790 : }
4926 tgl 1321 ECB :
8557 bruce 1322 : static void
2685 tgl 1323 GIC 244 : print_aligned_vertical(const printTableContent *cont,
1324 : FILE *fout, bool is_pager)
8557 bruce 1325 ECB : {
5445 alvherre 1326 GIC 244 : bool opt_tuples_only = cont->opt->tuples_only;
5445 alvherre 1327 CBC 244 : unsigned short opt_border = cont->opt->border;
4926 tgl 1328 244 : const printTextFormat *format = get_line_style(cont->opt);
4926 tgl 1329 GIC 244 : const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
5445 alvherre 1330 GBC 244 : int encoding = cont->opt->encoding;
5445 alvherre 1331 GIC 244 : unsigned long record = cont->opt->prior_records + 1;
2118 tgl 1332 EUB : const char *const *ptr;
1333 : unsigned int i,
8557 bruce 1334 GBC 244 : hwidth = 0,
6267 bruce 1335 GIC 244 : dwidth = 0,
6267 bruce 1336 GBC 244 : hheight = 1,
1337 244 : dheight = 1,
1338 244 : hformatsize = 0,
1339 244 : dformatsize = 0;
1340 : struct lineptr *hlineptr,
1341 : *dlineptr;
2685 tgl 1342 GIC 244 : bool is_local_pager = false,
3156 stark 1343 244 : hmultiline = false,
1344 244 : dmultiline = false;
2118 tgl 1345 GBC 244 : int output_columns = 0; /* Width of interactive console */
6143 tgl 1346 EUB :
6143 tgl 1347 GIC 244 : if (cancel_pressed)
6143 tgl 1348 UIC 0 : return;
1349 :
6067 tgl 1350 CBC 244 : if (opt_border > 2)
6067 tgl 1351 LBC 0 : opt_border = 2;
6031 bruce 1352 ECB :
5445 alvherre 1353 CBC 244 : if (cont->cells[0] == NULL && cont->opt->start_table &&
5445 alvherre 1354 GIC 8 : cont->opt->stop_table)
8397 bruce 1355 ECB : {
2938 bruce 1356 CBC 8 : printTableFooter *footers = footers_with_default(cont);
2935 peter_e 1357 ECB :
2938 bruce 1358 CBC 8 : if (!opt_tuples_only && !cancel_pressed && footers)
1359 : {
1360 : printTableFooter *f;
2938 bruce 1361 ECB :
2938 bruce 1362 CBC 16 : for (f = footers; f; f = f->next)
2938 bruce 1363 GIC 8 : fprintf(fout, "%s\n", f->data);
1364 : }
2938 bruce 1365 ECB :
2938 bruce 1366 GIC 8 : fputc('\n', fout);
1367 :
8397 bruce 1368 CBC 8 : return;
8397 bruce 1369 ECB : }
8521 1370 :
4166 peter_e 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 : */
2685 tgl 1376 CBC 236 : if (!is_pager)
2685 tgl 1377 ECB : {
2685 tgl 1378 CBC 224 : IsPagerNeeded(cont, 0, true, &fout, &is_pager);
1379 224 : is_local_pager = is_pager;
2685 tgl 1380 ECB : }
4166 peter_e 1381 :
1382 : /* Find the maximum dimensions for the headers */
5445 alvherre 1383 GIC 1322 : for (i = 0; i < cont->ncolumns; i++)
7846 ishii 1384 ECB : {
5449 bruce 1385 : int width,
1386 : height,
6031 1387 : fs;
1388 :
4228 peter_e 1389 CBC 1086 : pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
5445 alvherre 1390 EUB : encoding, &width, &height, &fs);
5449 bruce 1391 GIC 1086 : if (width > hwidth)
5449 bruce 1392 CBC 302 : hwidth = width;
6267 bruce 1393 GBC 1086 : if (height > hheight)
1394 : {
6267 bruce 1395 CBC 36 : hheight = height;
3156 stark 1396 36 : hmultiline = true;
1397 : }
6267 bruce 1398 1086 : if (fs > hformatsize)
6267 bruce 1399 GIC 302 : hformatsize = fs;
7846 ishii 1400 ECB : }
1401 :
1402 : /* find longest data cell */
5445 alvherre 1403 GIC 2445 : for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
7836 bruce 1404 ECB : {
5449 1405 : int width,
1406 : height,
1407 : fs;
6482 1408 :
4228 peter_e 1409 GIC 2209 : pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
5447 tgl 1410 ECB : &width, &height, &fs);
5449 bruce 1411 GIC 2209 : if (width > dwidth)
1412 460 : dwidth = width;
6267 1413 2209 : if (height > dheight)
1414 : {
1415 42 : dheight = height;
3156 stark 1416 42 : dmultiline = true;
1417 : }
6267 bruce 1418 CBC 2209 : if (fs > dformatsize)
6267 bruce 1419 GIC 464 : dformatsize = fs;
7846 ishii 1420 ECB : }
6031 bruce 1421 :
1422 : /*
1423 : * We now have all the information we need to setup the formatting
1424 : * structures
1425 : */
3841 tgl 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);
8557 bruce 1431 ECB :
5445 alvherre 1432 GIC 236 : if (cont->opt->start_table)
6067 tgl 1433 ECB : {
1434 : /* print title */
5445 alvherre 1435 CBC 230 : if (!opt_tuples_only && cont->title)
5445 alvherre 1436 GIC 9 : fprintf(fout, "%s\n", cont->title);
6067 tgl 1437 ECB : }
1438 :
1439 : /*
3268 stark 1440 : * Choose target output width: \pset columns, or $COLUMNS, or ioctl
1441 : */
3268 stark 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)
3268 stark 1445 ECB : {
3268 stark 1446 GIC 12 : if (cont->opt->env_columns > 0)
3268 stark 1447 UIC 0 : output_columns = cont->opt->env_columns;
1448 : #ifdef TIOCGWINSZ
1449 : else
1450 : {
3268 stark 1451 ECB : struct winsize screen_size;
1452 :
3268 stark 1453 CBC 12 : if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
3268 stark 1454 LBC 0 : output_columns = screen_size.ws_col;
3268 stark 1455 ECB : }
1456 : #endif
1457 : }
1458 :
1459 : /*
2687 tgl 1460 : * Calculate available width for data in wrapped mode
1461 : */
3268 stark 1462 GIC 236 : if (cont->opt->format == PRINT_WRAPPED)
1463 : {
1464 : unsigned int swidth,
2687 tgl 1465 51 : rwidth = 0,
1466 : newdwidth;
1467 :
3268 stark 1468 CBC 51 : if (opt_border == 0)
3156 stark 1469 ECB : {
1470 : /*
2687 tgl 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 : */
3156 stark 1476 GIC 15 : swidth = 1;
2687 tgl 1477 ECB :
1478 : /* We might need a column for header newline markers, too */
2687 tgl 1479 GIC 15 : if (hmultiline)
1480 6 : swidth++;
1481 : }
3268 stark 1482 36 : else if (opt_border == 1)
1483 : {
3156 stark 1484 ECB : /*
2687 tgl 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 : */
3156 stark 1488 CBC 21 : swidth = 3;
2687 tgl 1489 EUB :
1490 : /* We might need a column for left header newline markers, too */
2687 tgl 1491 GIC 21 : if (hmultiline && (format == &pg_asciiformat_old))
1492 3 : swidth++;
1493 : }
1494 : else
2687 tgl 1495 ECB : {
3156 stark 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 : */
3156 stark 1502 GIC 15 : swidth = 7;
1503 : }
3268 stark 1504 ECB :
1505 : /* Reserve a column for data newline indicators, too, if needed */
2687 tgl 1506 GIC 51 : if (dmultiline &&
2687 tgl 1507 CBC 12 : opt_border < 2 && format != &pg_asciiformat_old)
2687 tgl 1508 GIC 6 : swidth++;
1509 :
2687 tgl 1510 ECB : /* Determine width required for record header lines */
3268 stark 1511 GIC 51 : if (!opt_tuples_only)
1512 : {
2686 tgl 1513 48 : if (cont->nrows > 0)
1514 48 : rwidth = 1 + (int) log10(cont->nrows);
3268 stark 1515 48 : if (opt_border == 0)
3156 1516 15 : rwidth += 9; /* "* RECORD " */
3268 1517 33 : else if (opt_border == 1)
3156 stark 1518 CBC 18 : rwidth += 12; /* "-[ RECORD ]" */
1519 : else
3156 stark 1520 GIC 15 : rwidth += 15; /* "+-[ RECORD ]-+" */
2687 tgl 1521 ECB : }
1522 :
1523 : /* We might need to do the rest of the calculation twice */
1524 : for (;;)
2687 tgl 1525 GIC 12 : {
1526 : unsigned int width;
1527 :
1528 : /* Total width required to not wrap data */
1529 63 : width = hwidth + swidth + dwidth;
2686 tgl 1530 ECB : /* ... and not the header lines, either */
2686 tgl 1531 GIC 63 : if (width < rwidth)
2686 tgl 1532 UIC 0 : width = rwidth;
3268 stark 1533 ECB :
2686 tgl 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 */
2686 tgl 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 :
2686 tgl 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;
2686 tgl 1549 ECB : }
2686 tgl 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 : }
2686 tgl 1555 ECB : else
1556 : {
1557 : /* Set data width to match output_columns */
2686 tgl 1558 CBC 39 : newdwidth = output_columns - hwidth - swidth;
2686 tgl 1559 ECB : }
2687 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) */
2686 tgl 1565 UIC 0 : newdwidth = width - hwidth - swidth;
1566 : }
2878 bruce 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.
3156 stark 1571 : */
2687 tgl 1572 GIC 63 : if (newdwidth < dwidth && !dmultiline &&
2687 tgl 1573 CBC 12 : opt_border < 2 && format != &pg_asciiformat_old)
2687 tgl 1574 EUB : {
2687 tgl 1575 GIC 12 : dmultiline = true;
2687 tgl 1576 CBC 12 : swidth++;
1577 : }
1578 : else
1579 : break;
1580 : }
2687 tgl 1581 ECB :
2687 tgl 1582 GIC 51 : dwidth = newdwidth;
3268 stark 1583 ECB : }
1584 :
1585 : /* print records */
5445 alvherre 1586 CBC 2445 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1587 : {
1588 : printTextRule pos;
1589 : int dline,
3268 stark 1590 ECB : hline,
1591 : dcomplete,
1592 : hcomplete,
1593 : offset,
1594 : chars_to_output;
6031 bruce 1595 :
4926 tgl 1596 GIC 2209 : if (cancel_pressed)
4926 tgl 1597 UIC 0 : break;
1598 :
4926 tgl 1599 GIC 2209 : if (i == 0)
4790 bruce 1600 CBC 230 : pos = PRINT_RULE_TOP;
1601 : else
4926 tgl 1602 GIC 1979 : pos = PRINT_RULE_MIDDLE;
1603 :
1604 : /* Print record header (e.g. "[ RECORD N ]") above each record */
5445 alvherre 1605 2209 : if (i % cont->ncolumns == 0)
1606 : {
3156 stark 1607 GBC 766 : unsigned int lhwidth = hwidth;
1608 :
3156 stark 1609 GIC 766 : if ((opt_border < 2) &&
3153 peter_e 1610 48 : (hmultiline) &&
1611 : (format == &pg_asciiformat_old))
2878 bruce 1612 24 : lhwidth++; /* for newline indicators */
1613 :
6478 bruce 1614 CBC 766 : if (!opt_tuples_only)
258 andrew 1615 GNC 754 : print_aligned_vertical_line(cont->opt, record++,
1616 : lhwidth, dwidth, output_columns,
1617 : pos, fout);
5445 alvherre 1618 CBC 12 : else if (i != 0 || !cont->opt->start_table || opt_border == 2)
258 andrew 1619 GNC 6 : print_aligned_vertical_line(cont->opt, 0, lhwidth,
1620 : dwidth, output_columns, pos, fout);
1621 : }
1622 :
1623 : /* Format the header */
4228 peter_e 1624 GIC 2209 : pg_wcsformat((const unsigned char *) cont->headers[i % cont->ncolumns],
5445 alvherre 1625 CBC 2209 : strlen(cont->headers[i % cont->ncolumns]),
1626 : encoding, hlineptr, hheight);
1627 : /* Format the data */
4228 peter_e 1628 GIC 2209 : pg_wcsformat((const unsigned char *) *ptr, strlen(*ptr), encoding,
5447 tgl 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 : */
3268 stark 1635 GIC 2209 : dline = hline = 0;
6267 bruce 1636 2209 : dcomplete = hcomplete = 0;
3268 stark 1637 2209 : offset = 0;
1638 2209 : chars_to_output = dlineptr[dline].width;
6267 bruce 1639 CBC 5822 : while (!dcomplete || !hcomplete)
6482 bruce 1640 EUB : {
1641 : /* Left border */
6267 bruce 1642 CBC 3613 : if (opt_border == 2)
3268 stark 1643 909 : fprintf(fout, "%s", dformat->leftvrule);
1644 :
3268 stark 1645 ECB : /* Header (never wrapped so just need to deal with newlines) */
6267 bruce 1646 GIC 3613 : if (!hcomplete)
1647 : {
3156 stark 1648 CBC 2425 : int swidth = hwidth,
3156 stark 1649 GIC 2425 : target_width = hwidth;
2878 bruce 1650 ECB :
1651 : /*
3156 stark 1652 : * Left spacer or new line indicator
1653 : */
3156 stark 1654 GIC 2425 : if ((opt_border == 2) ||
3156 stark 1655 CBC 240 : (hmultiline && (format == &pg_asciiformat_old)))
3156 stark 1656 GIC 600 : fputs(hline ? format->header_nl_left : " ", fout);
2878 bruce 1657 ECB :
3156 stark 1658 : /*
1659 : * Header text
1660 : */
3156 stark 1661 CBC 2425 : strlen_max_width(hlineptr[hline].ptr, &target_width,
3268 stark 1662 ECB : encoding);
3268 stark 1663 GIC 2425 : fprintf(fout, "%-s", hlineptr[hline].ptr);
1664 :
1665 : /*
1666 : * Spacer
3156 stark 1667 ECB : */
3156 stark 1668 CBC 2425 : swidth -= target_width;
3156 stark 1669 GIC 2425 : if (swidth > 0)
3268 1670 1495 : fprintf(fout, "%*s", swidth, " ");
3268 stark 1671 ECB :
1672 : /*
1673 : * New line indicator or separator's space
1674 : */
3268 stark 1675 GIC 2425 : if (hlineptr[hline + 1].ptr)
1676 : {
1677 : /* More lines after this one due to a newline */
3156 stark 1678 CBC 216 : if ((opt_border > 0) ||
1679 72 : (hmultiline && (format != &pg_asciiformat_old)))
1680 180 : fputs(format->header_nl_right, fout);
3268 1681 216 : hline++;
3267 peter_e 1682 ECB : }
1683 : else
1684 : {
3268 stark 1685 : /* This was the last line of the header */
3156 stark 1686 CBC 2209 : if ((opt_border > 0) ||
3156 stark 1687 GIC 48 : (hmultiline && (format != &pg_asciiformat_old)))
1688 1825 : fputs(" ", fout);
6267 bruce 1689 CBC 2209 : hcomplete = 1;
1690 : }
6267 bruce 1691 ECB : }
6482 1692 : else
1693 : {
3156 stark 1694 GIC 1188 : unsigned int swidth = hwidth + opt_border;
1695 :
1696 1188 : if ((opt_border < 2) &&
3156 stark 1697 CBC 354 : (hmultiline) &&
3156 stark 1698 ECB : (format == &pg_asciiformat_old))
3156 stark 1699 CBC 174 : swidth++;
1700 :
3153 peter_e 1701 GIC 1188 : if ((opt_border == 0) &&
3156 stark 1702 273 : (format != &pg_asciiformat_old) &&
1703 : (hmultiline))
3156 stark 1704 CBC 90 : swidth++;
1705 :
1706 1188 : fprintf(fout, "%*s", swidth, " ");
1707 : }
1708 :
1709 : /* Separator */
6267 bruce 1710 GIC 3613 : if (opt_border > 0)
3268 stark 1711 ECB : {
3268 stark 1712 CBC 2776 : if (offset)
1713 558 : fputs(format->midvrule_wrap, fout);
2687 tgl 1714 GIC 2218 : else if (dline == 0)
3268 stark 1715 1801 : fputs(dformat->midvrule, fout);
1716 : else
2687 tgl 1717 417 : fputs(format->midvrule_nl, fout);
3268 stark 1718 ECB : }
1719 :
1720 : /* Data */
6267 bruce 1721 CBC 3613 : if (!dcomplete)
6267 bruce 1722 ECB : {
3156 stark 1723 CBC 3523 : int target_width = dwidth,
3260 bruce 1724 ECB : bytes_to_output,
3156 stark 1725 GIC 3523 : swidth = dwidth;
1726 :
1727 : /*
1728 : * Left spacer or wrap indicator
3156 stark 1729 ECB : */
2687 tgl 1730 CBC 3523 : fputs(offset == 0 ? " " : format->wrap_left, fout);
6031 bruce 1731 ECB :
3156 stark 1732 : /*
1733 : * Data text
1734 : */
3268 stark 1735 GIC 3523 : bytes_to_output = strlen_max_width(dlineptr[dline].ptr + offset,
1736 : &target_width, encoding);
1014 tgl 1737 CBC 3523 : fwrite((char *) (dlineptr[dline].ptr + offset),
1738 : 1, bytes_to_output, fout);
3268 stark 1739 ECB :
3268 stark 1740 CBC 3523 : chars_to_output -= target_width;
3268 stark 1741 GIC 3523 : offset += bytes_to_output;
3268 stark 1742 ECB :
1743 : /* Spacer */
3156 stark 1744 CBC 3523 : swidth -= target_width;
3268 stark 1745 ECB :
3268 stark 1746 GIC 3523 : if (chars_to_output)
3268 stark 1747 ECB : {
1748 : /* continuing a wrapped column */
3156 stark 1749 CBC 705 : if ((opt_border > 1) ||
3156 stark 1750 GIC 426 : (dmultiline && (format != &pg_asciiformat_old)))
1751 : {
1752 681 : if (swidth > 0)
3156 stark 1753 LBC 0 : fprintf(fout, "%*s", swidth, " ");
3156 stark 1754 GIC 681 : fputs(format->wrap_right, fout);
3156 stark 1755 ECB : }
3268 1756 : }
3268 stark 1757 CBC 2818 : else if (dlineptr[dline + 1].ptr)
3268 stark 1758 ECB : {
1759 : /* reached a newline in the column */
3156 stark 1760 CBC 609 : if ((opt_border > 1) ||
3156 stark 1761 GIC 417 : (dmultiline && (format != &pg_asciiformat_old)))
1762 : {
1763 417 : if (swidth > 0)
3156 stark 1764 CBC 408 : fprintf(fout, "%*s", swidth, " ");
3156 stark 1765 GIC 417 : fputs(format->nl_right, fout);
3156 stark 1766 ECB : }
3268 stark 1767 GIC 609 : dline++;
3268 stark 1768 CBC 609 : offset = 0;
3268 stark 1769 GIC 609 : chars_to_output = dlineptr[dline].width;
1770 : }
1771 : else
1772 : {
3268 stark 1773 ECB : /* reached the end of the cell */
3156 stark 1774 GIC 2209 : if (opt_border > 1)
1775 : {
1776 408 : if (swidth > 0)
1777 369 : fprintf(fout, "%*s", swidth, " ");
3156 stark 1778 CBC 408 : fputs(" ", fout);
1779 : }
6031 bruce 1780 2209 : dcomplete = 1;
1781 : }
1782 :
3156 stark 1783 ECB : /* Right border */
3268 stark 1784 CBC 3523 : if (opt_border == 2)
3268 stark 1785 GIC 879 : fputs(dformat->rightvrule, fout);
1786 :
3268 stark 1787 CBC 3523 : fputs("\n", fout);
1788 : }
6031 bruce 1789 ECB : else
1790 : {
1791 : /*
3260 1792 : * data exhausted (this can occur if header is longer than the
1793 : * data due to newlines in the header)
1794 : */
6031 bruce 1795 CBC 90 : if (opt_border < 2)
3268 stark 1796 GBC 60 : fputs("\n", fout);
6031 bruce 1797 ECB : else
3268 stark 1798 GIC 30 : fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule);
1799 : }
6031 bruce 1800 ECB : }
1801 : }
1802 :
5445 alvherre 1803 CBC 236 : if (cont->opt->stop_table)
6067 tgl 1804 ECB : {
6067 tgl 1805 GIC 230 : if (opt_border == 2 && !cancel_pressed)
258 andrew 1806 GNC 30 : print_aligned_vertical_line(cont->opt, 0, hwidth, dwidth,
1807 : output_columns, PRINT_RULE_BOTTOM, fout);
8557 bruce 1808 ECB :
1809 : /* print footers */
5445 alvherre 1810 CBC 230 : if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
6067 tgl 1811 ECB : {
5445 alvherre 1812 : printTableFooter *f;
1813 :
6067 tgl 1814 GIC 6 : if (opt_border < 2)
1815 6 : fputc('\n', fout);
5445 alvherre 1816 12 : for (f = cont->footers; f; f = f->next)
5445 alvherre 1817 CBC 6 : fprintf(fout, "%s\n", f->data);
1818 : }
8557 bruce 1819 ECB :
6067 tgl 1820 CBC 230 : fputc('\n', fout);
8557 bruce 1821 ECB : }
1822 :
6267 bruce 1823 CBC 236 : free(hlineptr->ptr);
6267 bruce 1824 GIC 236 : free(dlineptr->ptr);
1825 236 : free(hlineptr);
1826 236 : free(dlineptr);
4166 peter_e 1827 ECB :
2685 tgl 1828 CBC 236 : if (is_local_pager)
4166 peter_e 1829 UIC 0 : ClosePager(fout);
8557 bruce 1830 ECB : }
1831 :
1832 :
1833 : /**********************/
1834 : /* CSV format */
1835 : /**********************/
1836 :
1837 :
1595 tgl 1838 : static void
1595 tgl 1839 CBC 54 : csv_escaped_print(const char *str, FILE *fout)
1840 : {
1595 tgl 1841 ECB : const char *p;
1842 :
1595 tgl 1843 GIC 54 : fputc('"', fout);
1844 462 : for (p = str; *p; p++)
1845 : {
1595 tgl 1846 CBC 408 : if (*p == '"')
1595 tgl 1847 GIC 21 : fputc('"', fout); /* double quotes are doubled */
1595 tgl 1848 CBC 408 : fputc(*p, fout);
1595 tgl 1849 ECB : }
1595 tgl 1850 GIC 54 : fputc('"', fout);
1851 54 : }
1852 :
1595 tgl 1853 ECB : static void
1595 tgl 1854 GIC 312 : csv_print_field(const char *str, FILE *fout, char sep)
1855 : {
1856 : /*----------------
1595 tgl 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 : */
1595 tgl 1869 CBC 312 : if (strchr(str, sep) != NULL ||
1595 tgl 1870 GIC 306 : strcspn(str, "\r\n\"") != strlen(str) ||
1595 tgl 1871 CBC 273 : strcmp(str, "\\.") == 0 ||
1595 tgl 1872 GBC 270 : sep == '\\' || sep == '.')
1595 tgl 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;
1595 tgl 1882 ECB : int i;
1883 :
1595 tgl 1884 GIC 24 : if (cancel_pressed)
1595 tgl 1885 UIC 0 : return;
1595 tgl 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 : */
1595 tgl 1895 GIC 24 : if (cont->opt->start_table && !cont->opt->tuples_only)
1896 : {
1595 tgl 1897 ECB : /* print headers */
1595 tgl 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)
1595 tgl 1912 CBC 72 : fputc(cont->opt->csvFieldSep[0], fout);
1595 tgl 1913 ECB : else
1595 tgl 1914 CBC 30 : fputc('\n', fout);
1595 tgl 1915 ECB : }
1916 : }
1917 :
1918 : static void
1595 tgl 1919 CBC 9 : print_csv_vertical(const printTableContent *cont, FILE *fout)
1920 : {
1921 : const char *const *ptr;
1595 tgl 1922 ECB : int i;
1923 :
1924 : /* print records */
1595 tgl 1925 GIC 84 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1926 : {
1595 tgl 1927 CBC 75 : if (cancel_pressed)
1595 tgl 1928 UBC 0 : return;
1929 :
1930 : /* print name of column */
1595 tgl 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 */
1595 tgl 1938 CBC 75 : csv_print_field(*ptr, fout, cont->opt->csvFieldSep[0]);
1939 :
1595 tgl 1940 GIC 75 : fputc('\n', fout);
1595 tgl 1941 ECB : }
1942 : }
1943 :
1944 :
1945 : /**********************/
1946 : /* HTML */
8557 bruce 1947 : /**********************/
1948 :
1949 :
1950 : void
8557 bruce 1951 CBC 411 : html_escaped_print(const char *in, FILE *fout)
1952 : {
8557 bruce 1953 ECB : const char *p;
6385 bruce 1954 CBC 411 : bool leading_space = true;
6385 bruce 1955 ECB :
8557 bruce 1956 GIC 3450 : for (p = in; *p; p++)
6508 bruce 1957 ECB : {
8557 bruce 1958 GIC 3039 : switch (*p)
1959 : {
1960 27 : case '&':
1961 27 : fputs("&", fout);
8557 bruce 1962 CBC 27 : break;
8557 bruce 1963 GIC 72 : case '<':
1964 72 : fputs("<", fout);
1965 72 : break;
1966 72 : case '>':
1967 72 : fputs(">", fout);
8557 bruce 1968 CBC 72 : break;
8557 bruce 1969 GIC 36 : case '\n':
7241 bruce 1970 CBC 36 : fputs("<br />\n", fout);
7241 bruce 1971 GBC 36 : break;
7241 bruce 1972 GIC 48 : case '"':
1973 48 : fputs(""", fout);
7241 bruce 1974 CBC 48 : break;
6508 1975 135 : case ' ':
1976 : /* protect leading space, for EXPLAIN output */
6508 bruce 1977 GIC 135 : if (leading_space)
6508 bruce 1978 CBC 72 : fputs(" ", fout);
1979 : else
6508 bruce 1980 GIC 63 : fputs(" ", fout);
6508 bruce 1981 CBC 135 : break;
8557 bruce 1982 GIC 2649 : default:
8557 bruce 1983 CBC 2649 : fputc(*p, fout);
1984 : }
6508 bruce 1985 GIC 3039 : if (*p != ' ')
1986 2904 : leading_space = false;
1987 : }
8557 1988 411 : }
1989 :
1990 :
1991 : static void
5445 alvherre 1992 15 : print_html_text(const printTableContent *cont, FILE *fout)
1993 : {
5445 alvherre 1994 CBC 15 : bool opt_tuples_only = cont->opt->tuples_only;
5445 alvherre 1995 GIC 15 : unsigned short opt_border = cont->opt->border;
1996 15 : const char *opt_table_attr = cont->opt->tableAttr;
8557 bruce 1997 ECB : unsigned int i;
1998 : const char *const *ptr;
1999 :
6143 tgl 2000 GIC 15 : if (cancel_pressed)
6143 tgl 2001 LBC 0 : return;
2002 :
5445 alvherre 2003 CBC 15 : if (cont->opt->start_table)
8557 bruce 2004 ECB : {
6067 tgl 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);
8557 bruce 2009 ECB :
6067 tgl 2010 : /* print title */
5445 alvherre 2011 CBC 15 : if (!opt_tuples_only && cont->title)
6067 tgl 2012 ECB : {
6067 tgl 2013 CBC 3 : fputs(" <caption>", fout);
5445 alvherre 2014 3 : html_escaped_print(cont->title, fout);
6067 tgl 2015 3 : fputs("</caption>\n", fout);
6067 tgl 2016 ECB : }
2017 :
2018 : /* print headers */
6478 bruce 2019 GIC 15 : if (!opt_tuples_only)
8557 bruce 2020 ECB : {
6067 tgl 2021 CBC 12 : fputs(" <tr>\n", fout);
5445 alvherre 2022 GIC 69 : for (ptr = cont->headers; *ptr; ptr++)
6067 tgl 2023 ECB : {
6067 tgl 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);
8557 bruce 2029 ECB : }
2030 : }
2031 :
2032 : /* print cells */
5445 alvherre 2033 GIC 138 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2034 : {
5445 alvherre 2035 CBC 123 : if (i % cont->ncolumns == 0)
2036 : {
6143 tgl 2037 27 : if (cancel_pressed)
6143 tgl 2038 LBC 0 : break;
7241 bruce 2039 CBC 27 : fputs(" <tr valign=\"top\">\n", fout);
2040 : }
2041 :
5445 alvherre 2042 GIC 123 : fprintf(fout, " <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
6482 bruce 2043 ECB : /* is string only whitespace? */
6385 bruce 2044 GBC 123 : if ((*ptr)[strspn(*ptr, " \t")] == '\0')
7241 bruce 2045 GIC 18 : fputs(" ", fout);
8557 bruce 2046 ECB : else
8557 bruce 2047 GIC 105 : html_escaped_print(*ptr, fout);
6482 bruce 2048 ECB :
8557 bruce 2049 CBC 123 : fputs("</td>\n", fout);
8557 bruce 2050 ECB :
5445 alvherre 2051 CBC 123 : if ((i + 1) % cont->ncolumns == 0)
8557 bruce 2052 GIC 27 : fputs(" </tr>\n", fout);
2053 : }
8557 bruce 2054 ECB :
5445 alvherre 2055 GIC 15 : if (cont->opt->stop_table)
7241 bruce 2056 ECB : {
3995 rhaas 2057 CBC 15 : printTableFooter *footers = footers_with_default(cont);
3995 rhaas 2058 ECB :
6067 tgl 2059 GIC 15 : fputs("</table>\n", fout);
2060 :
2061 : /* print footers */
3995 rhaas 2062 CBC 15 : if (!opt_tuples_only && footers != NULL && !cancel_pressed)
2063 : {
5445 alvherre 2064 ECB : printTableFooter *f;
2065 :
6067 tgl 2066 GIC 12 : fputs("<p>", fout);
3995 rhaas 2067 CBC 24 : for (f = footers; f; f = f->next)
6067 tgl 2068 ECB : {
5445 alvherre 2069 CBC 12 : html_escaped_print(f->data, fout);
6067 tgl 2070 GIC 12 : fputs("<br />\n", fout);
6067 tgl 2071 ECB : }
6067 tgl 2072 GIC 12 : fputs("</p>", fout);
2073 : }
2074 :
2075 15 : fputc('\n', fout);
7241 bruce 2076 ECB : }
2077 : }
8557 2078 :
2079 :
2080 : static void
5445 alvherre 2081 GBC 15 : print_html_vertical(const printTableContent *cont, FILE *fout)
8557 bruce 2082 ECB : {
5445 alvherre 2083 GIC 15 : bool opt_tuples_only = cont->opt->tuples_only;
2084 15 : unsigned short opt_border = cont->opt->border;
5445 alvherre 2085 CBC 15 : const char *opt_table_attr = cont->opt->tableAttr;
5445 alvherre 2086 GIC 15 : unsigned long record = cont->opt->prior_records + 1;
8557 bruce 2087 ECB : unsigned int i;
2118 tgl 2088 : const char *const *ptr;
2089 :
6143 tgl 2090 CBC 15 : if (cancel_pressed)
6143 tgl 2091 UIC 0 : return;
6143 tgl 2092 ECB :
5445 alvherre 2093 GIC 15 : if (cont->opt->start_table)
6067 tgl 2094 ECB : {
6067 tgl 2095 CBC 15 : fprintf(fout, "<table border=\"%d\"", opt_border);
6067 tgl 2096 GIC 15 : if (opt_table_attr)
2097 3 : fprintf(fout, " %s", opt_table_attr);
6067 tgl 2098 CBC 15 : fputs(">\n", fout);
2099 :
6067 tgl 2100 ECB : /* print title */
5445 alvherre 2101 GIC 15 : if (!opt_tuples_only && cont->title)
6067 tgl 2102 ECB : {
6067 tgl 2103 GIC 3 : fputs(" <caption>", fout);
5445 alvherre 2104 3 : html_escaped_print(cont->title, fout);
6067 tgl 2105 CBC 3 : fputs("</caption>\n", fout);
2106 : }
2107 : }
2108 :
8557 bruce 2109 ECB : /* print records */
5445 alvherre 2110 CBC 138 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2111 : {
2112 123 : if (i % cont->ncolumns == 0)
8557 bruce 2113 ECB : {
6143 tgl 2114 GIC 27 : if (cancel_pressed)
6143 tgl 2115 LBC 0 : break;
6478 bruce 2116 GIC 27 : if (!opt_tuples_only)
6067 tgl 2117 21 : fprintf(fout,
6067 tgl 2118 ECB : "\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
2119 : record++);
2120 : else
7241 bruce 2121 GIC 6 : fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout);
2122 : }
2123 123 : fputs(" <tr valign=\"top\">\n"
8557 bruce 2124 ECB : " <th>", fout);
5445 alvherre 2125 GIC 123 : html_escaped_print(cont->headers[i % cont->ncolumns], fout);
8557 bruce 2126 CBC 123 : fputs("</th>\n", fout);
8557 bruce 2127 ECB :
5445 alvherre 2128 CBC 123 : fprintf(fout, " <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
6482 bruce 2129 ECB : /* is string only whitespace? */
6385 bruce 2130 GIC 123 : if ((*ptr)[strspn(*ptr, " \t")] == '\0')
7241 2131 18 : fputs(" ", fout);
2132 : else
8557 bruce 2133 CBC 105 : html_escaped_print(*ptr, fout);
6482 bruce 2134 EUB :
8557 bruce 2135 GIC 123 : fputs("</td>\n </tr>\n", fout);
8557 bruce 2136 ECB : }
2137 :
5445 alvherre 2138 CBC 15 : if (cont->opt->stop_table)
7241 bruce 2139 ECB : {
6067 tgl 2140 CBC 15 : fputs("</table>\n", fout);
6067 tgl 2141 ECB :
2142 : /* print footers */
5445 alvherre 2143 GIC 15 : if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
8557 bruce 2144 ECB : {
2145 : printTableFooter *f;
5445 alvherre 2146 :
6067 tgl 2147 CBC 3 : fputs("<p>", fout);
5445 alvherre 2148 6 : for (f = cont->footers; f; f = f->next)
2149 : {
5445 alvherre 2150 GIC 3 : html_escaped_print(f->data, fout);
6067 tgl 2151 3 : fputs("<br />\n", fout);
2152 : }
6067 tgl 2153 CBC 3 : fputs("</p>", fout);
2154 : }
6067 tgl 2155 ECB :
6067 tgl 2156 GIC 15 : fputc('\n', fout);
7241 bruce 2157 ECB : }
8557 bruce 2158 EUB : }
8557 bruce 2159 ECB :
2160 :
2161 : /*************************/
2162 : /* ASCIIDOC */
2163 : /*************************/
2931 2164 :
2165 :
2166 : static void
2931 bruce 2167 GIC 327 : asciidoc_escaped_print(const char *in, FILE *fout)
2931 bruce 2168 ECB : {
2169 : const char *p;
2170 :
2931 bruce 2171 CBC 2295 : for (p = in; *p; p++)
2172 : {
2878 2173 1968 : switch (*p)
2931 bruce 2174 ECB : {
2931 bruce 2175 GIC 63 : case '|':
2931 bruce 2176 CBC 63 : fputs("\\|", fout);
2931 bruce 2177 GIC 63 : break;
2931 bruce 2178 CBC 1905 : default:
2931 bruce 2179 GIC 1905 : fputc(*p, fout);
2180 : }
2931 bruce 2181 ECB : }
2931 bruce 2182 GIC 327 : }
2931 bruce 2183 ECB :
2184 : static void
2931 bruce 2185 GIC 15 : print_asciidoc_text(const printTableContent *cont, FILE *fout)
2931 bruce 2186 ECB : {
2931 bruce 2187 GIC 15 : bool opt_tuples_only = cont->opt->tuples_only;
2188 15 : unsigned short opt_border = cont->opt->border;
2189 : unsigned int i;
2118 tgl 2190 ECB : const char *const *ptr;
2931 bruce 2191 :
2931 bruce 2192 GIC 15 : if (cancel_pressed)
2931 bruce 2193 LBC 0 : return;
2931 bruce 2194 ECB :
2931 bruce 2195 GIC 15 : if (cont->opt->start_table)
2931 bruce 2196 ECB : {
2197 : /* print table in new paragraph - enforce preliminary new line */
2931 bruce 2198 GIC 15 : fputs("\n", fout);
2931 bruce 2199 ECB :
2200 : /* print title */
2931 bruce 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\"," : "");
2878 bruce 2210 CBC 78 : for (i = 0; i < cont->ncolumns; i++)
2211 : {
2931 bruce 2212 GIC 63 : if (i != 0)
2213 48 : fputs(",", fout);
2931 bruce 2214 CBC 63 : fprintf(fout, "%s", cont->aligns[(i) % cont->ncolumns] == 'r' ? ">l" : "<l");
2215 : }
2216 15 : fputs("\"", fout);
2931 bruce 2217 GIC 15 : switch (opt_border)
2931 bruce 2218 ECB : {
2931 bruce 2219 CBC 3 : case 0:
2220 3 : fputs(",frame=\"none\",grid=\"none\"", fout);
2221 3 : break;
2222 9 : case 1:
2931 bruce 2223 GIC 9 : fputs(",frame=\"none\"", fout);
2224 9 : break;
2931 bruce 2225 CBC 3 : case 2:
2931 bruce 2226 GIC 3 : fputs(",frame=\"all\",grid=\"all\"", fout);
2227 3 : break;
2931 bruce 2228 ECB : }
2931 bruce 2229 GIC 15 : fputs("]\n", fout);
2931 bruce 2230 CBC 15 : fputs("|====\n", fout);
2931 bruce 2231 ECB :
2232 : /* print headers */
2931 bruce 2233 GIC 15 : if (!opt_tuples_only)
2234 : {
2931 bruce 2235 CBC 60 : for (ptr = cont->headers; *ptr; ptr++)
2931 bruce 2236 EUB : {
2931 bruce 2237 GIC 48 : if (ptr != cont->headers)
2931 bruce 2238 CBC 36 : fputs(" ", fout);
2931 bruce 2239 GIC 48 : fputs("^l|", fout);
2240 48 : asciidoc_escaped_print(*ptr, fout);
2931 bruce 2241 ECB : }
2931 bruce 2242 GIC 12 : fputs("\n", fout);
2243 : }
2931 bruce 2244 ECB : }
2245 :
2246 : /* print cells */
2931 bruce 2247 CBC 120 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2931 bruce 2248 ECB : {
2931 bruce 2249 GIC 105 : if (i % cont->ncolumns == 0)
2250 : {
2251 27 : if (cancel_pressed)
2931 bruce 2252 LBC 0 : break;
2931 bruce 2253 ECB : }
2254 :
2931 bruce 2255 CBC 105 : if (i % cont->ncolumns != 0)
2256 78 : fputs(" ", fout);
2257 105 : fputs("|", fout);
2258 :
2931 bruce 2259 ECB : /* protect against needless spaces */
2931 bruce 2260 CBC 105 : if ((*ptr)[strspn(*ptr, " \t")] == '\0')
2261 : {
2262 18 : if ((i + 1) % cont->ncolumns != 0)
2263 18 : fputs(" ", fout);
2931 bruce 2264 ECB : }
2265 : else
2931 bruce 2266 CBC 87 : asciidoc_escaped_print(*ptr, fout);
2931 bruce 2267 ECB :
2931 bruce 2268 CBC 105 : if ((i + 1) % cont->ncolumns == 0)
2269 27 : fputs("\n", fout);
2931 bruce 2270 ECB : }
2271 :
2931 bruce 2272 CBC 15 : fputs("|====\n", fout);
2931 bruce 2273 ECB :
2931 bruce 2274 GIC 15 : if (cont->opt->stop_table)
2275 : {
2931 bruce 2276 CBC 15 : printTableFooter *footers = footers_with_default(cont);
2277 :
2931 bruce 2278 ECB : /* print footers */
2931 bruce 2279 GIC 15 : if (!opt_tuples_only && footers != NULL && !cancel_pressed)
2931 bruce 2280 ECB : {
2281 : printTableFooter *f;
2282 :
2931 bruce 2283 CBC 12 : fputs("\n....\n", fout);
2931 bruce 2284 GIC 24 : for (f = footers; f; f = f->next)
2931 bruce 2285 ECB : {
2931 bruce 2286 GIC 12 : fputs(f->data, fout);
2287 12 : fputs("\n", fout);
2288 : }
2289 12 : fputs("....\n", fout);
2931 bruce 2290 ECB : }
2291 : }
2292 : }
2293 :
2294 : static void
2931 bruce 2295 GBC 15 : print_asciidoc_vertical(const printTableContent *cont, FILE *fout)
2296 : {
2931 bruce 2297 GIC 15 : bool opt_tuples_only = cont->opt->tuples_only;
2931 bruce 2298 CBC 15 : unsigned short opt_border = cont->opt->border;
2299 15 : unsigned long record = cont->opt->prior_records + 1;
2931 bruce 2300 ECB : unsigned int i;
2301 : const char *const *ptr;
2302 :
2931 bruce 2303 CBC 15 : if (cancel_pressed)
2931 bruce 2304 UIC 0 : return;
2931 bruce 2305 ECB :
2931 bruce 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 :
2931 bruce 2311 ECB : /* print title */
2931 bruce 2312 CBC 15 : if (!opt_tuples_only && cont->title)
2313 : {
2931 bruce 2314 GIC 3 : fputs(".", fout);
2931 bruce 2315 CBC 3 : fputs(cont->title, fout);
2931 bruce 2316 GIC 3 : fputs("\n", fout);
2931 bruce 2317 ECB : }
2318 :
2319 : /* print table [] header definition */
2931 bruce 2320 GIC 15 : fputs("[cols=\"h,l\"", fout);
2321 15 : switch (opt_border)
2931 bruce 2322 ECB : {
2931 bruce 2323 GIC 3 : case 0:
2324 3 : fputs(",frame=\"none\",grid=\"none\"", fout);
2325 3 : break;
2931 bruce 2326 CBC 9 : case 1:
2327 9 : fputs(",frame=\"none\"", fout);
2931 bruce 2328 GIC 9 : break;
2931 bruce 2329 CBC 3 : case 2:
2330 3 : fputs(",frame=\"all\",grid=\"all\"", fout);
2878 bruce 2331 GIC 3 : break;
2931 bruce 2332 ECB : }
2931 bruce 2333 GIC 15 : fputs("]\n", fout);
2334 15 : fputs("|====\n", fout);
2335 : }
2336 :
2337 : /* print records */
2931 bruce 2338 CBC 120 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2339 : {
2340 105 : if (i % cont->ncolumns == 0)
2931 bruce 2341 ECB : {
2931 bruce 2342 CBC 27 : if (cancel_pressed)
2931 bruce 2343 UIC 0 : break;
2931 bruce 2344 GIC 27 : if (!opt_tuples_only)
2345 21 : fprintf(fout,
2931 bruce 2346 ECB : "2+^|Record %lu\n",
2931 bruce 2347 EUB : record++);
2348 : else
2931 bruce 2349 CBC 6 : fputs("2+|\n", fout);
2350 : }
2351 :
2352 105 : fputs("<l|", fout);
2931 bruce 2353 GIC 105 : asciidoc_escaped_print(cont->headers[i % cont->ncolumns], fout);
2354 :
2931 bruce 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);
2931 bruce 2359 ECB : else
2931 bruce 2360 GIC 87 : asciidoc_escaped_print(*ptr, fout);
2361 105 : fputs("\n", fout);
2362 : }
2931 bruce 2363 ECB :
2931 bruce 2364 CBC 15 : fputs("|====\n", fout);
2365 :
2366 15 : if (cont->opt->stop_table)
2931 bruce 2367 ECB : {
2368 : /* print footers */
2931 bruce 2369 CBC 15 : if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
2931 bruce 2370 ECB : {
2371 : printTableFooter *f;
2372 :
2931 bruce 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 : }
2931 bruce 2379 GIC 3 : fputs("....\n", fout);
2380 : }
2931 bruce 2381 ECB : }
2382 : }
2383 :
2384 :
8557 2385 : /*************************/
6067 tgl 2386 EUB : /* LaTeX */
8557 bruce 2387 ECB : /*************************/
2388 :
2389 :
2390 : static void
8557 bruce 2391 GIC 1227 : latex_escaped_print(const char *in, FILE *fout)
8557 bruce 2392 ECB : {
2393 : const char *p;
2394 :
8557 bruce 2395 CBC 10782 : for (p = in; *p; p++)
2396 9555 : switch (*p)
2397 : {
1595 tgl 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 : */
1595 tgl 2403 CBC 108 : case '#':
2404 108 : fputs("\\#", fout);
1595 tgl 2405 GIC 108 : break;
2406 96 : case '$':
1595 tgl 2407 CBC 96 : fputs("\\$", fout);
8557 bruce 2408 GIC 96 : break;
8557 bruce 2409 CBC 108 : case '%':
8557 bruce 2410 GIC 108 : fputs("\\%", fout);
2411 108 : break;
1595 tgl 2412 CBC 108 : case '&':
1595 tgl 2413 GIC 108 : fputs("\\&", fout);
2414 108 : break;
2415 108 : case '<':
1595 tgl 2416 CBC 108 : fputs("\\textless{}", fout);
2417 108 : break;
1595 tgl 2418 GIC 108 : case '>':
1595 tgl 2419 CBC 108 : fputs("\\textgreater{}", fout);
2420 108 : break;
1595 tgl 2421 GIC 108 : case '\\':
1595 tgl 2422 CBC 108 : fputs("\\textbackslash{}", fout);
1595 tgl 2423 GIC 108 : break;
2424 108 : case '^':
2425 108 : fputs("\\^{}", fout);
8557 bruce 2426 108 : break;
6820 2427 234 : case '_':
2428 234 : fputs("\\_", fout);
2429 234 : break;
8557 2430 108 : case '{':
2431 108 : fputs("\\{", fout);
2432 108 : break;
1595 tgl 2433 108 : case '|':
1595 tgl 2434 CBC 108 : fputs("\\textbar{}", fout);
1595 tgl 2435 GIC 108 : break;
8557 bruce 2436 108 : case '}':
2437 108 : fputs("\\}", fout);
8557 bruce 2438 CBC 108 : break;
1595 tgl 2439 108 : case '~':
1595 tgl 2440 GIC 108 : fputs("\\~{}", fout);
8557 bruce 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;
8557 bruce 2446 CBC 7929 : default:
2447 7929 : fputc(*p, fout);
8557 bruce 2448 ECB : }
8557 bruce 2449 CBC 1227 : }
8557 bruce 2450 ECB :
2451 :
2452 : static void
5445 alvherre 2453 CBC 18 : print_latex_text(const printTableContent *cont, FILE *fout)
8557 bruce 2454 ECB : {
5445 alvherre 2455 CBC 18 : bool opt_tuples_only = cont->opt->tuples_only;
2456 18 : unsigned short opt_border = cont->opt->border;
8557 bruce 2457 ECB : unsigned int i;
2118 tgl 2458 : const char *const *ptr;
8557 bruce 2459 :
6143 tgl 2460 CBC 18 : if (cancel_pressed)
6143 tgl 2461 LBC 0 : return;
8557 bruce 2462 ECB :
3734 bruce 2463 CBC 18 : if (opt_border > 3)
3734 bruce 2464 LBC 0 : opt_border = 3;
8557 bruce 2465 ECB :
5445 alvherre 2466 CBC 18 : if (cont->opt->start_table)
8557 bruce 2467 ECB : {
6067 tgl 2468 : /* print title */
5445 alvherre 2469 CBC 18 : if (!opt_tuples_only && cont->title)
6067 tgl 2470 ECB : {
6067 tgl 2471 CBC 3 : fputs("\\begin{center}\n", fout);
5445 alvherre 2472 3 : latex_escaped_print(cont->title, fout);
6067 tgl 2473 3 : fputs("\n\\end{center}\n\n", fout);
6067 tgl 2474 ECB : }
6820 bruce 2475 :
6067 tgl 2476 : /* begin environment and set alignments and borders */
6067 tgl 2477 CBC 18 : fputs("\\begin{tabular}{", fout);
8557 bruce 2478 ECB :
3734 bruce 2479 CBC 18 : if (opt_border >= 2)
6067 tgl 2480 6 : fputs("| ", fout);
5445 alvherre 2481 102 : for (i = 0; i < cont->ncolumns; i++)
6067 tgl 2482 ECB : {
5445 alvherre 2483 CBC 84 : fputc(*(cont->aligns + i), fout);
2484 84 : if (opt_border != 0 && i < cont->ncolumns - 1)
6067 tgl 2485 57 : fputs(" | ", fout);
2486 : }
3734 bruce 2487 18 : if (opt_border >= 2)
6067 tgl 2488 6 : fputs(" |", fout);
8557 bruce 2489 ECB :
6067 tgl 2490 CBC 18 : fputs("}\n", fout);
2491 :
3734 bruce 2492 18 : if (!opt_tuples_only && opt_border >= 2)
6067 tgl 2493 GIC 6 : fputs("\\hline\n", fout);
2494 :
2495 : /* print headers */
6478 bruce 2496 CBC 18 : if (!opt_tuples_only)
2497 : {
5445 alvherre 2498 84 : for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
6067 tgl 2499 ECB : {
6067 tgl 2500 GIC 69 : if (i != 0)
2501 54 : fputs(" & ", fout);
2502 69 : fputs("\\textit{", fout);
6067 tgl 2503 CBC 69 : latex_escaped_print(*ptr, fout);
6067 tgl 2504 GBC 69 : fputc('}', fout);
2505 : }
6067 tgl 2506 CBC 15 : fputs(" \\\\\n", fout);
6067 tgl 2507 GBC 15 : fputs("\\hline\n", fout);
2508 : }
8557 bruce 2509 ECB : }
2510 :
2511 : /* print cells */
5445 alvherre 2512 CBC 165 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2513 : {
4787 heikki.linnakangas 2514 147 : latex_escaped_print(*ptr, fout);
8557 bruce 2515 ECB :
5445 alvherre 2516 CBC 147 : if ((i + 1) % cont->ncolumns == 0)
2517 : {
8557 bruce 2518 GIC 33 : fputs(" \\\\\n", fout);
3734 2519 33 : if (opt_border == 3)
3734 bruce 2520 CBC 6 : fputs("\\hline\n", fout);
6143 tgl 2521 GIC 33 : if (cancel_pressed)
6143 tgl 2522 LBC 0 : break;
6143 tgl 2523 ECB : }
8557 bruce 2524 : else
8557 bruce 2525 GIC 114 : fputs(" & ", fout);
8557 bruce 2526 ECB : }
2527 :
5445 alvherre 2528 CBC 18 : if (cont->opt->stop_table)
2529 : {
3995 rhaas 2530 18 : printTableFooter *footers = footers_with_default(cont);
3995 rhaas 2531 ECB :
3733 bruce 2532 GIC 18 : if (opt_border == 2)
6067 tgl 2533 CBC 3 : fputs("\\hline\n", fout);
2534 :
2535 18 : fputs("\\end{tabular}\n\n\\noindent ", fout);
8557 bruce 2536 ECB :
2537 : /* print footers */
3995 rhaas 2538 GIC 18 : if (footers && !opt_tuples_only && !cancel_pressed)
8557 bruce 2539 ECB : {
2540 : printTableFooter *f;
5445 alvherre 2541 :
3995 rhaas 2542 GIC 30 : for (f = footers; f; f = f->next)
6067 tgl 2543 ECB : {
5445 alvherre 2544 CBC 15 : latex_escaped_print(f->data, fout);
6067 tgl 2545 15 : fputs(" \\\\\n", fout);
6067 tgl 2546 ECB : }
8557 bruce 2547 : }
2548 :
6067 tgl 2549 CBC 18 : fputc('\n', fout);
6067 tgl 2550 ECB : }
2551 : }
2552 :
2553 :
2554 : /*************************/
1595 2555 : /* LaTeX longtable */
2556 : /*************************/
2557 :
2558 :
3734 bruce 2559 : static void
3733 bruce 2560 GIC 21 : print_latex_longtable_text(const printTableContent *cont, FILE *fout)
3734 bruce 2561 ECB : {
3734 bruce 2562 CBC 21 : bool opt_tuples_only = cont->opt->tuples_only;
2563 21 : unsigned short opt_border = cont->opt->border;
3734 bruce 2564 ECB : unsigned int i;
3734 bruce 2565 GBC 21 : const char *opt_table_attr = cont->opt->tableAttr;
3734 bruce 2566 GIC 21 : const char *next_opt_table_attr_char = opt_table_attr;
2567 21 : const char *last_opt_table_attr_char = NULL;
2118 tgl 2568 ECB : const char *const *ptr;
2569 :
3734 bruce 2570 GIC 21 : if (cancel_pressed)
3734 bruce 2571 LBC 0 : return;
2572 :
3734 bruce 2573 CBC 21 : if (opt_border > 3)
3734 bruce 2574 UIC 0 : opt_border = 3;
3734 bruce 2575 ECB :
3734 bruce 2576 CBC 21 : if (cont->opt->start_table)
2577 : {
3734 bruce 2578 ECB : /* begin environment and set alignments and borders */
3734 bruce 2579 GIC 21 : fputs("\\begin{longtable}{", fout);
2580 :
3734 bruce 2581 CBC 21 : if (opt_border >= 2)
3734 bruce 2582 GIC 9 : fputs("| ", fout);
2583 :
2584 117 : for (i = 0; i < cont->ncolumns; i++)
3734 bruce 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? */
3734 bruce 2588 CBC 96 : if (*(cont->aligns + i) == 'l' && opt_table_attr)
2589 : {
2590 : #define LONGTABLE_WHITESPACE " \t\n"
2591 :
3734 bruce 2592 ECB : /* advance over whitespace */
3734 bruce 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,
3602 bruce 2603 ECB : LONGTABLE_WHITESPACE);
3734 bruce 2604 GIC 3 : fputs("\\textwidth}", fout);
3734 bruce 2605 ECB : }
2606 : /* use previous value */
3734 bruce 2607 GIC 6 : else if (last_opt_table_attr_char != NULL)
3734 bruce 2608 ECB : {
3734 bruce 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);
3734 bruce 2612 GIC 6 : fputs("\\textwidth}", fout);
3734 bruce 2613 ECB : }
3734 bruce 2614 EUB : else
3734 bruce 2615 UIC 0 : fputc('l', fout);
3734 bruce 2616 ECB : }
3734 bruce 2617 EUB : else
3734 bruce 2618 GIC 87 : fputc(*(cont->aligns + i), fout);
3734 bruce 2619 ECB :
3734 bruce 2620 GIC 96 : if (opt_border != 0 && i < cont->ncolumns - 1)
2621 66 : fputs(" | ", fout);
3734 bruce 2622 ECB : }
2623 :
3734 bruce 2624 CBC 21 : if (opt_border >= 2)
2625 9 : fputs(" |", fout);
2626 :
2627 21 : fputs("}\n", fout);
2628 :
2629 : /* print headers */
3734 bruce 2630 GIC 21 : if (!opt_tuples_only)
3734 bruce 2631 ECB : {
2632 : /* firsthead */
3734 bruce 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++)
3734 bruce 2636 ECB : {
3734 bruce 2637 GIC 81 : if (i != 0)
2638 63 : fputs(" & ", fout);
3734 bruce 2639 CBC 81 : fputs("\\small\\textbf{\\textit{", fout);
3734 bruce 2640 GIC 81 : latex_escaped_print(*ptr, fout);
3734 bruce 2641 CBC 81 : fputs("}}", fout);
3734 bruce 2642 ECB : }
3734 bruce 2643 GIC 18 : fputs(" \\\\\n", fout);
3734 bruce 2644 CBC 18 : fputs("\\midrule\n\\endfirsthead\n", fout);
3734 bruce 2645 ECB :
2646 : /* secondary heads */
3734 bruce 2647 CBC 18 : if (opt_border >= 2)
3734 bruce 2648 GIC 9 : fputs("\\toprule\n", fout);
2649 99 : for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
3734 bruce 2650 ECB : {
3734 bruce 2651 GIC 81 : if (i != 0)
3734 bruce 2652 CBC 63 : fputs(" & ", fout);
2653 81 : fputs("\\small\\textbf{\\textit{", fout);
3734 bruce 2654 GIC 81 : latex_escaped_print(*ptr, fout);
3734 bruce 2655 CBC 81 : fputs("}}", fout);
2656 : }
3734 bruce 2657 GIC 18 : fputs(" \\\\\n", fout);
3734 bruce 2658 EUB : /* If the line under the row already appeared, don't do another */
3734 bruce 2659 GIC 18 : if (opt_border != 3)
2660 12 : fputs("\\midrule\n", fout);
3734 bruce 2661 CBC 18 : fputs("\\endhead\n", fout);
2662 :
3734 bruce 2663 ECB : /* table name, caption? */
3734 bruce 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)
3734 bruce 2668 LBC 0 : fputs("\\bottomrule\n", fout);
3734 bruce 2669 GIC 3 : fputs("\\caption[", fout);
3734 bruce 2670 CBC 3 : latex_escaped_print(cont->title, fout);
3734 bruce 2671 GIC 3 : fputs(" (Continued)]{", fout);
2672 3 : latex_escaped_print(cont->title, fout);
3734 bruce 2673 CBC 3 : fputs("}\n\\endfoot\n", fout);
3734 bruce 2674 GIC 3 : if (opt_border == 2)
3734 bruce 2675 UIC 0 : fputs("\\bottomrule\n", fout);
3734 bruce 2676 CBC 3 : fputs("\\caption[", fout);
2677 3 : latex_escaped_print(cont->title, fout);
2678 3 : fputs("]{", fout);
3734 bruce 2679 GIC 3 : latex_escaped_print(cont->title, fout);
3734 bruce 2680 CBC 3 : fputs("}\n\\endlastfoot\n", fout);
3734 bruce 2681 ECB : }
2682 : /* output bottom table line? */
3734 bruce 2683 CBC 15 : else if (opt_border >= 2)
3734 bruce 2684 ECB : {
3734 bruce 2685 GIC 9 : fputs("\\bottomrule\n\\endfoot\n", fout);
3734 bruce 2686 CBC 9 : fputs("\\bottomrule\n\\endlastfoot\n", fout);
3734 bruce 2687 ECB : }
2688 : }
2689 : }
2690 :
2691 : /* print cells */
3734 bruce 2692 CBC 192 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2693 : {
3734 bruce 2694 ECB : /* Add a line under each row? */
3734 bruce 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);
3734 bruce 2699 GIC 171 : fputc('}', fout);
3734 bruce 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 : }
3734 bruce 2706 GIC 171 : if (cancel_pressed)
3734 bruce 2707 LBC 0 : break;
2708 : }
2709 :
3734 bruce 2710 CBC 21 : if (cont->opt->stop_table)
3734 bruce 2711 GBC 21 : fputs("\\end{longtable}\n", fout);
3734 bruce 2712 ECB : }
2713 :
2714 :
8557 2715 : static void
5445 alvherre 2716 CBC 39 : print_latex_vertical(const printTableContent *cont, FILE *fout)
8557 bruce 2717 ECB : {
5445 alvherre 2718 GBC 39 : bool opt_tuples_only = cont->opt->tuples_only;
5445 alvherre 2719 CBC 39 : unsigned short opt_border = cont->opt->border;
2720 39 : unsigned long record = cont->opt->prior_records + 1;
8557 bruce 2721 ECB : unsigned int i;
2118 tgl 2722 : const char *const *ptr;
8557 bruce 2723 :
6143 tgl 2724 GIC 39 : if (cancel_pressed)
6143 tgl 2725 UIC 0 : return;
6143 tgl 2726 ECB :
6067 tgl 2727 GIC 39 : if (opt_border > 2)
6067 tgl 2728 CBC 9 : opt_border = 2;
8557 bruce 2729 ECB :
5445 alvherre 2730 GIC 39 : if (cont->opt->start_table)
2731 : {
2732 : /* print title */
2733 39 : if (!opt_tuples_only && cont->title)
2734 : {
6067 tgl 2735 CBC 6 : fputs("\\begin{center}\n", fout);
5445 alvherre 2736 GIC 6 : latex_escaped_print(cont->title, fout);
6067 tgl 2737 6 : fputs("\n\\end{center}\n\n", fout);
6067 tgl 2738 ECB : }
2739 :
2740 : /* begin environment and set alignments and borders */
6067 tgl 2741 CBC 39 : fputs("\\begin{tabular}{", fout);
2742 39 : if (opt_border == 0)
2743 6 : fputs("cl", fout);
6067 tgl 2744 GIC 33 : else if (opt_border == 1)
6067 tgl 2745 CBC 18 : fputs("c|l", fout);
2746 15 : else if (opt_border == 2)
2747 15 : fputs("|c|l|", fout);
6067 tgl 2748 GIC 39 : fputs("}\n", fout);
6067 tgl 2749 ECB : }
8557 bruce 2750 EUB :
2751 : /* print records */
5445 alvherre 2752 GIC 357 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
8557 bruce 2753 ECB : {
2754 : /* new record */
5445 alvherre 2755 GIC 318 : if (i % cont->ncolumns == 0)
2756 : {
6143 tgl 2757 72 : if (cancel_pressed)
6143 tgl 2758 UIC 0 : break;
6478 bruce 2759 CBC 72 : if (!opt_tuples_only)
2760 : {
8557 2761 60 : if (opt_border == 2)
6820 bruce 2762 ECB : {
8557 bruce 2763 CBC 30 : fputs("\\hline\n", fout);
6067 tgl 2764 GIC 30 : fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
2765 : }
2766 : else
6067 tgl 2767 CBC 30 : fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
8557 bruce 2768 EUB : }
8557 bruce 2769 GIC 72 : if (opt_border >= 1)
8557 bruce 2770 CBC 60 : fputs("\\hline\n", fout);
8557 bruce 2771 ECB : }
2772 :
5445 alvherre 2773 CBC 318 : latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
8557 bruce 2774 GIC 318 : fputs(" & ", fout);
2775 318 : latex_escaped_print(*ptr, fout);
8557 bruce 2776 CBC 318 : fputs(" \\\\\n", fout);
2777 : }
8557 bruce 2778 ECB :
5445 alvherre 2779 CBC 39 : if (cont->opt->stop_table)
6067 tgl 2780 ECB : {
6067 tgl 2781 GIC 39 : if (opt_border == 2)
2782 15 : fputs("\\hline\n", fout);
2783 :
6067 tgl 2784 CBC 39 : fputs("\\end{tabular}\n\n\\noindent ", fout);
8557 bruce 2785 ECB :
6067 tgl 2786 : /* print footers */
5445 alvherre 2787 CBC 39 : if (cont->footers && !opt_tuples_only && !cancel_pressed)
8557 bruce 2788 ECB : {
5445 alvherre 2789 : printTableFooter *f;
2790 :
5445 alvherre 2791 CBC 12 : for (f = cont->footers; f; f = f->next)
2792 : {
4787 heikki.linnakangas 2793 GIC 6 : latex_escaped_print(f->data, fout);
6067 tgl 2794 6 : fputs(" \\\\\n", fout);
6478 bruce 2795 ECB : }
2796 : }
2797 :
6067 tgl 2798 CBC 39 : fputc('\n', fout);
2799 : }
8557 bruce 2800 ECB : }
8557 bruce 2801 EUB :
8557 bruce 2802 ECB :
2803 : /*************************/
1595 tgl 2804 : /* Troff -ms */
2805 : /*************************/
6513 bruce 2806 :
2807 :
2808 : static void
6513 bruce 2809 GIC 447 : troff_ms_escaped_print(const char *in, FILE *fout)
6513 bruce 2810 ECB : {
2811 : const char *p;
2812 :
6513 bruce 2813 CBC 3594 : for (p = in; *p; p++)
6513 bruce 2814 GIC 3147 : switch (*p)
2815 : {
6513 bruce 2816 CBC 63 : case '\\':
2817 63 : fputs("\\(rs", fout);
2818 63 : break;
2819 3084 : default:
6513 bruce 2820 GIC 3084 : fputc(*p, fout);
2821 : }
6513 bruce 2822 CBC 447 : }
2823 :
6513 bruce 2824 ECB :
2825 : static void
5445 alvherre 2826 GIC 15 : print_troff_ms_text(const printTableContent *cont, FILE *fout)
6513 bruce 2827 ECB : {
5445 alvherre 2828 GIC 15 : bool opt_tuples_only = cont->opt->tuples_only;
2829 15 : unsigned short opt_border = cont->opt->border;
6513 bruce 2830 ECB : unsigned int i;
2831 : const char *const *ptr;
2832 :
6143 tgl 2833 GIC 15 : if (cancel_pressed)
6143 tgl 2834 LBC 0 : return;
2835 :
6067 tgl 2836 CBC 15 : if (opt_border > 2)
6067 tgl 2837 LBC 0 : opt_border = 2;
2838 :
5445 alvherre 2839 GIC 15 : if (cont->opt->start_table)
2840 : {
6067 tgl 2841 ECB : /* print title */
5445 alvherre 2842 GIC 15 : if (!opt_tuples_only && cont->title)
2843 : {
6067 tgl 2844 3 : fputs(".LP\n.DS C\n", fout);
5445 alvherre 2845 3 : troff_ms_escaped_print(cont->title, fout);
6067 tgl 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)
6067 tgl 2852 CBC 3 : fputs("center box;\n", fout);
2853 : else
6067 tgl 2854 GIC 12 : fputs("center;\n", fout);
2855 :
5445 alvherre 2856 CBC 87 : for (i = 0; i < cont->ncolumns; i++)
6067 tgl 2857 ECB : {
5445 alvherre 2858 GIC 72 : fputc(*(cont->aligns + i), fout);
5445 alvherre 2859 CBC 72 : if (opt_border > 0 && i < cont->ncolumns - 1)
6067 tgl 2860 48 : fputs(" | ", fout);
6067 tgl 2861 ECB : }
6067 tgl 2862 CBC 15 : fputs(".\n", fout);
6067 tgl 2863 ECB :
2864 : /* print headers */
6478 bruce 2865 CBC 15 : if (!opt_tuples_only)
2866 : {
5445 alvherre 2867 GIC 69 : for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
2868 : {
6067 tgl 2869 CBC 57 : if (i != 0)
6067 tgl 2870 GIC 45 : fputc('\t', fout);
6067 tgl 2871 CBC 57 : fputs("\\fI", fout);
2872 57 : troff_ms_escaped_print(*ptr, fout);
6067 tgl 2873 GIC 57 : fputs("\\fP", fout);
2874 : }
2875 12 : fputs("\n_\n", fout);
6513 bruce 2876 ECB : }
6513 bruce 2877 EUB : }
2878 :
6513 bruce 2879 ECB : /* print cells */
5445 alvherre 2880 GBC 138 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2881 : {
4787 heikki.linnakangas 2882 CBC 123 : troff_ms_escaped_print(*ptr, fout);
2883 :
5445 alvherre 2884 GIC 123 : if ((i + 1) % cont->ncolumns == 0)
6143 tgl 2885 ECB : {
6513 bruce 2886 GIC 27 : fputc('\n', fout);
6143 tgl 2887 CBC 27 : if (cancel_pressed)
6143 tgl 2888 LBC 0 : break;
6143 tgl 2889 ECB : }
2890 : else
6513 bruce 2891 GIC 96 : fputc('\t', fout);
2892 : }
6513 bruce 2893 ECB :
5445 alvherre 2894 CBC 15 : if (cont->opt->stop_table)
6067 tgl 2895 ECB : {
3995 rhaas 2896 GIC 15 : printTableFooter *footers = footers_with_default(cont);
3995 rhaas 2897 ECB :
6067 tgl 2898 GIC 15 : fputs(".TE\n.DS L\n", fout);
6513 bruce 2899 ECB :
2900 : /* print footers */
3995 rhaas 2901 CBC 15 : if (footers && !opt_tuples_only && !cancel_pressed)
5445 alvherre 2902 ECB : {
2903 : printTableFooter *f;
2904 :
3995 rhaas 2905 CBC 24 : for (f = footers; f; f = f->next)
2906 : {
5445 alvherre 2907 GIC 12 : troff_ms_escaped_print(f->data, fout);
6067 tgl 2908 CBC 12 : fputc('\n', fout);
2909 : }
5445 alvherre 2910 ECB : }
2911 :
6067 tgl 2912 CBC 15 : fputs(".DE\n", fout);
6067 tgl 2913 ECB : }
6513 bruce 2914 : }
2915 :
2916 :
2917 : static void
5445 alvherre 2918 CBC 15 : print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
2919 : {
5445 alvherre 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;
6513 bruce 2923 ECB : unsigned int i;
2924 : const char *const *ptr;
6385 bruce 2925 CBC 15 : unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
2926 :
6143 tgl 2927 15 : if (cancel_pressed)
6143 tgl 2928 UIC 0 : return;
6143 tgl 2929 ECB :
6067 tgl 2930 CBC 15 : if (opt_border > 2)
6067 tgl 2931 UBC 0 : opt_border = 2;
2932 :
5445 alvherre 2933 GIC 15 : if (cont->opt->start_table)
6067 tgl 2934 ECB : {
2935 : /* print title */
5445 alvherre 2936 GIC 15 : if (!opt_tuples_only && cont->title)
6067 tgl 2937 ECB : {
6067 tgl 2938 GIC 3 : fputs(".LP\n.DS C\n", fout);
5445 alvherre 2939 CBC 3 : troff_ms_escaped_print(cont->title, fout);
6067 tgl 2940 GIC 3 : fputs("\n.DE\n", fout);
6067 tgl 2941 ECB : }
2942 :
2943 : /* begin environment and set alignments and borders */
6067 tgl 2944 CBC 15 : fputs(".LP\n.TS\n", fout);
6067 tgl 2945 GIC 15 : if (opt_border == 2)
2946 3 : fputs("center box;\n", fout);
2947 : else
6067 tgl 2948 CBC 12 : fputs("center;\n", fout);
2949 :
6067 tgl 2950 ECB : /* basic format */
6067 tgl 2951 CBC 15 : if (opt_tuples_only)
6067 tgl 2952 GIC 3 : fputs("c l;\n", fout);
2953 : }
2954 : else
6067 tgl 2955 LBC 0 : current_format = 2; /* assume tuples printed already */
2956 :
2957 : /* print records */
5445 alvherre 2958 GIC 138 : for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
2959 : {
2960 : /* new record */
5445 alvherre 2961 CBC 123 : if (i % cont->ncolumns == 0)
2962 : {
6143 tgl 2963 27 : if (cancel_pressed)
6143 tgl 2964 LBC 0 : break;
6478 bruce 2965 CBC 27 : if (!opt_tuples_only)
2966 : {
6513 bruce 2967 GIC 21 : if (current_format != 1)
6513 bruce 2968 ECB : {
6067 tgl 2969 GIC 21 : if (opt_border == 2 && record > 1)
6513 bruce 2970 CBC 3 : fputs("_\n", fout);
6513 bruce 2971 GBC 21 : if (current_format != 0)
6513 bruce 2972 GIC 9 : fputs(".T&\n", fout);
6513 bruce 2973 CBC 21 : fputs("c s.\n", fout);
6513 bruce 2974 GBC 21 : current_format = 1;
2975 : }
6067 tgl 2976 CBC 21 : fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
2977 : }
6513 bruce 2978 GIC 27 : if (opt_border >= 1)
6513 bruce 2979 CBC 21 : fputs("_\n", fout);
2980 : }
6513 bruce 2981 ECB :
6478 bruce 2982 CBC 123 : if (!opt_tuples_only)
6513 bruce 2983 ECB : {
6513 bruce 2984 GIC 93 : if (current_format != 2)
2985 : {
2986 21 : if (current_format != 0)
6513 bruce 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);
6513 bruce 2992 GIC 21 : current_format = 2;
2993 : }
6513 bruce 2994 ECB : }
2995 :
5445 alvherre 2996 GIC 123 : troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
6513 bruce 2997 123 : fputc('\t', fout);
4787 heikki.linnakangas 2998 GBC 123 : troff_ms_escaped_print(*ptr, fout);
2999 :
6513 bruce 3000 GIC 123 : fputc('\n', fout);
6513 bruce 3001 ECB : }
3002 :
5445 alvherre 3003 GIC 15 : if (cont->opt->stop_table)
6067 tgl 3004 ECB : {
6067 tgl 3005 GIC 15 : fputs(".TE\n.DS L\n", fout);
6513 bruce 3006 ECB :
6067 tgl 3007 EUB : /* print footers */
5445 alvherre 3008 CBC 15 : if (cont->footers && !opt_tuples_only && !cancel_pressed)
3009 : {
5445 alvherre 3010 ECB : printTableFooter *f;
3011 :
5445 alvherre 3012 CBC 6 : for (f = cont->footers; f; f = f->next)
6067 tgl 3013 ECB : {
5445 alvherre 3014 CBC 3 : troff_ms_escaped_print(f->data, fout);
6067 tgl 3015 3 : fputc('\n', fout);
6067 tgl 3016 ECB : }
5445 alvherre 3017 : }
3018 :
6067 tgl 3019 CBC 15 : fputs(".DE\n", fout);
3020 : }
6513 bruce 3021 ECB : }
3022 :
3023 :
3024 : /********************************/
2684 tgl 3025 : /* Public functions */
3026 : /********************************/
8557 bruce 3027 :
3028 :
2684 tgl 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
2684 tgl 3038 UIC 0 : disable_sigpipe_trap(void)
2684 tgl 3039 ECB : {
3040 : #ifndef WIN32
2684 tgl 3041 LBC 0 : pqsignal(SIGPIPE, SIG_IGN);
3042 : #endif
3043 0 : }
3044 :
3045 : /*
2684 tgl 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
2684 tgl 3061 GIC 6186 : restore_sigpipe_trap(void)
2684 tgl 3062 ECB : {
3063 : #ifndef WIN32
2684 tgl 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 : /*
7327 peter_e 3081 EUB : * PageOutput
3082 : *
3083 : * Tests if pager is needed and returns appropriate FILE pointer.
2934 andrew 3084 : *
3085 : * If the topt argument is NULL no pager is used.
7327 peter_e 3086 : */
3087 : FILE *
2934 andrew 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
2750 andrew 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 :
7327 peter_e 3099 0 : result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
3100 :
3101 : /* >= accounts for a one-line prompt */
2934 andrew 3102 0 : if (result == -1
3103 0 : || (lines >= screen_size.ws_row && lines >= min_lines)
2934 andrew 3104 LBC 0 : || pager > 1)
3105 : #endif
3106 : {
2684 tgl 3107 ECB : const char *pagerprog;
3108 : FILE *pagerpipe;
3109 :
2042 tgl 3110 UIC 0 : pagerprog = getenv("PSQL_PAGER");
3111 0 : if (!pagerprog)
3112 0 : pagerprog = getenv("PAGER");
7327 peter_e 3113 0 : if (!pagerprog)
3114 0 : pagerprog = DEFAULT_PAGER;
3115 : else
3116 : {
2314 tgl 3117 ECB : /* if PAGER is empty or all-white-space, don't use pager */
2314 tgl 3118 UIC 0 : if (strspn(pagerprog, " \t\r\n") == strlen(pagerprog))
2314 tgl 3119 LBC 0 : return stdout;
2314 tgl 3120 ECB : }
223 tgl 3121 UNC 0 : fflush(NULL);
2684 tgl 3122 UIC 0 : disable_sigpipe_trap();
6067 3123 0 : pagerpipe = popen(pagerprog, "w");
3124 0 : if (pagerpipe)
3125 0 : return pagerpipe;
3126 : /* if popen fails, silently proceed without pager */
2314 3127 0 : restore_sigpipe_trap();
3128 : }
3129 : }
3130 :
7327 peter_e 3131 GIC 58682 : return stdout;
7327 peter_e 3132 ECB : }
3133 :
3134 : /*
6067 tgl 3135 : * ClosePager
3136 : *
3137 : * Close previously opened pager pipe, if any
6067 tgl 3138 EUB : */
3139 : void
6067 tgl 3140 GIC 359 : ClosePager(FILE *pagerpipe)
3141 : {
3142 359 : if (pagerpipe && pagerpipe != stdout)
6067 tgl 3143 EUB : {
3144 : /*
3145 : * If printing was canceled midstream, warn about it.
3146 : *
6031 bruce 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 : */
6067 tgl 3152 UIC 0 : if (cancel_pressed)
3153 0 : fprintf(pagerpipe, _("Interrupted\n"));
6067 tgl 3154 EUB :
6067 tgl 3155 UBC 0 : pclose(pagerpipe);
2684 3156 0 : restore_sigpipe_trap();
6067 tgl 3157 EUB : }
6067 tgl 3158 GBC 359 : }
3159 :
3160 : /*
3161 : * Initialise a table contents struct.
5444 alvherre 3162 EUB : * Must be called before any other printTable method is used.
5445 3163 : *
3164 : * The title is not duplicated; the caller must ensure that the buffer
5444 3165 : * is available for the lifetime of the printTableContent struct.
5445 3166 : *
3167 : * If you call this, you must call printTableCleanup once you're done with the
3168 : * table.
3169 : */
3170 : void
5445 alvherre 3171 GBC 58700 : printTableInit(printTableContent *const content, const printTableOpt *opt,
3172 : const char *title, const int ncolumns, const int nrows)
3173 : {
5445 alvherre 3174 GIC 58700 : content->opt = opt;
5445 alvherre 3175 CBC 58700 : content->title = title;
5445 alvherre 3176 GIC 58700 : content->ncolumns = ncolumns;
3177 58700 : content->nrows = nrows;
3178 :
3841 tgl 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 :
4787 heikki.linnakangas 3183 58700 : content->cellmustfree = NULL;
5445 alvherre 3184 CBC 58700 : content->footers = NULL;
3185 :
3841 tgl 3186 58700 : content->aligns = pg_malloc0((ncolumns + 1) * sizeof(*content->align));
3187 :
5445 alvherre 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;
4787 heikki.linnakangas 3192 58700 : content->cellsadded = 0;
5445 alvherre 3193 58700 : }
3194 :
3195 : /*
5445 alvherre 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.
5445 alvherre 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
4041 peter_e 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 :
5445 alvherre 3215 CBC 111524 : if (content->header >= content->headers + content->ncolumns)
3216 : {
5445 alvherre 3217 UIC 0 : fprintf(stderr, _("Cannot add header to table content: "
5445 alvherre 3218 ECB : "column count of %d exceeded.\n"),
3219 : content->ncolumns);
5445 alvherre 3220 LBC 0 : exit(EXIT_FAILURE);
5445 alvherre 3221 ECB : }
3222 :
5445 alvherre 3223 CBC 223048 : *content->header = (char *) mbvalidate((unsigned char *) header,
5445 alvherre 3224 GIC 111524 : content->opt->encoding);
5445 alvherre 3225 ECB : #ifdef ENABLE_NLS
5445 alvherre 3226 GIC 111524 : if (translate)
5445 alvherre 3227 CBC 15032 : *content->header = _(*content->header);
5445 alvherre 3228 ECB : #endif
5445 alvherre 3229 GIC 111524 : content->header++;
5445 alvherre 3230 ECB :
5445 alvherre 3231 GIC 111524 : *content->align = align;
5445 alvherre 3232 CBC 111524 : content->align++;
3233 111524 : }
5445 alvherre 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
4041 peter_e 3248 GIC 4493548 : printTableAddCell(printTableContent *const content, char *cell,
3249 : const bool translate, const bool mustfree)
3250 : {
3251 : #ifndef ENABLE_NLS
5050 bruce 3252 ECB : (void) translate; /* unused parameter */
3253 : #endif
3254 :
4787 heikki.linnakangas 3255 GIC 4493548 : if (content->cellsadded >= content->ncolumns * content->nrows)
3256 : {
5445 alvherre 3257 UIC 0 : fprintf(stderr, _("Cannot add cell to table content: "
3258 : "total cell count of %d exceeded.\n"),
5445 alvherre 3259 LBC 0 : content->ncolumns * content->nrows);
5445 alvherre 3260 UIC 0 : exit(EXIT_FAILURE);
5445 alvherre 3261 EUB : }
3262 :
5445 alvherre 3263 GIC 8987096 : *content->cell = (char *) mbvalidate((unsigned char *) cell,
5445 alvherre 3264 GBC 4493548 : content->opt->encoding);
3265 :
3266 : #ifdef ENABLE_NLS
5445 alvherre 3267 CBC 4493548 : if (translate)
4787 heikki.linnakangas 3268 763 : *content->cell = _(*content->cell);
3269 : #endif
4787 heikki.linnakangas 3270 ECB :
4787 heikki.linnakangas 3271 CBC 4493548 : if (mustfree)
3272 : {
3273 128 : if (content->cellmustfree == NULL)
3841 tgl 3274 GIC 74 : content->cellmustfree =
3841 tgl 3275 CBC 74 : pg_malloc0((content->ncolumns * content->nrows + 1) * sizeof(bool));
4787 heikki.linnakangas 3276 ECB :
4787 heikki.linnakangas 3277 CBC 128 : content->cellmustfree[content->cellsadded] = true;
3278 : }
5445 alvherre 3279 GIC 4493548 : content->cell++;
4787 heikki.linnakangas 3280 4493548 : content->cellsadded++;
5445 alvherre 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
5445 alvherre 3292 ECB : * be made of up individually translated components, rather than being
3293 : * translated as a whole.
3294 : */
3295 : void
5445 alvherre 3296 GIC 4306 : printTableAddFooter(printTableContent *const content, const char *footer)
3297 : {
3298 : printTableFooter *f;
5445 alvherre 3299 ECB :
3841 tgl 3300 GIC 4306 : f = pg_malloc0(sizeof(*f));
5445 alvherre 3301 GBC 4306 : f->data = pg_strdup(footer);
3302 :
3303 4306 : if (content->footers == NULL)
3304 1511 : content->footers = f;
3305 : else
5445 alvherre 3306 GIC 2795 : content->footer->next = f;
5445 alvherre 3307 ECB :
5445 alvherre 3308 CBC 4306 : content->footer = f;
5445 alvherre 3309 GIC 4306 : }
3310 :
5445 alvherre 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
5445 alvherre 3321 CBC 15 : printTableSetFooter(printTableContent *const content, const char *footer)
3322 : {
3323 15 : if (content->footers != NULL)
5445 alvherre 3324 ECB : {
5445 alvherre 3325 CBC 15 : free(content->footer->data);
5445 alvherre 3326 GIC 15 : content->footer->data = pg_strdup(footer);
3327 : }
3328 : else
5445 alvherre 3329 UIC 0 : printTableAddFooter(content, footer);
5445 alvherre 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
5436 magnus 3339 58700 : printTableCleanup(printTableContent *const content)
5445 alvherre 3340 ECB : {
4787 heikki.linnakangas 3341 GIC 58700 : if (content->cellmustfree)
3342 : {
3343 : int i;
4660 bruce 3344 ECB :
4787 heikki.linnakangas 3345 CBC 1208 : for (i = 0; i < content->nrows * content->ncolumns; i++)
3346 : {
3347 1134 : if (content->cellmustfree[i])
1531 peter 3348 128 : free(unconstify(char *, content->cells[i]));
3349 : }
4787 heikki.linnakangas 3350 74 : free(content->cellmustfree);
4787 heikki.linnakangas 3351 GIC 74 : content->cellmustfree = NULL;
4787 heikki.linnakangas 3352 ECB : }
5445 alvherre 3353 CBC 58700 : free(content->headers);
5445 alvherre 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;
5445 alvherre 3365 ECB :
5445 alvherre 3366 GIC 58700 : if (content->footers)
5445 alvherre 3367 ECB : {
5445 alvherre 3368 GIC 5817 : for (content->footer = content->footers; content->footer;)
5445 alvherre 3369 ECB : {
3370 : printTableFooter *f;
3371 :
5445 alvherre 3372 GIC 4306 : f = content->footer;
5445 alvherre 3373 GBC 4306 : content->footer = f->next;
5445 alvherre 3374 CBC 4306 : free(f->data);
5445 alvherre 3375 GIC 4306 : free(f);
3376 : }
3377 : }
3378 58700 : content->footers = NULL;
3379 58700 : content->footer = NULL;
3380 58700 : }
3381 :
3382 : /*
5441 bruce 3383 ECB : * IsPagerNeeded
3384 : *
3385 : * Setup pager if required
3386 : */
3387 : static void
2685 tgl 3388 GIC 58323 : IsPagerNeeded(const printTableContent *cont, int extra_lines, bool expanded,
2685 tgl 3389 ECB : FILE **fout, bool *is_pager)
3390 : {
5441 bruce 3391 CBC 58323 : if (*fout == stdout)
8557 bruce 3392 ECB : {
3393 : int lines;
5445 alvherre 3394 :
4166 peter_e 3395 CBC 58323 : if (expanded)
5445 alvherre 3396 GIC 368 : lines = (cont->ncolumns + 1) * cont->nrows;
8557 bruce 3397 ECB : else
5445 alvherre 3398 CBC 57955 : lines = cont->nrows + 1;
5445 alvherre 3399 ECB :
5445 alvherre 3400 GIC 58323 : if (!cont->opt->tuples_only)
5445 alvherre 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 : */
5445 alvherre 3408 CBC 59038 : for (f = cont->footers; f; f = f->next)
7525 bruce 3409 GIC 4294 : lines++;
5445 alvherre 3410 ECB : }
3411 :
2934 andrew 3412 CBC 58323 : *fout = PageOutput(lines + extra_lines, cont->opt);
5441 bruce 3413 GIC 58323 : *is_pager = (*fout != stdout);
3414 : }
3415 : else
5441 bruce 3416 LBC 0 : *is_pager = false;
5441 bruce 3417 CBC 58323 : }
5441 bruce 3418 ECB :
3419 : /*
3420 : * Use this to print any table in the supported formats.
3421 : *
2685 tgl 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
2685 tgl 3428 GIC 58697 : printTable(const printTableContent *cont,
3429 : FILE *fout, bool is_pager, FILE *flog)
3430 : {
3431 58697 : bool is_local_pager = false;
5050 bruce 3432 ECB :
5441 bruce 3433 GIC 58697 : if (cancel_pressed)
5441 bruce 3434 UIC 0 : return;
5441 bruce 3435 ECB :
5441 bruce 3436 GIC 58697 : if (cont->opt->format == PRINT_NOTHING)
5441 bruce 3437 UIC 0 : return;
3438 :
2685 tgl 3439 ECB : /* print_aligned_*() handle the pager themselves */
2685 tgl 3440 CBC 58697 : if (!is_pager &&
2685 tgl 3441 GIC 58637 : cont->opt->format != PRINT_ALIGNED &&
4166 peter_e 3442 CBC 3960 : cont->opt->format != PRINT_WRAPPED)
3443 : {
3444 3840 : IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager);
2685 tgl 3445 GIC 3840 : is_local_pager = is_pager;
3446 : }
3447 :
3448 : /* clear any pre-existing error indication on the output stream */
741 alvherre 3449 58697 : clearerr(fout);
3450 :
3451 : /* print the stuff */
8557 bruce 3452 ECB :
6508 bruce 3453 CBC 58697 : if (flog)
2685 tgl 3454 UIC 0 : print_aligned_text(cont, flog, false);
3455 :
5445 alvherre 3456 CBC 58697 : switch (cont->opt->format)
8557 bruce 3457 ECB : {
8557 bruce 3458 GIC 3639 : case PRINT_UNALIGNED:
4166 peter_e 3459 3639 : if (cont->opt->expanded == 1)
5441 bruce 3460 GBC 51 : print_unaligned_vertical(cont, fout);
8557 bruce 3461 ECB : else
5441 bruce 3462 GIC 3588 : print_unaligned_text(cont, fout);
8557 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 : */
2685 tgl 3472 CBC 54857 : if (cont->opt->expanded == 1 ||
2685 tgl 3473 GIC 54613 : (cont->opt->expanded == 2 && is_pager))
3474 244 : print_aligned_vertical(cont, fout, is_pager);
8557 bruce 3475 ECB : else
2685 tgl 3476 GIC 54613 : print_aligned_text(cont, fout, is_pager);
8557 bruce 3477 CBC 54857 : break;
1595 tgl 3478 GBC 33 : case PRINT_CSV:
1595 tgl 3479 GIC 33 : if (cont->opt->expanded == 1)
1595 tgl 3480 CBC 9 : print_csv_vertical(cont, fout);
1595 tgl 3481 EUB : else
1595 tgl 3482 GIC 24 : print_csv_text(cont, fout);
3483 33 : break;
8557 bruce 3484 CBC 30 : case PRINT_HTML:
4166 peter_e 3485 30 : if (cont->opt->expanded == 1)
5441 bruce 3486 15 : print_html_vertical(cont, fout);
3487 : else
3488 15 : print_html_text(cont, fout);
8557 3489 30 : break;
2931 bruce 3490 GIC 30 : case PRINT_ASCIIDOC:
3491 30 : if (cont->opt->expanded == 1)
3492 15 : print_asciidoc_vertical(cont, fout);
2931 bruce 3493 ECB : else
2931 bruce 3494 GIC 15 : print_asciidoc_text(cont, fout);
3495 30 : break;
8557 3496 36 : case PRINT_LATEX:
4166 peter_e 3497 CBC 36 : if (cont->opt->expanded == 1)
5441 bruce 3498 GBC 18 : print_latex_vertical(cont, fout);
3499 : else
5441 bruce 3500 CBC 18 : print_latex_text(cont, fout);
8557 bruce 3501 GIC 36 : break;
3734 bruce 3502 CBC 42 : case PRINT_LATEX_LONGTABLE:
3503 42 : if (cont->opt->expanded == 1)
3504 21 : print_latex_vertical(cont, fout);
3505 : else
3733 3506 21 : print_latex_longtable_text(cont, fout);
3734 3507 42 : break;
6513 3508 30 : case PRINT_TROFF_MS:
4166 peter_e 3509 GIC 30 : if (cont->opt->expanded == 1)
5441 bruce 3510 15 : print_troff_ms_vertical(cont, fout);
3511 : else
3512 15 : print_troff_ms_text(cont, fout);
6513 3513 30 : break;
8557 bruce 3514 UIC 0 : default:
5111 peter_e 3515 0 : fprintf(stderr, _("invalid output format (internal error): %d"),
5445 alvherre 3516 LBC 0 : cont->opt->format);
6509 neilc 3517 0 : exit(EXIT_FAILURE);
8557 bruce 3518 ECB : }
3519 :
2685 tgl 3520 CBC 58697 : if (is_local_pager)
5441 bruce 3521 LBC 0 : ClosePager(fout);
8557 bruce 3522 ECB : }
3523 :
5445 alvherre 3524 : /*
3525 : * Use this to print query results
3526 : *
2685 tgl 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)
5445 alvherre 3532 : */
8557 bruce 3533 : void
2685 tgl 3534 CBC 56972 : printQuery(const PGresult *result, const printQueryOpt *opt,
2685 tgl 3535 ECB : FILE *fout, bool is_pager, FILE *flog)
8557 bruce 3536 : {
3537 : printTableContent cont;
5597 tgl 3538 : int i,
3539 : r,
3540 : c;
8557 bruce 3541 :
6143 tgl 3542 CBC 56972 : if (cancel_pressed)
6143 tgl 3543 UIC 0 : return;
6143 tgl 3544 ECB :
5445 alvherre 3545 CBC 56972 : printTableInit(&cont, &opt->topt, opt->title,
5445 alvherre 3546 ECB : PQnfields(result), PQntuples(result));
8557 bruce 3547 :
3382 tgl 3548 : /* Assert caller supplied enough translate_columns[] entries */
3382 tgl 3549 GIC 56972 : Assert(opt->translate_columns == NULL ||
3382 tgl 3550 ECB : opt->n_translate_columns >= cont.ncolumns);
3551 :
5445 alvherre 3552 CBC 157969 : for (i = 0; i < cont.ncolumns; i++)
8557 bruce 3553 ECB : {
5445 alvherre 3554 CBC 100997 : printTableAddHeader(&cont, PQfname(result, i),
2557 alvherre 3555 GIC 100997 : opt->translate_header,
2557 alvherre 3556 CBC 100997 : column_type_alignment(PQftype(result, i)));
8557 bruce 3557 ECB : }
8557 bruce 3558 EUB :
5445 alvherre 3559 : /* set cells */
5445 alvherre 3560 GBC 2288278 : for (r = 0; r < cont.nrows; r++)
5445 alvherre 3561 EUB : {
5445 alvherre 3562 GIC 6701612 : for (c = 0; c < cont.ncolumns; c++)
3563 : {
5050 bruce 3564 ECB : char *cell;
4787 heikki.linnakangas 3565 GBC 4470306 : bool mustfree = false;
3566 : bool translate;
3567 :
5445 alvherre 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);
4787 heikki.linnakangas 3573 4441702 : if (cont.aligns[c] == 'r' && opt->topt.numericLocale)
3574 : {
3575 48 : cell = format_numeric_locale(cell);
3576 48 : mustfree = true;
3577 : }
4787 heikki.linnakangas 3578 ECB : }
3579 :
5382 bruce 3580 GIC 4470306 : translate = (opt->translate_columns && opt->translate_columns[c]);
4787 heikki.linnakangas 3581 4470306 : printTableAddCell(&cont, cell, translate, mustfree);
3582 : }
3583 : }
3584 :
3585 : /* set footers */
5445 alvherre 3586 CBC 56972 : if (opt->footers)
5445 alvherre 3587 EUB : {
3588 : char **footer;
5445 alvherre 3589 ECB :
5445 alvherre 3590 GIC 153 : for (footer = opt->footers; *footer; footer++)
3591 72 : printTableAddFooter(&cont, *footer);
3592 : }
5445 alvherre 3593 ECB :
2685 tgl 3594 GIC 56972 : printTable(&cont, fout, is_pager, flog);
5445 alvherre 3595 56972 : printTableCleanup(&cont);
8557 bruce 3596 ECB : }
3597 :
2557 alvherre 3598 : char
2557 alvherre 3599 CBC 101081 : column_type_alignment(Oid ftype)
2557 alvherre 3600 ECB : {
3601 : char align;
3602 :
2557 alvherre 3603 GIC 101081 : switch (ftype)
2557 alvherre 3604 ECB : {
2557 alvherre 3605 GIC 40099 : case INT2OID:
2557 alvherre 3606 ECB : case INT4OID:
3607 : case INT8OID:
3608 : case FLOAT4OID:
3609 : case FLOAT8OID:
3610 : case NUMERICOID:
3611 : case OIDOID:
3612 : case XIDOID:
1097 tmunro 3613 : case XID8OID:
3614 : case CIDOID:
3615 : case MONEYOID:
2557 alvherre 3616 CBC 40099 : align = 'r';
3617 40099 : break;
2557 alvherre 3618 GIC 60982 : default:
2557 alvherre 3619 CBC 60982 : align = 'l';
3620 60982 : break;
3621 : }
2557 alvherre 3622 GIC 101081 : return align;
3623 : }
8557 bruce 3624 ECB :
6478 3625 : void
6478 bruce 3626 GIC 6478 : setDecimalLocale(void)
3627 : {
3628 : struct lconv *extlconv;
3629 :
6478 bruce 3630 CBC 6478 : extlconv = localeconv();
3631 :
3632 : /* Don't accept an empty decimal_point string */
6478 bruce 3633 GIC 6478 : if (*extlconv->decimal_point)
5445 alvherre 3634 CBC 6478 : decimal_point = pg_strdup(extlconv->decimal_point);
6478 bruce 3635 ECB : else
6478 bruce 3636 UIC 0 : decimal_point = "."; /* SQL output standard */
3637 :
2753 tgl 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 : */
2753 tgl 3645 GIC 6478 : groupdigits = *extlconv->grouping;
3646 6478 : if (groupdigits <= 0 || groupdigits > 6)
2754 tgl 3647 CBC 19 : groupdigits = 3; /* most common */
3648 :
2753 tgl 3649 ECB : /* Don't accept an empty thousands_sep string, either */
3650 : /* similar code exists in formatting.c */
6478 bruce 3651 GIC 6478 : if (*extlconv->thousands_sep)
5445 alvherre 3652 6459 : thousands_sep = pg_strdup(extlconv->thousands_sep);
3653 : /* Make sure thousands separator doesn't match decimal point symbol. */
5618 bruce 3654 19 : else if (strcmp(decimal_point, ",") != 0)
6478 3655 19 : thousands_sep = ",";
3656 : else
6478 bruce 3657 UIC 0 : thousands_sep = ".";
6478 bruce 3658 GIC 6478 : }
3659 :
4926 tgl 3660 ECB : /* get selected or default line style */
3661 : const printTextFormat *
4926 tgl 3662 CBC 55650 : get_line_style(const printTableOpt *opt)
4926 tgl 3663 ECB : {
4883 3664 : /*
3665 : * Note: this function mainly exists to preserve the convention that a
4790 bruce 3666 : * printTableOpt struct can be initialized to zeroes to get default
3667 : * behavior.
3668 : */
4926 tgl 3669 GIC 55650 : if (opt->line_style != NULL)
4926 tgl 3670 CBC 1407 : return opt->line_style;
3671 : else
4926 tgl 3672 GIC 54243 : return &pg_asciiformat;
3673 : }
4926 tgl 3674 ECB :
3675 : void
3131 sfrost 3676 GIC 6478 : refresh_utf8format(const printTableOpt *opt)
3131 sfrost 3677 ECB : {
2584 tgl 3678 CBC 6478 : printTextFormat *popt = &pg_utf8format;
3679 :
3131 sfrost 3680 EUB : const unicodeStyleBorderFormat *border;
3681 : const unicodeStyleRowFormat *header;
3682 : const unicodeStyleColumnFormat *column;
3683 :
3131 sfrost 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];
3131 sfrost 3689 ECB :
3131 sfrost 3690 CBC 6478 : popt->lrule[PRINT_RULE_TOP].hrule = border->horizontal;
3691 6478 : popt->lrule[PRINT_RULE_TOP].leftvrule = border->down_and_right;
3131 sfrost 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 :
3131 sfrost 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];
3131 sfrost 3697 GIC 6478 : popt->lrule[PRINT_RULE_MIDDLE].midvrule = column->vertical_and_horizontal[opt->unicode_header_linestyle];
3131 sfrost 3698 CBC 6478 : popt->lrule[PRINT_RULE_MIDDLE].rightvrule = header->vertical_and_left[opt->unicode_border_linestyle];
3131 sfrost 3699 ECB :
3131 sfrost 3700 GIC 6478 : popt->lrule[PRINT_RULE_BOTTOM].hrule = border->horizontal;
3131 sfrost 3701 GBC 6478 : popt->lrule[PRINT_RULE_BOTTOM].leftvrule = border->up_and_right;
3131 sfrost 3702 CBC 6478 : popt->lrule[PRINT_RULE_BOTTOM].midvrule = column->up_and_horizontal[opt->unicode_border_linestyle];
3131 sfrost 3703 GIC 6478 : popt->lrule[PRINT_RULE_BOTTOM].rightvrule = border->left_and_right;
3704 :
3705 : /* N/A */
3131 sfrost 3706 CBC 6478 : popt->lrule[PRINT_RULE_DATA].hrule = "";
3131 sfrost 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;
3131 sfrost 3713 CBC 6478 : popt->midvrule_blank = column->vertical;
3131 sfrost 3714 ECB :
3715 : /* Same for all unicode today */
3131 sfrost 3716 CBC 6478 : popt->header_nl_left = unicode_style.header_nl_left;
3131 sfrost 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;
3131 sfrost 3720 CBC 6478 : popt->wrap_left = unicode_style.wrap_left;
3131 sfrost 3721 GIC 6478 : popt->wrap_right = unicode_style.wrap_right;
3131 sfrost 3722 CBC 6478 : popt->wrap_right_border = unicode_style.wrap_right_border;
3131 sfrost 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
5447 tgl 3728 ECB : * to be the number of display character positions actually filled.
3729 : */
5449 bruce 3730 : static int
5449 bruce 3731 CBC 503969 : strlen_max_width(unsigned char *str, int *target_width, int encoding)
5449 bruce 3732 ECB : {
5449 bruce 3733 GIC 503969 : unsigned char *start = str;
5447 tgl 3734 CBC 503969 : unsigned char *end = str + strlen((char *) str);
5050 bruce 3735 503969 : int curr_width = 0;
5449 bruce 3736 ECB :
5447 tgl 3737 CBC 6144252 : while (str < end)
3738 : {
5050 bruce 3739 5641048 : int char_width = PQdsplen((char *) str, encoding);
5449 bruce 3740 ECB :
3741 : /*
5050 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.
5449 3746 : */
5447 tgl 3747 CBC 5641048 : if (*target_width < curr_width + char_width && curr_width != 0)
5449 bruce 3748 GIC 765 : break;
3749 :
5449 bruce 3750 CBC 5640283 : curr_width += char_width;
5050 bruce 3751 ECB :
5447 tgl 3752 CBC 5640283 : str += PQmblen((char *) str, encoding);
671 tgl 3753 ECB :
671 tgl 3754 GIC 5640283 : if (str > end) /* Don't overrun invalid string */
671 tgl 3755 LBC 0 : str = end;
5449 bruce 3756 ECB : }
3757 :
5449 bruce 3758 GIC 503969 : *target_width = curr_width;
3759 :
5449 bruce 3760 CBC 503969 : return str - start;
5449 bruce 3761 ECB : }
|