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