2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2008, PostgreSQL Global Development Group
8 #include "postgres_fe.h"
16 #include <sys/ioctl.h> /* for ioctl() */
25 #include "catalog/pg_type.h"
33 * We define the cancel_pressed flag in this file, rather than common.c where
34 * it naturally belongs, because this file is also used by non-psql programs
35 * (see the bin/scripts/ directory). In those programs cancel_pressed will
36 * never become set and will have no effect.
38 * Note: print.c's general strategy for when to check cancel_pressed is to do
39 * so at completion of each row of output.
41 volatile bool cancel_pressed
= false;
43 static char *decimal_point
;
44 static char *grouping
;
45 static char *thousands_sep
;
48 static int strlen_max_width(unsigned char *str
, int *target_width
, int encoding
);
49 static void IsPagerNeeded(const printTableContent
*cont
, const int extra_lines
,
50 FILE **fout
, bool *is_pager
);
54 pg_local_malloc(size_t size
)
61 fprintf(stderr
, _("out of memory\n"));
68 pg_local_calloc(int count
, size_t size
)
72 tmp
= calloc(count
, size
);
75 fprintf(stderr
, _("out of memory\n"));
82 integer_digits(const char *my_str
)
89 frac_len
= strchr(my_str
, '.') ? strlen(strchr(my_str
, '.')) : 0;
91 return strlen(my_str
) - frac_len
;
94 /* Return additional length required for locale-aware numeric output */
96 additional_numeric_locale_len(const char *my_str
)
98 int int_len
= integer_digits(my_str
),
100 int groupdigits
= atoi(grouping
);
103 /* Don't count a leading separator */
104 len
= (int_len
/ groupdigits
- (int_len
% groupdigits
== 0)) *
105 strlen(thousands_sep
);
107 if (strchr(my_str
, '.') != NULL
)
108 len
+= strlen(decimal_point
) - strlen(".");
114 strlen_with_numeric_locale(const char *my_str
)
116 return strlen(my_str
) + additional_numeric_locale_len(my_str
);
119 /* Returns the appropriately formatted string in a new allocated block, caller must free */
121 format_numeric_locale(const char *my_str
)
125 int_len
= integer_digits(my_str
),
127 int groupdigits
= atoi(grouping
);
128 int new_str_start
= 0;
129 char *new_str
= new_str
= pg_local_malloc(
130 strlen_with_numeric_locale(my_str
) + 1);
132 leading_digits
= (int_len
% groupdigits
!= 0) ?
133 int_len
% groupdigits
: groupdigits
;
135 if (my_str
[0] == '-') /* skip over sign, affects grouping
138 new_str
[0] = my_str
[0];
143 for (i
= 0, j
= new_str_start
;; i
++, j
++)
145 /* Hit decimal point? */
146 if (my_str
[i
] == '.')
148 strcpy(&new_str
[j
], decimal_point
);
149 j
+= strlen(decimal_point
);
150 /* add fractional part */
151 strcpy(&new_str
[j
], &my_str
[i
] + 1);
156 if (my_str
[i
] == '\0')
163 if (i
!= 0 && (i
- leading_digits
) % groupdigits
== 0)
165 strcpy(&new_str
[j
], thousands_sep
);
166 j
+= strlen(thousands_sep
);
169 new_str
[j
] = my_str
[i
];
175 /*************************/
177 /*************************/
181 print_unaligned_text(const printTableContent
*cont
, FILE *fout
)
183 const char *opt_fieldsep
= cont
->opt
->fieldSep
;
184 const char *opt_recordsep
= cont
->opt
->recordSep
;
185 bool opt_tuples_only
= cont
->opt
->tuples_only
;
186 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
188 const char *const * ptr
;
189 bool need_recordsep
= false;
199 if (cont
->opt
->start_table
)
202 if (!opt_tuples_only
&& cont
->title
)
203 fprintf(fout
, "%s%s", cont
->title
, opt_recordsep
);
206 if (!opt_tuples_only
)
208 for (ptr
= cont
->headers
; *ptr
; ptr
++)
210 if (ptr
!= cont
->headers
)
211 fputs(opt_fieldsep
, fout
);
214 need_recordsep
= true;
218 /* assume continuing printout */
219 need_recordsep
= true;
222 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
226 fputs(opt_recordsep
, fout
);
227 need_recordsep
= false;
231 if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
233 char *my_cell
= format_numeric_locale(*ptr
);
235 fputs(my_cell
, fout
);
241 if ((i
+ 1) % cont
->ncolumns
)
242 fputs(opt_fieldsep
, fout
);
244 need_recordsep
= true;
248 if (cont
->opt
->stop_table
)
250 if (!opt_tuples_only
&& cont
->footers
!= NULL
&& !cancel_pressed
)
254 for (f
= cont
->footers
; f
; f
= f
->next
)
258 fputs(opt_recordsep
, fout
);
259 need_recordsep
= false;
261 fputs(f
->data
, fout
);
262 need_recordsep
= true;
265 /* the last record needs to be concluded with a newline */
273 print_unaligned_vertical(const printTableContent
*cont
, FILE *fout
)
275 const char *opt_fieldsep
= cont
->opt
->fieldSep
;
276 const char *opt_recordsep
= cont
->opt
->recordSep
;
277 bool opt_tuples_only
= cont
->opt
->tuples_only
;
278 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
280 const char *const * ptr
;
281 bool need_recordsep
= false;
291 if (cont
->opt
->start_table
)
294 if (!opt_tuples_only
&& cont
->title
)
296 fputs(cont
->title
, fout
);
297 need_recordsep
= true;
301 /* assume continuing printout */
302 need_recordsep
= true;
305 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
309 /* record separator is 2 occurrences of recordsep in this mode */
310 fputs(opt_recordsep
, fout
);
311 fputs(opt_recordsep
, fout
);
312 need_recordsep
= false;
317 fputs(cont
->headers
[i
% cont
->ncolumns
], fout
);
318 fputs(opt_fieldsep
, fout
);
319 if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
321 char *my_cell
= format_numeric_locale(*ptr
);
323 fputs(my_cell
, fout
);
329 if ((i
+ 1) % cont
->ncolumns
)
330 fputs(opt_recordsep
, fout
);
332 need_recordsep
= true;
335 if (cont
->opt
->stop_table
)
338 if (!opt_tuples_only
&& cont
->footers
!= NULL
&& !cancel_pressed
)
342 fputs(opt_recordsep
, fout
);
343 for (f
= cont
->footers
; f
; f
= f
->next
)
345 fputs(opt_recordsep
, fout
);
346 fputs(f
->data
, fout
);
355 /********************/
357 /********************/
362 _print_horizontal_line(const unsigned int ncolumns
, const unsigned int *widths
,
363 unsigned short border
, FILE *fout
)
370 else if (border
== 2)
373 for (i
= 0; i
< ncolumns
; i
++)
375 for (j
= 0; j
< widths
[i
]; j
++)
378 if (i
< ncolumns
- 1)
389 else if (border
== 1)
397 * Print pretty boxes around cells.
400 print_aligned_text(const printTableContent
*cont
, FILE *fout
)
402 bool opt_tuples_only
= cont
->opt
->tuples_only
;
403 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
404 int encoding
= cont
->opt
->encoding
;
405 unsigned short opt_border
= cont
->opt
->border
;
407 unsigned int col_count
= 0, cell_count
= 0;
412 unsigned int *width_header
,
416 unsigned int *max_nl_lines
, /* value split by newlines */
419 unsigned char **format_buf
;
420 unsigned int width_total
;
421 unsigned int total_header_width
;
422 unsigned int extra_row_output_lines
= 0;
423 unsigned int extra_output_lines
= 0;
425 const char * const *ptr
;
427 struct lineptr
**col_lineptrs
; /* pointers to line pointer per column */
429 bool *header_done
; /* Have all header lines been output? */
430 int *bytes_output
; /* Bytes output for column value */
431 int output_columns
= 0; /* Width of interactive console */
432 bool is_pager
= false;
440 if (cont
->ncolumns
> 0)
442 col_count
= cont
->ncolumns
;
443 width_header
= pg_local_calloc(col_count
, sizeof(*width_header
));
444 width_average
= pg_local_calloc(col_count
, sizeof(*width_average
));
445 max_width
= pg_local_calloc(col_count
, sizeof(*max_width
));
446 width_wrap
= pg_local_calloc(col_count
, sizeof(*width_wrap
));
447 max_nl_lines
= pg_local_calloc(col_count
, sizeof(*max_nl_lines
));
448 curr_nl_line
= pg_local_calloc(col_count
, sizeof(*curr_nl_line
));
449 col_lineptrs
= pg_local_calloc(col_count
, sizeof(*col_lineptrs
));
450 max_bytes
= pg_local_calloc(col_count
, sizeof(*max_bytes
));
451 format_buf
= pg_local_calloc(col_count
, sizeof(*format_buf
));
452 header_done
= pg_local_calloc(col_count
, sizeof(*header_done
));
453 bytes_output
= pg_local_calloc(col_count
, sizeof(*bytes_output
));
458 width_average
= NULL
;
470 /* scan all column headers, find maximum width and max max_nl_lines */
471 for (i
= 0; i
< col_count
; i
++)
477 pg_wcssize((unsigned char *) cont
->headers
[i
], strlen(cont
->headers
[i
]),
478 encoding
, &width
, &nl_lines
, &bytes_required
);
479 if (width
> max_width
[i
])
480 max_width
[i
] = width
;
481 if (nl_lines
> max_nl_lines
[i
])
482 max_nl_lines
[i
] = nl_lines
;
483 if (bytes_required
> max_bytes
[i
])
484 max_bytes
[i
] = bytes_required
;
485 if (nl_lines
> extra_row_output_lines
)
486 extra_row_output_lines
= nl_lines
;
488 width_header
[i
] = width
;
490 /* Add height of tallest header column */
491 extra_output_lines
+= extra_row_output_lines
;
492 extra_row_output_lines
= 0;
494 /* scan all cells, find maximum width, compute cell_count */
495 for (i
= 0, ptr
= cont
->cells
; *ptr
; ptr
++, i
++, cell_count
++)
501 pg_wcssize((unsigned char *) *ptr
, strlen(*ptr
), encoding
,
502 &width
, &nl_lines
, &bytes_required
);
503 if (opt_numeric_locale
&& cont
->aligns
[i
% col_count
] == 'r')
505 width
+= additional_numeric_locale_len(*ptr
);
506 bytes_required
+= additional_numeric_locale_len(*ptr
);
509 if (width
> max_width
[i
% col_count
])
510 max_width
[i
% col_count
] = width
;
511 if (nl_lines
> max_nl_lines
[i
% col_count
])
512 max_nl_lines
[i
% col_count
] = nl_lines
;
513 if (bytes_required
> max_bytes
[i
% col_count
])
514 max_bytes
[i
% col_count
] = bytes_required
;
516 width_average
[i
% col_count
] += width
;
519 /* If we have rows, compute average */
520 if (col_count
!= 0 && cell_count
!= 0)
522 int rows
= cell_count
/ col_count
;
524 for (i
= 0; i
< col_count
; i
++)
525 width_average
[i
] /= rows
;
528 /* adjust the total display width based on border style */
530 width_total
= col_count
- 1;
531 else if (opt_border
== 1)
532 width_total
= col_count
* 3 - 1;
534 width_total
= col_count
* 3 + 1;
535 total_header_width
= width_total
;
537 for (i
= 0; i
< col_count
; i
++)
539 width_total
+= max_width
[i
];
540 total_header_width
+= width_header
[i
];
544 * At this point: max_width[] contains the max width of each column,
545 * max_nl_lines[] contains the max number of lines in each column,
546 * max_bytes[] contains the maximum storage space for formatting
547 * strings, width_total contains the giant width sum. Now we allocate
548 * some memory for line pointers.
550 for (i
= 0; i
< col_count
; i
++)
552 /* Add entry for ptr == NULL array termination */
553 col_lineptrs
[i
] = pg_local_calloc(max_nl_lines
[i
] + 1,
554 sizeof(**col_lineptrs
));
556 format_buf
[i
] = pg_local_malloc(max_bytes
[i
] + 1);
558 col_lineptrs
[i
]->ptr
= format_buf
[i
];
561 /* Default word wrap to the full width, i.e. no word wrap */
562 for (i
= 0; i
< col_count
; i
++)
563 width_wrap
[i
] = max_width
[i
];
566 * Choose target output width: \pset columns, or $COLUMNS, or ioctl
568 if (cont
->opt
->columns
> 0)
569 output_columns
= cont
->opt
->columns
;
570 else if ((fout
== stdout
&& isatty(fileno(stdout
))) || is_pager
)
572 if (cont
->opt
->env_columns
> 0)
573 output_columns
= cont
->opt
->env_columns
;
577 struct winsize screen_size
;
579 if (ioctl(fileno(stdout
), TIOCGWINSZ
, &screen_size
) != -1)
580 output_columns
= screen_size
.ws_col
;
585 if (cont
->opt
->format
== PRINT_WRAPPED
)
588 * Optional optimized word wrap. Shrink columns with a high max/avg
589 * ratio. Slighly bias against wider columns. (Increases chance a
590 * narrow column will fit in its cell.) If available columns is
591 * positive... and greater than the width of the unshrinkable column
594 if (output_columns
> 0 && output_columns
>= total_header_width
)
596 /* While there is still excess width... */
597 while (width_total
> output_columns
)
599 double max_ratio
= 0;
603 * Find column that has the highest ratio of its maximum
604 * width compared to its average width. This tells us which
605 * column will produce the fewest wrapped values if shortened.
606 * width_wrap starts as equal to max_width.
608 for (i
= 0; i
< col_count
; i
++)
610 if (width_average
[i
] && width_wrap
[i
] > width_header
[i
])
612 /* Penalize wide columns by 1% of their width */
615 ratio
= (double) width_wrap
[i
] / width_average
[i
] +
617 if (ratio
> max_ratio
)
625 /* Exit loop if we can't squeeze any more. */
629 /* Decrease width of target column by one. */
630 width_wrap
[worst_col
]--;
636 /* If we wrapped beyond the display width, use the pager */
637 if (!is_pager
&& fout
== stdout
&& output_columns
> 0 &&
638 (output_columns
< total_header_width
|| output_columns
< width_total
))
640 fout
= PageOutput(INT_MAX
, cont
->opt
->pager
); /* force pager */
644 /* Check if newlines or our wrapping now need the pager */
647 /* scan all cells, find maximum width, compute cell_count */
648 for (i
= 0, ptr
= cont
->cells
; *ptr
; ptr
++, cell_count
++)
654 pg_wcssize((unsigned char *) *ptr
, strlen(*ptr
), encoding
,
655 &width
, &nl_lines
, &bytes_required
);
656 if (opt_numeric_locale
&& cont
->align
[i
] == 'r')
657 width
+= additional_numeric_locale_len(*ptr
);
660 * A row can have both wrapping and newlines that cause
661 * it to display across multiple lines. We check
662 * for both cases below.
664 if (width
> 0 && width_wrap
[i
])
666 unsigned int extra_lines
;
668 extra_lines
= (width
-1) / width_wrap
[i
] + nl_lines
;
669 if (extra_lines
> extra_row_output_lines
)
670 extra_row_output_lines
= extra_lines
;
673 /* i is the current column number: increment with wrap */
674 if (++i
>= col_count
)
677 /* At last column of each row, add tallest column height */
678 extra_output_lines
+= extra_row_output_lines
;
679 extra_row_output_lines
= 0;
682 IsPagerNeeded(cont
, extra_output_lines
, &fout
, &is_pager
);
686 if (cont
->opt
->start_table
)
689 if (cont
->title
&& !opt_tuples_only
)
693 pg_wcssize((unsigned char *) cont
->title
, strlen(cont
->title
),
694 encoding
, &width
, &height
, NULL
);
695 if (width
>= width_total
)
697 fprintf(fout
, "%s\n", cont
->title
);
700 fprintf(fout
, "%-*s%s\n", (width_total
- width
) / 2, "",
705 if (!opt_tuples_only
)
707 int more_col_wrapping
;
711 _print_horizontal_line(col_count
, width_wrap
, opt_border
, fout
);
713 for (i
= 0; i
< col_count
; i
++)
714 pg_wcsformat((unsigned char *) cont
->headers
[i
],
715 strlen(cont
->headers
[i
]), encoding
,
716 col_lineptrs
[i
], max_nl_lines
[i
]);
718 more_col_wrapping
= col_count
;
720 memset(header_done
, false, col_count
* sizeof(bool));
721 while (more_col_wrapping
)
724 fprintf(fout
, "|%c", curr_nl_line
? '+' : ' ');
725 else if (opt_border
== 1)
726 fputc(curr_nl_line
? '+' : ' ', fout
);
728 for (i
= 0; i
< cont
->ncolumns
; i
++)
730 unsigned int nbspace
;
732 struct lineptr
*this_line
= col_lineptrs
[i
] + curr_nl_line
;
736 nbspace
= width_wrap
[i
] - this_line
->width
;
739 fprintf(fout
, "%-*s%s%-*s",
740 nbspace
/ 2, "", this_line
->ptr
, (nbspace
+ 1) / 2, "");
742 if (!(this_line
+ 1)->ptr
)
749 fprintf(fout
, "%*s", width_wrap
[i
], "");
750 if (i
< col_count
- 1)
753 fputc(curr_nl_line
? '+' : ' ', fout
);
755 fprintf(fout
, " |%c", curr_nl_line
? '+' : ' ');
762 else if (opt_border
== 1)
767 _print_horizontal_line(col_count
, width_wrap
, opt_border
, fout
);
771 /* print cells, one loop per row */
772 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
+= col_count
, ptr
+= col_count
)
780 * Format each cell. Format again, if it's a numeric formatting locale
781 * (e.g. 123,456 vs. 123456)
783 for (j
= 0; j
< col_count
; j
++)
785 pg_wcsformat((unsigned char *) ptr
[j
], strlen(ptr
[j
]), encoding
,
786 col_lineptrs
[j
], max_nl_lines
[j
]);
789 if (opt_numeric_locale
&& cont
->aligns
[j
] == 'r')
793 my_cell
= format_numeric_locale((char *) col_lineptrs
[j
]->ptr
);
794 /* Buffer IS large enough... now */
795 strcpy((char *) col_lineptrs
[j
]->ptr
, my_cell
);
800 memset(bytes_output
, 0, col_count
* sizeof(int));
803 * Each time through this loop, one display line is output.
804 * It can either be a full value or a partial value if embedded
805 * newlines exist or if 'format=wrapping' mode is enabled.
814 else if (opt_border
== 1)
817 /* for each column */
818 for (j
= 0; j
< col_count
; j
++)
820 /* We have a valid array element, so index it */
821 struct lineptr
*this_line
= &col_lineptrs
[j
][curr_nl_line
[j
]];
823 int chars_to_output
= width_wrap
[j
];
824 bool finalspaces
= (opt_border
== 2 || j
< col_count
- 1);
828 /* Past newline lines so just pad for other columns */
830 fprintf(fout
, "%*s", chars_to_output
, "");
834 /* Get strlen() of the characters up to width_wrap */
836 strlen_max_width(this_line
->ptr
+ bytes_output
[j
],
837 &chars_to_output
, encoding
);
840 * If we exceeded width_wrap, it means the display width
841 * of a single character was wider than our target width.
842 * In that case, we have to pretend we are only printing
843 * the target display width and make the best of it.
845 if (chars_to_output
> width_wrap
[j
])
846 chars_to_output
= width_wrap
[j
];
848 if (cont
->aligns
[j
] == 'r') /* Right aligned cell */
851 fprintf(fout
, "%*s", width_wrap
[j
] - chars_to_output
, "");
852 fprintf(fout
, "%.*s", bytes_to_output
,
853 this_line
->ptr
+ bytes_output
[j
]);
855 else /* Left aligned cell */
858 fprintf(fout
, "%.*s", bytes_to_output
,
859 this_line
->ptr
+ bytes_output
[j
]);
861 fprintf(fout
, "%*s", width_wrap
[j
] - chars_to_output
, "");
864 bytes_output
[j
] += bytes_to_output
;
866 /* Do we have more text to wrap? */
867 if (*(this_line
->ptr
+ bytes_output
[j
]) != '\0')
871 /* Advance to next newline line */
873 if (col_lineptrs
[j
][curr_nl_line
[j
]].ptr
!= NULL
)
879 /* print a divider, if not the last column */
880 if (j
< col_count
- 1)
884 /* Next value is beyond past newlines? */
885 else if (col_lineptrs
[j
+1][curr_nl_line
[j
+1]].ptr
== NULL
)
887 /* In wrapping of value? */
888 else if (bytes_output
[j
+1] != 0)
890 /* After first newline value */
891 else if (curr_nl_line
[j
+1] != 0)
899 /* end-of-row border */
904 } while (more_lines
);
907 if (cont
->opt
->stop_table
)
909 if (opt_border
== 2 && !cancel_pressed
)
910 _print_horizontal_line(col_count
, width_wrap
, opt_border
, fout
);
913 if (cont
->footers
&& !opt_tuples_only
&& !cancel_pressed
)
917 for (f
= cont
->footers
; f
; f
= f
->next
)
918 fprintf(fout
, "%s\n", f
->data
);
935 for (i
= 0; i
< col_count
; i
++)
945 print_aligned_vertical(const printTableContent
*cont
, FILE *fout
)
947 bool opt_tuples_only
= cont
->opt
->tuples_only
;
948 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
949 unsigned short opt_border
= cont
->opt
->border
;
950 int encoding
= cont
->opt
->encoding
;
951 unsigned long record
= cont
->opt
->prior_records
+ 1;
952 const char *const * ptr
;
961 struct lineptr
*hlineptr
,
970 if (cont
->cells
[0] == NULL
&& cont
->opt
->start_table
&&
971 cont
->opt
->stop_table
)
973 fprintf(fout
, _("(No rows)\n"));
977 /* Find the maximum dimensions for the headers */
978 for (i
= 0; i
< cont
->ncolumns
; i
++)
984 pg_wcssize((unsigned char *) cont
->headers
[i
], strlen(cont
->headers
[i
]),
985 encoding
, &width
, &height
, &fs
);
988 if (height
> hheight
)
990 if (fs
> hformatsize
)
994 /* find longest data cell */
995 for (i
= 0, ptr
= cont
->cells
; *ptr
; ptr
++, i
++)
997 int numeric_locale_len
;
1002 if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
1003 numeric_locale_len
= additional_numeric_locale_len(*ptr
);
1005 numeric_locale_len
= 0;
1007 pg_wcssize((unsigned char *) *ptr
, strlen(*ptr
), encoding
,
1008 &width
, &height
, &fs
);
1009 width
+= numeric_locale_len
;
1012 if (height
> dheight
)
1014 if (fs
> dformatsize
)
1019 * We now have all the information we need to setup the formatting
1022 dlineptr
= pg_local_malloc((sizeof(*dlineptr
) + 1) * dheight
);
1023 hlineptr
= pg_local_malloc((sizeof(*hlineptr
) + 1) * hheight
);
1025 dlineptr
->ptr
= pg_local_malloc(dformatsize
);
1026 hlineptr
->ptr
= pg_local_malloc(hformatsize
);
1028 /* make horizontal border */
1029 divider
= pg_local_malloc(hwidth
+ dwidth
+ 10);
1031 if (opt_border
== 2)
1032 strcat(divider
, "+-");
1033 for (i
= 0; i
< hwidth
; i
++)
1034 strcat(divider
, opt_border
> 0 ? "-" : " ");
1036 strcat(divider
, "-+-");
1038 strcat(divider
, " ");
1039 for (i
= 0; i
< dwidth
; i
++)
1040 strcat(divider
, opt_border
> 0 ? "-" : " ");
1041 if (opt_border
== 2)
1042 strcat(divider
, "-+");
1044 if (cont
->opt
->start_table
)
1047 if (!opt_tuples_only
&& cont
->title
)
1048 fprintf(fout
, "%s\n", cont
->title
);
1052 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1058 if (i
% cont
->ncolumns
== 0)
1062 if (!opt_tuples_only
)
1064 char record_str
[64];
1065 size_t record_str_len
;
1067 if (opt_border
== 0)
1068 snprintf(record_str
, 64, "* Record %lu", record
++);
1070 snprintf(record_str
, 64, "[ RECORD %lu ]", record
++);
1071 record_str_len
= strlen(record_str
);
1073 if (record_str_len
+ opt_border
> strlen(divider
))
1074 fprintf(fout
, "%.*s%s\n", opt_border
, divider
, record_str
);
1077 char *div_copy
= pg_strdup(divider
);
1079 strncpy(div_copy
+ opt_border
, record_str
, record_str_len
);
1080 fprintf(fout
, "%s\n", div_copy
);
1084 else if (i
!= 0 || !cont
->opt
->start_table
|| opt_border
== 2)
1085 fprintf(fout
, "%s\n", divider
);
1088 /* Format the header */
1089 pg_wcsformat((unsigned char *) cont
->headers
[i
% cont
->ncolumns
],
1090 strlen(cont
->headers
[i
% cont
->ncolumns
]),
1091 encoding
, hlineptr
, hheight
);
1092 /* Format the data */
1093 pg_wcsformat((unsigned char *) *ptr
, strlen(*ptr
), encoding
,
1097 dcomplete
= hcomplete
= 0;
1098 while (!dcomplete
|| !hcomplete
)
1100 if (opt_border
== 2)
1104 fprintf(fout
, "%-s%*s", hlineptr
[line_count
].ptr
,
1105 hwidth
- hlineptr
[line_count
].width
, "");
1107 if (!hlineptr
[line_count
+ 1].ptr
)
1111 fprintf(fout
, "%*s", hwidth
, "");
1114 fprintf(fout
, " %c ", (line_count
== 0) ? '|' : ':');
1120 if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
1122 char *my_cell
= format_numeric_locale((char *) dlineptr
[line_count
].ptr
);
1125 fprintf(fout
, "%s\n", my_cell
);
1127 fprintf(fout
, "%-s%*s |\n", my_cell
,
1128 (int) (dwidth
- strlen(my_cell
)), "");
1134 fprintf(fout
, "%s\n", dlineptr
[line_count
].ptr
);
1136 fprintf(fout
, "%-s%*s |\n", dlineptr
[line_count
].ptr
,
1137 dwidth
- dlineptr
[line_count
].width
, "");
1140 if (!dlineptr
[line_count
+ 1].ptr
)
1148 fprintf(fout
, "%*s |\n", dwidth
, "");
1154 if (cont
->opt
->stop_table
)
1156 if (opt_border
== 2 && !cancel_pressed
)
1157 fprintf(fout
, "%s\n", divider
);
1160 if (!opt_tuples_only
&& cont
->footers
!= NULL
&& !cancel_pressed
)
1162 printTableFooter
*f
;
1166 for (f
= cont
->footers
; f
; f
= f
->next
)
1167 fprintf(fout
, "%s\n", f
->data
);
1174 free(hlineptr
->ptr
);
1175 free(dlineptr
->ptr
);
1181 /**********************/
1182 /* HTML printing ******/
1183 /**********************/
1187 html_escaped_print(const char *in
, FILE *fout
)
1190 bool leading_space
= true;
1192 for (p
= in
; *p
; p
++)
1197 fputs("&", fout
);
1200 fputs("<", fout
);
1203 fputs(">", fout
);
1206 fputs("<br />\n", fout
);
1209 fputs(""", fout
);
1212 /* protect leading space, for EXPLAIN output */
1214 fputs(" ", fout
);
1222 leading_space
= false;
1228 print_html_text(const printTableContent
*cont
, FILE *fout
)
1230 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1231 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1232 unsigned short opt_border
= cont
->opt
->border
;
1233 const char *opt_table_attr
= cont
->opt
->tableAttr
;
1235 const char *const * ptr
;
1240 if (cont
->opt
->start_table
)
1242 fprintf(fout
, "<table border=\"%d\"", opt_border
);
1244 fprintf(fout
, " %s", opt_table_attr
);
1248 if (!opt_tuples_only
&& cont
->title
)
1250 fputs(" <caption>", fout
);
1251 html_escaped_print(cont
->title
, fout
);
1252 fputs("</caption>\n", fout
);
1256 if (!opt_tuples_only
)
1258 fputs(" <tr>\n", fout
);
1259 for (ptr
= cont
->headers
; *ptr
; ptr
++)
1261 fputs(" <th align=\"center\">", fout
);
1262 html_escaped_print(*ptr
, fout
);
1263 fputs("</th>\n", fout
);
1265 fputs(" </tr>\n", fout
);
1270 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1272 if (i
% cont
->ncolumns
== 0)
1276 fputs(" <tr valign=\"top\">\n", fout
);
1279 fprintf(fout
, " <td align=\"%s\">", cont
->aligns
[(i
) % cont
->ncolumns
] == 'r' ? "right" : "left");
1280 /* is string only whitespace? */
1281 if ((*ptr
)[strspn(*ptr
, " \t")] == '\0')
1282 fputs(" ", fout
);
1283 else if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
1285 char *my_cell
= format_numeric_locale(*ptr
);
1287 html_escaped_print(my_cell
, fout
);
1291 html_escaped_print(*ptr
, fout
);
1293 fputs("</td>\n", fout
);
1295 if ((i
+ 1) % cont
->ncolumns
== 0)
1296 fputs(" </tr>\n", fout
);
1299 if (cont
->opt
->stop_table
)
1301 fputs("</table>\n", fout
);
1304 if (!opt_tuples_only
&& cont
->footers
!= NULL
&& !cancel_pressed
)
1306 printTableFooter
*f
;
1309 for (f
= cont
->footers
; f
; f
= f
->next
)
1311 html_escaped_print(f
->data
, fout
);
1312 fputs("<br />\n", fout
);
1314 fputs("</p>", fout
);
1323 print_html_vertical(const printTableContent
*cont
, FILE *fout
)
1325 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1326 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1327 unsigned short opt_border
= cont
->opt
->border
;
1328 const char *opt_table_attr
= cont
->opt
->tableAttr
;
1329 unsigned long record
= cont
->opt
->prior_records
+ 1;
1331 const char *const * ptr
;
1336 if (cont
->opt
->start_table
)
1338 fprintf(fout
, "<table border=\"%d\"", opt_border
);
1340 fprintf(fout
, " %s", opt_table_attr
);
1344 if (!opt_tuples_only
&& cont
->title
)
1346 fputs(" <caption>", fout
);
1347 html_escaped_print(cont
->title
, fout
);
1348 fputs("</caption>\n", fout
);
1353 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1355 if (i
% cont
->ncolumns
== 0)
1359 if (!opt_tuples_only
)
1361 "\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
1364 fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout
);
1366 fputs(" <tr valign=\"top\">\n"
1368 html_escaped_print(cont
->headers
[i
% cont
->ncolumns
], fout
);
1369 fputs("</th>\n", fout
);
1371 fprintf(fout
, " <td align=\"%s\">", cont
->aligns
[i
% cont
->ncolumns
] == 'r' ? "right" : "left");
1372 /* is string only whitespace? */
1373 if ((*ptr
)[strspn(*ptr
, " \t")] == '\0')
1374 fputs(" ", fout
);
1375 else if (cont
->aligns
[i
% cont
->ncolumns
] == 'r' && opt_numeric_locale
)
1377 char *my_cell
= format_numeric_locale(*ptr
);
1379 html_escaped_print(my_cell
, fout
);
1383 html_escaped_print(*ptr
, fout
);
1385 fputs("</td>\n </tr>\n", fout
);
1388 if (cont
->opt
->stop_table
)
1390 fputs("</table>\n", fout
);
1393 if (!opt_tuples_only
&& cont
->footers
!= NULL
&& !cancel_pressed
)
1395 printTableFooter
*f
;
1398 for (f
= cont
->footers
; f
; f
= f
->next
)
1400 html_escaped_print(f
->data
, fout
);
1401 fputs("<br />\n", fout
);
1403 fputs("</p>", fout
);
1411 /*************************/
1413 /*************************/
1417 latex_escaped_print(const char *in
, FILE *fout
)
1421 for (p
= in
; *p
; p
++)
1443 fputs("\\backslash", fout
);
1446 fputs("\\\\", fout
);
1455 print_latex_text(const printTableContent
*cont
, FILE *fout
)
1457 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1458 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1459 unsigned short opt_border
= cont
->opt
->border
;
1461 const char *const * ptr
;
1469 if (cont
->opt
->start_table
)
1472 if (!opt_tuples_only
&& cont
->title
)
1474 fputs("\\begin{center}\n", fout
);
1475 latex_escaped_print(cont
->title
, fout
);
1476 fputs("\n\\end{center}\n\n", fout
);
1479 /* begin environment and set alignments and borders */
1480 fputs("\\begin{tabular}{", fout
);
1482 if (opt_border
== 2)
1484 for (i
= 0; i
< cont
->ncolumns
; i
++)
1486 fputc(*(cont
->aligns
+ i
), fout
);
1487 if (opt_border
!= 0 && i
< cont
->ncolumns
- 1)
1490 if (opt_border
== 2)
1495 if (!opt_tuples_only
&& opt_border
== 2)
1496 fputs("\\hline\n", fout
);
1499 if (!opt_tuples_only
)
1501 for (i
= 0, ptr
= cont
->headers
; i
< cont
->ncolumns
; i
++, ptr
++)
1505 fputs("\\textit{", fout
);
1506 latex_escaped_print(*ptr
, fout
);
1509 fputs(" \\\\\n", fout
);
1510 fputs("\\hline\n", fout
);
1515 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1517 if (opt_numeric_locale
)
1519 char *my_cell
= format_numeric_locale(*ptr
);
1521 latex_escaped_print(my_cell
, fout
);
1525 latex_escaped_print(*ptr
, fout
);
1527 if ((i
+ 1) % cont
->ncolumns
== 0)
1529 fputs(" \\\\\n", fout
);
1537 if (cont
->opt
->stop_table
)
1539 if (opt_border
== 2)
1540 fputs("\\hline\n", fout
);
1542 fputs("\\end{tabular}\n\n\\noindent ", fout
);
1545 if (cont
->footers
&& !opt_tuples_only
&& !cancel_pressed
)
1547 printTableFooter
*f
;
1549 for (f
= cont
->footers
; f
; f
= f
->next
)
1551 latex_escaped_print(f
->data
, fout
);
1552 fputs(" \\\\\n", fout
);
1562 print_latex_vertical(const printTableContent
*cont
, FILE *fout
)
1564 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1565 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1566 unsigned short opt_border
= cont
->opt
->border
;
1567 unsigned long record
= cont
->opt
->prior_records
+ 1;
1569 const char *const * ptr
;
1577 if (cont
->opt
->start_table
)
1580 if (!opt_tuples_only
&& cont
->title
)
1582 fputs("\\begin{center}\n", fout
);
1583 latex_escaped_print(cont
->title
, fout
);
1584 fputs("\n\\end{center}\n\n", fout
);
1587 /* begin environment and set alignments and borders */
1588 fputs("\\begin{tabular}{", fout
);
1589 if (opt_border
== 0)
1591 else if (opt_border
== 1)
1593 else if (opt_border
== 2)
1594 fputs("|c|l|", fout
);
1599 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1602 if (i
% cont
->ncolumns
== 0)
1606 if (!opt_tuples_only
)
1608 if (opt_border
== 2)
1610 fputs("\\hline\n", fout
);
1611 fprintf(fout
, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record
++);
1614 fprintf(fout
, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record
++);
1616 if (opt_border
>= 1)
1617 fputs("\\hline\n", fout
);
1620 latex_escaped_print(cont
->headers
[i
% cont
->ncolumns
], fout
);
1622 latex_escaped_print(*ptr
, fout
);
1623 fputs(" \\\\\n", fout
);
1626 if (cont
->opt
->stop_table
)
1628 if (opt_border
== 2)
1629 fputs("\\hline\n", fout
);
1631 fputs("\\end{tabular}\n\n\\noindent ", fout
);
1634 if (cont
->footers
&& !opt_tuples_only
&& !cancel_pressed
)
1636 printTableFooter
*f
;
1638 for (f
= cont
->footers
; f
; f
= f
->next
)
1640 if (opt_numeric_locale
)
1642 char *my_cell
= format_numeric_locale(f
->data
);
1644 latex_escaped_print(my_cell
, fout
);
1648 latex_escaped_print(f
->data
, fout
);
1649 fputs(" \\\\\n", fout
);
1658 /*************************/
1660 /*************************/
1664 troff_ms_escaped_print(const char *in
, FILE *fout
)
1668 for (p
= in
; *p
; p
++)
1672 fputs("\\(rs", fout
);
1681 print_troff_ms_text(const printTableContent
*cont
, FILE *fout
)
1683 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1684 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1685 unsigned short opt_border
= cont
->opt
->border
;
1687 const char *const * ptr
;
1695 if (cont
->opt
->start_table
)
1698 if (!opt_tuples_only
&& cont
->title
)
1700 fputs(".LP\n.DS C\n", fout
);
1701 troff_ms_escaped_print(cont
->title
, fout
);
1702 fputs("\n.DE\n", fout
);
1705 /* begin environment and set alignments and borders */
1706 fputs(".LP\n.TS\n", fout
);
1707 if (opt_border
== 2)
1708 fputs("center box;\n", fout
);
1710 fputs("center;\n", fout
);
1712 for (i
= 0; i
< cont
->ncolumns
; i
++)
1714 fputc(*(cont
->aligns
+ i
), fout
);
1715 if (opt_border
> 0 && i
< cont
->ncolumns
- 1)
1721 if (!opt_tuples_only
)
1723 for (i
= 0, ptr
= cont
->headers
; i
< cont
->ncolumns
; i
++, ptr
++)
1727 fputs("\\fI", fout
);
1728 troff_ms_escaped_print(*ptr
, fout
);
1729 fputs("\\fP", fout
);
1731 fputs("\n_\n", fout
);
1736 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1738 if (opt_numeric_locale
)
1740 char *my_cell
= format_numeric_locale(*ptr
);
1742 troff_ms_escaped_print(my_cell
, fout
);
1746 troff_ms_escaped_print(*ptr
, fout
);
1748 if ((i
+ 1) % cont
->ncolumns
== 0)
1758 if (cont
->opt
->stop_table
)
1760 fputs(".TE\n.DS L\n", fout
);
1763 if (cont
->footers
&& !opt_tuples_only
&& !cancel_pressed
)
1765 printTableFooter
*f
;
1767 for (f
= cont
->footers
; f
; f
= f
->next
)
1769 troff_ms_escaped_print(f
->data
, fout
);
1774 fputs(".DE\n", fout
);
1780 print_troff_ms_vertical(const printTableContent
*cont
, FILE *fout
)
1782 bool opt_tuples_only
= cont
->opt
->tuples_only
;
1783 bool opt_numeric_locale
= cont
->opt
->numericLocale
;
1784 unsigned short opt_border
= cont
->opt
->border
;
1785 unsigned long record
= cont
->opt
->prior_records
+ 1;
1787 const char *const * ptr
;
1788 unsigned short current_format
= 0; /* 0=none, 1=header, 2=body */
1796 if (cont
->opt
->start_table
)
1799 if (!opt_tuples_only
&& cont
->title
)
1801 fputs(".LP\n.DS C\n", fout
);
1802 troff_ms_escaped_print(cont
->title
, fout
);
1803 fputs("\n.DE\n", fout
);
1806 /* begin environment and set alignments and borders */
1807 fputs(".LP\n.TS\n", fout
);
1808 if (opt_border
== 2)
1809 fputs("center box;\n", fout
);
1811 fputs("center;\n", fout
);
1814 if (opt_tuples_only
)
1815 fputs("c l;\n", fout
);
1818 current_format
= 2; /* assume tuples printed already */
1821 for (i
= 0, ptr
= cont
->cells
; *ptr
; i
++, ptr
++)
1824 if (i
% cont
->ncolumns
== 0)
1828 if (!opt_tuples_only
)
1830 if (current_format
!= 1)
1832 if (opt_border
== 2 && record
> 1)
1834 if (current_format
!= 0)
1835 fputs(".T&\n", fout
);
1836 fputs("c s.\n", fout
);
1839 fprintf(fout
, "\\fIRecord %lu\\fP\n", record
++);
1841 if (opt_border
>= 1)
1845 if (!opt_tuples_only
)
1847 if (current_format
!= 2)
1849 if (current_format
!= 0)
1850 fputs(".T&\n", fout
);
1851 if (opt_border
!= 1)
1852 fputs("c l.\n", fout
);
1854 fputs("c | l.\n", fout
);
1859 troff_ms_escaped_print(cont
->headers
[i
% cont
->ncolumns
], fout
);
1861 if (opt_numeric_locale
)
1863 char *my_cell
= format_numeric_locale(*ptr
);
1865 troff_ms_escaped_print(my_cell
, fout
);
1869 troff_ms_escaped_print(*ptr
, fout
);
1874 if (cont
->opt
->stop_table
)
1876 fputs(".TE\n.DS L\n", fout
);
1879 if (cont
->footers
&& !opt_tuples_only
&& !cancel_pressed
)
1881 printTableFooter
*f
;
1883 for (f
= cont
->footers
; f
; f
= f
->next
)
1885 troff_ms_escaped_print(f
->data
, fout
);
1890 fputs(".DE\n", fout
);
1895 /********************************/
1896 /* Public functions */
1897 /********************************/
1903 * Tests if pager is needed and returns appropriate FILE pointer.
1906 PageOutput(int lines
, unsigned short int pager
)
1908 /* check whether we need / can / are supposed to use pager */
1909 if (pager
&& isatty(fileno(stdin
)) && isatty(fileno(stdout
)))
1911 const char *pagerprog
;
1916 struct winsize screen_size
;
1918 result
= ioctl(fileno(stdout
), TIOCGWINSZ
, &screen_size
);
1920 /* >= accounts for a one-line prompt */
1921 if (result
== -1 || lines
>= screen_size
.ws_row
|| pager
> 1)
1924 pagerprog
= getenv("PAGER");
1926 pagerprog
= DEFAULT_PAGER
;
1928 pqsignal(SIGPIPE
, SIG_IGN
);
1930 pagerpipe
= popen(pagerprog
, "w");
1944 * Close previously opened pager pipe, if any
1947 ClosePager(FILE *pagerpipe
)
1949 if (pagerpipe
&& pagerpipe
!= stdout
)
1952 * If printing was canceled midstream, warn about it.
1954 * Some pagers like less use Ctrl-C as part of their command set. Even
1955 * so, we abort our processing and warn the user what we did. If the
1956 * pager quit as a result of the SIGINT, this message won't go
1960 fprintf(pagerpipe
, _("Interrupted\n"));
1964 pqsignal(SIGPIPE
, SIG_DFL
);
1970 * Initialise a table contents struct.
1971 * Must be called before any other printTable method is used.
1973 * The title is not duplicated; the caller must ensure that the buffer
1974 * is available for the lifetime of the printTableContent struct.
1976 * If you call this, you must call printTableCleanup once you're done with the
1980 printTableInit(printTableContent
*const content
, const printTableOpt
*opt
,
1981 const char *title
, const int ncolumns
, const int nrows
)
1984 content
->title
= title
;
1985 content
->ncolumns
= ncolumns
;
1986 content
->nrows
= nrows
;
1988 content
->headers
= pg_local_calloc(ncolumns
+ 1,
1989 sizeof(*content
->headers
));
1991 content
->cells
= pg_local_calloc(ncolumns
* nrows
+ 1,
1992 sizeof(*content
->cells
));
1994 content
->footers
= NULL
;
1996 content
->aligns
= pg_local_calloc(ncolumns
+ 1,
1997 sizeof(*content
->align
));
1999 content
->header
= content
->headers
;
2000 content
->cell
= content
->cells
;
2001 content
->footer
= content
->footers
;
2002 content
->align
= content
->aligns
;
2006 * Add a header to the table.
2008 * Headers are not duplicated; you must ensure that the header string is
2009 * available for the lifetime of the printTableContent struct.
2011 * If translate is true, the function will pass the header through gettext.
2012 * Otherwise, the header will not be translated.
2014 * align is either 'l' or 'r', and specifies the alignment for cells in this
2018 printTableAddHeader(printTableContent
*const content
, const char *header
,
2019 const bool translate
, const char align
)
2022 (void) translate
; /* unused parameter */
2025 if (content
->header
>= content
->headers
+ content
->ncolumns
)
2027 fprintf(stderr
, _("Cannot add header to table content: "
2028 "column count of %d exceeded.\n"),
2033 *content
->header
= (char *) mbvalidate((unsigned char *) header
,
2034 content
->opt
->encoding
);
2037 *content
->header
= _(*content
->header
);
2041 *content
->align
= align
;
2046 * Add a cell to the table.
2048 * Cells are not duplicated; you must ensure that the cell string is available
2049 * for the lifetime of the printTableContent struct.
2051 * If translate is true, the function will pass the cell through gettext.
2052 * Otherwise, the cell will not be translated.
2055 printTableAddCell(printTableContent
*const content
, const char *cell
,
2056 const bool translate
)
2059 (void) translate
; /* unused parameter */
2062 if (content
->cell
>= content
->cells
+ (content
->ncolumns
* content
->nrows
))
2064 fprintf(stderr
, _("Cannot add cell to table content: "
2065 "total cell count of %d exceeded.\n"),
2066 content
->ncolumns
* content
->nrows
);
2070 *content
->cell
= (char *) mbvalidate((unsigned char *) cell
,
2071 content
->opt
->encoding
);
2075 *content
->header
= _(*content
->header
);
2081 * Add a footer to the table.
2083 * Footers are added as elements of a singly-linked list, and the content is
2084 * strdup'd, so there is no need to keep the original footer string around.
2086 * Footers are never translated by the function. If you want the footer
2087 * translated you must do so yourself, before calling printTableAddFooter. The
2088 * reason this works differently to headers and cells is that footers tend to
2089 * be made of up individually translated components, rather than being
2090 * translated as a whole.
2093 printTableAddFooter(printTableContent
*const content
, const char *footer
)
2095 printTableFooter
*f
;
2097 f
= pg_local_calloc(1, sizeof(*f
));
2098 f
->data
= pg_strdup(footer
);
2100 if (content
->footers
== NULL
)
2101 content
->footers
= f
;
2103 content
->footer
->next
= f
;
2105 content
->footer
= f
;
2109 * Change the content of the last-added footer.
2111 * The current contents of the last-added footer are freed, and replaced by the
2112 * content given in *footer. If there was no previous footer, add a new one.
2114 * The content is strdup'd, so there is no need to keep the original string
2118 printTableSetFooter(printTableContent
*const content
, const char *footer
)
2120 if (content
->footers
!= NULL
)
2122 free(content
->footer
->data
);
2123 content
->footer
->data
= pg_strdup(footer
);
2126 printTableAddFooter(content
, footer
);
2130 * Free all memory allocated to this struct.
2132 * Once this has been called, the struct is unusable unless you pass it to
2133 * printTableInit() again.
2136 printTableCleanup(printTableContent
*const content
)
2138 free(content
->headers
);
2139 free(content
->cells
);
2140 free(content
->aligns
);
2142 content
->opt
= NULL
;
2143 content
->title
= NULL
;
2144 content
->headers
= NULL
;
2145 content
->cells
= NULL
;
2146 content
->aligns
= NULL
;
2147 content
->header
= NULL
;
2148 content
->cell
= NULL
;
2149 content
->align
= NULL
;
2151 if (content
->footers
)
2153 for (content
->footer
= content
->footers
; content
->footer
;)
2155 printTableFooter
*f
;
2157 f
= content
->footer
;
2158 content
->footer
= f
->next
;
2163 content
->footers
= NULL
;
2164 content
->footer
= NULL
;
2170 * Setup pager if required
2173 IsPagerNeeded(const printTableContent
*cont
, const int extra_lines
, FILE **fout
,
2176 if (*fout
== stdout
)
2180 if (cont
->opt
->expanded
)
2181 lines
= (cont
->ncolumns
+ 1) * cont
->nrows
;
2183 lines
= cont
->nrows
+ 1;
2185 if (!cont
->opt
->tuples_only
)
2187 printTableFooter
*f
;
2190 * FIXME -- this is slightly bogus: it counts the number of
2191 * footers, not the number of lines in them.
2193 for (f
= cont
->footers
; f
; f
= f
->next
)
2197 *fout
= PageOutput(lines
+ extra_lines
, cont
->opt
->pager
);
2198 *is_pager
= (*fout
!= stdout
);
2205 * Use this to print just any table in the supported formats.
2208 printTable(const printTableContent
*cont
, FILE *fout
, FILE *flog
)
2210 bool is_pager
= false;
2215 if (cont
->opt
->format
== PRINT_NOTHING
)
2218 /* print_aligned_text() handles the pager itself */
2219 if ((cont
->opt
->format
!= PRINT_ALIGNED
&&
2220 cont
->opt
->format
!= PRINT_WRAPPED
) ||
2221 cont
->opt
->expanded
)
2222 IsPagerNeeded(cont
, 0, &fout
, &is_pager
);
2224 /* print the stuff */
2227 print_aligned_text(cont
, flog
);
2229 switch (cont
->opt
->format
)
2231 case PRINT_UNALIGNED
:
2232 if (cont
->opt
->expanded
)
2233 print_unaligned_vertical(cont
, fout
);
2235 print_unaligned_text(cont
, fout
);
2239 if (cont
->opt
->expanded
)
2240 print_aligned_vertical(cont
, fout
);
2242 print_aligned_text(cont
, fout
);
2245 if (cont
->opt
->expanded
)
2246 print_html_vertical(cont
, fout
);
2248 print_html_text(cont
, fout
);
2251 if (cont
->opt
->expanded
)
2252 print_latex_vertical(cont
, fout
);
2254 print_latex_text(cont
, fout
);
2256 case PRINT_TROFF_MS
:
2257 if (cont
->opt
->expanded
)
2258 print_troff_ms_vertical(cont
, fout
);
2260 print_troff_ms_text(cont
, fout
);
2263 fprintf(stderr
, _("invalid fout format (internal error): %d"),
2273 * Use this to print query results
2275 * It calls printTable with all the things set straight.
2278 printQuery(const PGresult
*result
, const printQueryOpt
*opt
, FILE *fout
, FILE *flog
)
2280 printTableContent cont
;
2288 printTableInit(&cont
, &opt
->topt
, opt
->title
,
2289 PQnfields(result
), PQntuples(result
));
2291 for (i
= 0; i
< cont
.ncolumns
; i
++)
2294 Oid ftype
= PQftype(result
, i
);
2315 printTableAddHeader(&cont
, PQfname(result
, i
),
2316 opt
->translate_header
, align
);
2320 for (r
= 0; r
< cont
.nrows
; r
++)
2322 for (c
= 0; c
< cont
.ncolumns
; c
++)
2327 if (PQgetisnull(result
, r
, c
))
2328 cell
= opt
->nullPrint
? opt
->nullPrint
: "";
2330 cell
= PQgetvalue(result
, r
, c
);
2332 translate
= (opt
->translate_columns
&& opt
->translate_columns
[c
]);
2333 printTableAddCell(&cont
, cell
, translate
);
2342 for (footer
= opt
->footers
; *footer
; footer
++)
2343 printTableAddFooter(&cont
, *footer
);
2345 else if (!opt
->topt
.expanded
&& opt
->default_footer
)
2347 unsigned long total_records
;
2348 char default_footer
[100];
2350 total_records
= opt
->topt
.prior_records
+ cont
.nrows
;
2351 if (total_records
== 1)
2352 snprintf(default_footer
, 100, _("(1 row)"));
2354 snprintf(default_footer
, 100, _("(%lu rows)"), total_records
);
2356 printTableAddFooter(&cont
, default_footer
);
2359 printTable(&cont
, fout
, flog
);
2360 printTableCleanup(&cont
);
2365 setDecimalLocale(void)
2367 struct lconv
*extlconv
;
2369 extlconv
= localeconv();
2371 if (*extlconv
->decimal_point
)
2372 decimal_point
= pg_strdup(extlconv
->decimal_point
);
2374 decimal_point
= "."; /* SQL output standard */
2375 if (*extlconv
->grouping
&& atoi(extlconv
->grouping
) > 0)
2376 grouping
= pg_strdup(extlconv
->grouping
);
2378 grouping
= "3"; /* most common */
2380 /* similar code exists in formatting.c */
2381 if (*extlconv
->thousands_sep
)
2382 thousands_sep
= pg_strdup(extlconv
->thousands_sep
);
2383 /* Make sure thousands separator doesn't match decimal point symbol. */
2384 else if (strcmp(decimal_point
, ",") != 0)
2385 thousands_sep
= ",";
2387 thousands_sep
= ".";
2391 * Compute the byte distance to the end of the string or *target_width
2392 * display character positions, whichever comes first. Update *target_width
2393 * to be the number of display character positions actually filled.
2396 strlen_max_width(unsigned char *str
, int *target_width
, int encoding
)
2398 unsigned char *start
= str
;
2399 unsigned char *end
= str
+ strlen((char *) str
);
2404 int char_width
= PQdsplen((char *) str
, encoding
);
2407 * If the display width of the new character causes
2408 * the string to exceed its target width, skip it
2409 * and return. However, if this is the first character
2410 * of the string (curr_width == 0), we have to accept it.
2412 if (*target_width
< curr_width
+ char_width
&& curr_width
!= 0)
2415 curr_width
+= char_width
;
2417 str
+= PQmblen((char *) str
, encoding
);
2420 *target_width
= curr_width
;