2
* psql - the PostgreSQL interactive terminal
4
* Copyright (c) 2000-2011, 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;
47
/* Line style control structures */
48
const printTextFormat pg_asciiformat =
69
const printTextFormat pg_asciiformat_old =
90
const printTextFormat pg_utf8format =
94
/* ā, ā, ā¬, ā */
95
{"\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220"},
96
/* ā, ā, ā¼, ā¤ */
97
{"\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244"},
98
/* ā, ā, ā“, ā */
99
{"\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230"},
100
/* N/A, ā, ā, ā */
101
{"", "\342\224\202", "\342\224\202", "\342\224\202"}
123
/* Local functions */
124
static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
125
static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
126
FILE **fout, bool *is_pager);
130
pg_local_malloc(size_t size)
137
fprintf(stderr, _("out of memory\n"));
144
pg_local_calloc(int count, size_t size)
148
tmp = calloc(count, size);
151
fprintf(stderr, _("out of memory\n"));
158
integer_digits(const char *my_str)
162
if (my_str[0] == '-')
165
frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
167
return strlen(my_str) - frac_len;
170
/* Return additional length required for locale-aware numeric output */
172
additional_numeric_locale_len(const char *my_str)
174
int int_len = integer_digits(my_str),
176
int groupdigits = atoi(grouping);
179
/* Don't count a leading separator */
180
len = (int_len / groupdigits - (int_len % groupdigits == 0)) *
181
strlen(thousands_sep);
183
if (strchr(my_str, '.') != NULL)
184
len += strlen(decimal_point) - strlen(".");
190
strlen_with_numeric_locale(const char *my_str)
192
return strlen(my_str) + additional_numeric_locale_len(my_str);
196
* Returns the appropriately formatted string in a new allocated block,
200
format_numeric_locale(const char *my_str)
204
int_len = integer_digits(my_str),
206
int groupdigits = atoi(grouping);
207
int new_str_start = 0;
208
char *new_str = new_str = pg_local_malloc(
209
strlen_with_numeric_locale(my_str) + 1);
211
leading_digits = (int_len % groupdigits != 0) ?
212
int_len % groupdigits : groupdigits;
214
if (my_str[0] == '-') /* skip over sign, affects grouping
217
new_str[0] = my_str[0];
222
for (i = 0, j = new_str_start;; i++, j++)
224
/* Hit decimal point? */
225
if (my_str[i] == '.')
227
strcpy(&new_str[j], decimal_point);
228
j += strlen(decimal_point);
229
/* add fractional part */
230
strcpy(&new_str[j], &my_str[i] + 1);
235
if (my_str[i] == '\0')
242
if (i != 0 && (i - leading_digits) % groupdigits == 0)
244
strcpy(&new_str[j], thousands_sep);
245
j += strlen(thousands_sep);
248
new_str[j] = my_str[i];
256
* fputnbytes: print exactly N bytes to a file
258
* We avoid using %.*s here because it can misbehave if the data
259
* is not valid in what libc thinks is the prevailing encoding.
262
fputnbytes(FILE *f, const char *str, size_t n)
269
/*************************/
271
/*************************/
275
print_unaligned_text(const printTableContent *cont, FILE *fout)
277
const char *opt_fieldsep = cont->opt->fieldSep;
278
const char *opt_recordsep = cont->opt->recordSep;
279
bool opt_tuples_only = cont->opt->tuples_only;
281
const char *const * ptr;
282
bool need_recordsep = false;
292
if (cont->opt->start_table)
295
if (!opt_tuples_only && cont->title)
296
fprintf(fout, "%s%s", cont->title, opt_recordsep);
299
if (!opt_tuples_only)
301
for (ptr = cont->headers; *ptr; ptr++)
303
if (ptr != cont->headers)
304
fputs(opt_fieldsep, fout);
307
need_recordsep = true;
311
/* assume continuing printout */
312
need_recordsep = true;
315
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
319
fputs(opt_recordsep, fout);
320
need_recordsep = false;
326
if ((i + 1) % cont->ncolumns)
327
fputs(opt_fieldsep, fout);
329
need_recordsep = true;
333
if (cont->opt->stop_table)
335
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
339
for (f = cont->footers; f; f = f->next)
343
fputs(opt_recordsep, fout);
344
need_recordsep = false;
346
fputs(f->data, fout);
347
need_recordsep = true;
350
/* the last record needs to be concluded with a newline */
358
print_unaligned_vertical(const printTableContent *cont, FILE *fout)
360
const char *opt_fieldsep = cont->opt->fieldSep;
361
const char *opt_recordsep = cont->opt->recordSep;
362
bool opt_tuples_only = cont->opt->tuples_only;
364
const char *const * ptr;
365
bool need_recordsep = false;
375
if (cont->opt->start_table)
378
if (!opt_tuples_only && cont->title)
380
fputs(cont->title, fout);
381
need_recordsep = true;
385
/* assume continuing printout */
386
need_recordsep = true;
389
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
393
/* record separator is 2 occurrences of recordsep in this mode */
394
fputs(opt_recordsep, fout);
395
fputs(opt_recordsep, fout);
396
need_recordsep = false;
401
fputs(cont->headers[i % cont->ncolumns], fout);
402
fputs(opt_fieldsep, fout);
405
if ((i + 1) % cont->ncolumns)
406
fputs(opt_recordsep, fout);
408
need_recordsep = true;
411
if (cont->opt->stop_table)
414
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
418
fputs(opt_recordsep, fout);
419
for (f = cont->footers; f; f = f->next)
421
fputs(opt_recordsep, fout);
422
fputs(f->data, fout);
431
/********************/
433
/********************/
438
_print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths,
439
unsigned short border, printTextRule pos,
440
const printTextFormat *format,
443
const printTextLineFormat *lformat = &format->lrule[pos];
448
fputs(lformat->hrule, fout);
449
else if (border == 2)
450
fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
452
for (i = 0; i < ncolumns; i++)
454
for (j = 0; j < widths[i]; j++)
455
fputs(lformat->hrule, fout);
457
if (i < ncolumns - 1)
462
fprintf(fout, "%s%s%s", lformat->hrule,
463
lformat->midvrule, lformat->hrule);
468
fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
469
else if (border == 1)
470
fputs(lformat->hrule, fout);
477
* Print pretty boxes around cells.
480
print_aligned_text(const printTableContent *cont, FILE *fout)
482
bool opt_tuples_only = cont->opt->tuples_only;
483
int encoding = cont->opt->encoding;
484
unsigned short opt_border = cont->opt->border;
485
const printTextFormat *format = get_line_style(cont->opt);
486
const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
488
unsigned int col_count = 0,
494
unsigned int *width_header,
498
unsigned int *max_nl_lines, /* value split by newlines */
501
unsigned char **format_buf;
502
unsigned int width_total;
503
unsigned int total_header_width;
504
unsigned int extra_row_output_lines = 0;
505
unsigned int extra_output_lines = 0;
507
const char *const * ptr;
509
struct lineptr **col_lineptrs; /* pointers to line pointer per column */
511
bool *header_done; /* Have all header lines been output? */
512
int *bytes_output; /* Bytes output for column value */
513
printTextLineWrap *wrap; /* Wrap status for each column */
514
int output_columns = 0; /* Width of interactive console */
515
bool is_pager = false;
523
if (cont->ncolumns > 0)
525
col_count = cont->ncolumns;
526
width_header = pg_local_calloc(col_count, sizeof(*width_header));
527
width_average = pg_local_calloc(col_count, sizeof(*width_average));
528
max_width = pg_local_calloc(col_count, sizeof(*max_width));
529
width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
530
max_nl_lines = pg_local_calloc(col_count, sizeof(*max_nl_lines));
531
curr_nl_line = pg_local_calloc(col_count, sizeof(*curr_nl_line));
532
col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
533
max_bytes = pg_local_calloc(col_count, sizeof(*max_bytes));
534
format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
535
header_done = pg_local_calloc(col_count, sizeof(*header_done));
536
bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output));
537
wrap = pg_local_calloc(col_count, sizeof(*wrap));
542
width_average = NULL;
555
/* scan all column headers, find maximum width and max max_nl_lines */
556
for (i = 0; i < col_count; i++)
562
pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
563
encoding, &width, &nl_lines, &bytes_required);
564
if (width > max_width[i])
565
max_width[i] = width;
566
if (nl_lines > max_nl_lines[i])
567
max_nl_lines[i] = nl_lines;
568
if (bytes_required > max_bytes[i])
569
max_bytes[i] = bytes_required;
570
if (nl_lines > extra_row_output_lines)
571
extra_row_output_lines = nl_lines;
573
width_header[i] = width;
575
/* Add height of tallest header column */
576
extra_output_lines += extra_row_output_lines;
577
extra_row_output_lines = 0;
579
/* scan all cells, find maximum width, compute cell_count */
580
for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
586
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
587
&width, &nl_lines, &bytes_required);
589
if (width > max_width[i % col_count])
590
max_width[i % col_count] = width;
591
if (nl_lines > max_nl_lines[i % col_count])
592
max_nl_lines[i % col_count] = nl_lines;
593
if (bytes_required > max_bytes[i % col_count])
594
max_bytes[i % col_count] = bytes_required;
596
width_average[i % col_count] += width;
599
/* If we have rows, compute average */
600
if (col_count != 0 && cell_count != 0)
602
int rows = cell_count / col_count;
604
for (i = 0; i < col_count; i++)
605
width_average[i] /= rows;
608
/* adjust the total display width based on border style */
610
width_total = col_count;
611
else if (opt_border == 1)
612
width_total = col_count * 3 - 1;
614
width_total = col_count * 3 + 1;
615
total_header_width = width_total;
617
for (i = 0; i < col_count; i++)
619
width_total += max_width[i];
620
total_header_width += width_header[i];
624
* At this point: max_width[] contains the max width of each column,
625
* max_nl_lines[] contains the max number of lines in each column,
626
* max_bytes[] contains the maximum storage space for formatting strings,
627
* width_total contains the giant width sum. Now we allocate some memory
630
for (i = 0; i < col_count; i++)
632
/* Add entry for ptr == NULL array termination */
633
col_lineptrs[i] = pg_local_calloc(max_nl_lines[i] + 1,
634
sizeof(**col_lineptrs));
636
format_buf[i] = pg_local_malloc(max_bytes[i] + 1);
638
col_lineptrs[i]->ptr = format_buf[i];
641
/* Default word wrap to the full width, i.e. no word wrap */
642
for (i = 0; i < col_count; i++)
643
width_wrap[i] = max_width[i];
646
* Choose target output width: \pset columns, or $COLUMNS, or ioctl
648
if (cont->opt->columns > 0)
649
output_columns = cont->opt->columns;
650
else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
652
if (cont->opt->env_columns > 0)
653
output_columns = cont->opt->env_columns;
657
struct winsize screen_size;
659
if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
660
output_columns = screen_size.ws_col;
665
if (cont->opt->format == PRINT_WRAPPED)
668
* Optional optimized word wrap. Shrink columns with a high max/avg
669
* ratio. Slighly bias against wider columns. (Increases chance a
670
* narrow column will fit in its cell.) If available columns is
671
* positive... and greater than the width of the unshrinkable column
674
if (output_columns > 0 && output_columns >= total_header_width)
676
/* While there is still excess width... */
677
while (width_total > output_columns)
679
double max_ratio = 0;
683
* Find column that has the highest ratio of its maximum width
684
* compared to its average width. This tells us which column
685
* will produce the fewest wrapped values if shortened.
686
* width_wrap starts as equal to max_width.
688
for (i = 0; i < col_count; i++)
690
if (width_average[i] && width_wrap[i] > width_header[i])
692
/* Penalize wide columns by 1% of their width */
695
ratio = (double) width_wrap[i] / width_average[i] +
697
if (ratio > max_ratio)
705
/* Exit loop if we can't squeeze any more. */
709
/* Decrease width of target column by one. */
710
width_wrap[worst_col]--;
716
/* If we wrapped beyond the display width, use the pager */
717
if (!is_pager && fout == stdout && output_columns > 0 &&
718
(output_columns < total_header_width || output_columns < width_total))
720
fout = PageOutput(INT_MAX, cont->opt->pager); /* force pager */
724
/* Check if newlines or our wrapping now need the pager */
727
/* scan all cells, find maximum width, compute cell_count */
728
for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
734
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
735
&width, &nl_lines, &bytes_required);
738
* A row can have both wrapping and newlines that cause it to
739
* display across multiple lines. We check for both cases below.
741
if (width > 0 && width_wrap[i])
743
unsigned int extra_lines;
745
extra_lines = (width - 1) / width_wrap[i] + nl_lines;
746
if (extra_lines > extra_row_output_lines)
747
extra_row_output_lines = extra_lines;
750
/* i is the current column number: increment with wrap */
751
if (++i >= col_count)
754
/* At last column of each row, add tallest column height */
755
extra_output_lines += extra_row_output_lines;
756
extra_row_output_lines = 0;
759
IsPagerNeeded(cont, extra_output_lines, &fout, &is_pager);
763
if (cont->opt->start_table)
766
if (cont->title && !opt_tuples_only)
771
pg_wcssize((unsigned char *) cont->title, strlen(cont->title),
772
encoding, &width, &height, NULL);
773
if (width >= width_total)
775
fprintf(fout, "%s\n", cont->title);
778
fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
783
if (!opt_tuples_only)
785
int more_col_wrapping;
789
_print_horizontal_line(col_count, width_wrap, opt_border,
790
PRINT_RULE_TOP, format, fout);
792
for (i = 0; i < col_count; i++)
793
pg_wcsformat((unsigned char *) cont->headers[i],
794
strlen(cont->headers[i]), encoding,
795
col_lineptrs[i], max_nl_lines[i]);
797
more_col_wrapping = col_count;
799
memset(header_done, false, col_count * sizeof(bool));
800
while (more_col_wrapping)
803
fputs(dformat->leftvrule, fout);
805
for (i = 0; i < cont->ncolumns; i++)
807
struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
808
unsigned int nbspace;
810
if (opt_border != 0 ||
811
(!format->wrap_right_border && i > 0))
812
fputs(curr_nl_line ? format->header_nl_left : " ",
817
nbspace = width_wrap[i] - this_line->width;
820
fprintf(fout, "%-*s%s%-*s",
821
nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
823
if (!(this_line + 1)->ptr)
830
fprintf(fout, "%*s", width_wrap[i], "");
832
if (opt_border != 0 || format->wrap_right_border)
833
fputs(!header_done[i] ? format->header_nl_right : " ",
836
if (opt_border != 0 && i < col_count - 1)
837
fputs(dformat->midvrule, fout);
842
fputs(dformat->rightvrule, fout);
846
_print_horizontal_line(col_count, width_wrap, opt_border,
847
PRINT_RULE_MIDDLE, format, fout);
851
/* print cells, one loop per row */
852
for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
862
for (j = 0; j < col_count; j++)
864
pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding,
865
col_lineptrs[j], max_nl_lines[j]);
869
memset(bytes_output, 0, col_count * sizeof(int));
872
* Each time through this loop, one display line is output. It can
873
* either be a full value or a partial value if embedded newlines
874
* exist or if 'format=wrapping' mode is enabled.
882
fputs(dformat->leftvrule, fout);
884
/* for each column */
885
for (j = 0; j < col_count; j++)
887
/* We have a valid array element, so index it */
888
struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
890
int chars_to_output = width_wrap[j];
891
bool finalspaces = (opt_border == 2 || j < col_count - 1);
893
/* Print left-hand wrap or newline mark */
896
if (wrap[j] == PRINT_LINE_WRAP_WRAP)
897
fputs(format->wrap_left, fout);
898
else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
899
fputs(format->nl_left, fout);
906
/* Past newline lines so just pad for other columns */
908
fprintf(fout, "%*s", chars_to_output, "");
912
/* Get strlen() of the characters up to width_wrap */
914
strlen_max_width(this_line->ptr + bytes_output[j],
915
&chars_to_output, encoding);
918
* If we exceeded width_wrap, it means the display width
919
* of a single character was wider than our target width.
920
* In that case, we have to pretend we are only printing
921
* the target display width and make the best of it.
923
if (chars_to_output > width_wrap[j])
924
chars_to_output = width_wrap[j];
926
if (cont->aligns[j] == 'r') /* Right aligned cell */
929
fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
931
(char *) (this_line->ptr + bytes_output[j]),
934
else /* Left aligned cell */
938
(char *) (this_line->ptr + bytes_output[j]),
942
bytes_output[j] += bytes_to_output;
944
/* Do we have more text to wrap? */
945
if (*(this_line->ptr + bytes_output[j]) != '\0')
949
/* Advance to next newline line */
951
if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
957
/* Determine next line's wrap status for this column */
958
wrap[j] = PRINT_LINE_WRAP_NONE;
959
if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
961
if (bytes_output[j] != 0)
962
wrap[j] = PRINT_LINE_WRAP_WRAP;
963
else if (curr_nl_line[j] != 0)
964
wrap[j] = PRINT_LINE_WRAP_NEWLINE;
968
* If left-aligned, pad out remaining space if needed (not
969
* last column, and/or wrap marks required).
971
if (cont->aligns[j] != 'r') /* Left aligned cell */
974
wrap[j] == PRINT_LINE_WRAP_WRAP ||
975
wrap[j] == PRINT_LINE_WRAP_NEWLINE)
977
width_wrap[j] - chars_to_output, "");
980
/* Print right-hand wrap or newline mark */
981
if (wrap[j] == PRINT_LINE_WRAP_WRAP)
982
fputs(format->wrap_right, fout);
983
else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
984
fputs(format->nl_right, fout);
985
else if (opt_border == 2 || j < col_count - 1)
988
/* Print column divider, if not the last column */
989
if (opt_border != 0 && j < col_count - 1)
991
if (wrap[j + 1] == PRINT_LINE_WRAP_WRAP)
992
fputs(format->midvrule_wrap, fout);
993
else if (wrap[j + 1] == PRINT_LINE_WRAP_NEWLINE)
994
fputs(format->midvrule_nl, fout);
995
else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL)
996
fputs(format->midvrule_blank, fout);
998
fputs(dformat->midvrule, fout);
1002
/* end-of-row border */
1003
if (opt_border == 2)
1004
fputs(dformat->rightvrule, fout);
1007
} while (more_lines);
1010
if (cont->opt->stop_table)
1012
if (opt_border == 2 && !cancel_pressed)
1013
_print_horizontal_line(col_count, width_wrap, opt_border,
1014
PRINT_RULE_BOTTOM, format, fout);
1017
if (cont->footers && !opt_tuples_only && !cancel_pressed)
1019
printTableFooter *f;
1021
for (f = cont->footers; f; f = f->next)
1022
fprintf(fout, "%s\n", f->data);
1029
for (i = 0; i < col_count; i++)
1031
free(col_lineptrs[i]);
1032
free(format_buf[i]);
1035
free(width_average);
1053
print_aligned_vertical_line(const printTableContent *cont,
1054
unsigned long record,
1055
unsigned int hwidth,
1056
unsigned int dwidth,
1060
const printTextFormat *format = get_line_style(cont->opt);
1061
const printTextLineFormat *lformat = &format->lrule[pos];
1062
unsigned short opt_border = cont->opt->border;
1066
if (opt_border == 2)
1067
fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
1068
else if (opt_border == 1)
1069
fputs(lformat->hrule, fout);
1073
if (opt_border == 0)
1074
reclen = fprintf(fout, "* Record %lu", record);
1076
reclen = fprintf(fout, "[ RECORD %lu ]", record);
1078
if (opt_border != 2)
1082
for (i = reclen; i < hwidth; i++)
1083
fputs(opt_border > 0 ? lformat->hrule : " ", fout);
1089
fputs(lformat->hrule, fout);
1091
fputs(lformat->midvrule, fout);
1093
fputs(lformat->hrule, fout);
1102
for (i = reclen; i < dwidth; i++)
1103
fputs(opt_border > 0 ? lformat->hrule : " ", fout);
1104
if (opt_border == 2)
1105
fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
1110
print_aligned_vertical(const printTableContent *cont, FILE *fout)
1112
bool opt_tuples_only = cont->opt->tuples_only;
1113
unsigned short opt_border = cont->opt->border;
1114
const printTextFormat *format = get_line_style(cont->opt);
1115
const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
1116
int encoding = cont->opt->encoding;
1117
unsigned long record = cont->opt->prior_records + 1;
1118
const char *const * ptr;
1126
struct lineptr *hlineptr,
1135
if (cont->cells[0] == NULL && cont->opt->start_table &&
1136
cont->opt->stop_table)
1138
fprintf(fout, _("(No rows)\n"));
1142
/* Find the maximum dimensions for the headers */
1143
for (i = 0; i < cont->ncolumns; i++)
1149
pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
1150
encoding, &width, &height, &fs);
1153
if (height > hheight)
1155
if (fs > hformatsize)
1159
/* find longest data cell */
1160
for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
1166
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
1167
&width, &height, &fs);
1170
if (height > dheight)
1172
if (fs > dformatsize)
1177
* We now have all the information we need to setup the formatting
1180
dlineptr = pg_local_malloc((sizeof(*dlineptr) + 1) * dheight);
1181
hlineptr = pg_local_malloc((sizeof(*hlineptr) + 1) * hheight);
1183
dlineptr->ptr = pg_local_malloc(dformatsize);
1184
hlineptr->ptr = pg_local_malloc(hformatsize);
1186
if (cont->opt->start_table)
1189
if (!opt_tuples_only && cont->title)
1190
fprintf(fout, "%s\n", cont->title);
1194
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1205
pos = PRINT_RULE_TOP;
1206
else if (!(*(ptr + 1)))
1207
pos = PRINT_RULE_BOTTOM;
1209
pos = PRINT_RULE_MIDDLE;
1211
if (i % cont->ncolumns == 0)
1213
if (!opt_tuples_only)
1214
print_aligned_vertical_line(cont, record++, hwidth, dwidth,
1216
else if (i != 0 || !cont->opt->start_table || opt_border == 2)
1217
print_aligned_vertical_line(cont, 0, hwidth, dwidth,
1221
/* Format the header */
1222
pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns],
1223
strlen(cont->headers[i % cont->ncolumns]),
1224
encoding, hlineptr, hheight);
1225
/* Format the data */
1226
pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding,
1230
dcomplete = hcomplete = 0;
1231
while (!dcomplete || !hcomplete)
1233
if (opt_border == 2)
1234
fprintf(fout, "%s ", dformat->leftvrule);
1237
fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
1238
hwidth - hlineptr[line_count].width, "");
1240
if (!hlineptr[line_count + 1].ptr)
1244
fprintf(fout, "%*s", hwidth, "");
1247
fprintf(fout, " %s ", dformat->midvrule);
1254
fprintf(fout, "%s\n", dlineptr[line_count].ptr);
1256
fprintf(fout, "%-s%*s %s\n", dlineptr[line_count].ptr,
1257
dwidth - dlineptr[line_count].width, "",
1258
dformat->rightvrule);
1260
if (!dlineptr[line_count + 1].ptr)
1268
fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule);
1274
if (cont->opt->stop_table)
1276
if (opt_border == 2 && !cancel_pressed)
1277
print_aligned_vertical_line(cont, 0, hwidth, dwidth,
1278
PRINT_RULE_BOTTOM, fout);
1281
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
1283
printTableFooter *f;
1287
for (f = cont->footers; f; f = f->next)
1288
fprintf(fout, "%s\n", f->data);
1294
free(hlineptr->ptr);
1295
free(dlineptr->ptr);
1301
/**********************/
1302
/* HTML printing ******/
1303
/**********************/
1307
html_escaped_print(const char *in, FILE *fout)
1310
bool leading_space = true;
1312
for (p = in; *p; p++)
1317
fputs("&", fout);
1320
fputs("<", fout);
1323
fputs(">", fout);
1326
fputs("<br />\n", fout);
1329
fputs(""", fout);
1332
/* protect leading space, for EXPLAIN output */
1334
fputs(" ", fout);
1342
leading_space = false;
1348
print_html_text(const printTableContent *cont, FILE *fout)
1350
bool opt_tuples_only = cont->opt->tuples_only;
1351
unsigned short opt_border = cont->opt->border;
1352
const char *opt_table_attr = cont->opt->tableAttr;
1354
const char *const * ptr;
1359
if (cont->opt->start_table)
1361
fprintf(fout, "<table border=\"%d\"", opt_border);
1363
fprintf(fout, " %s", opt_table_attr);
1367
if (!opt_tuples_only && cont->title)
1369
fputs(" <caption>", fout);
1370
html_escaped_print(cont->title, fout);
1371
fputs("</caption>\n", fout);
1375
if (!opt_tuples_only)
1377
fputs(" <tr>\n", fout);
1378
for (ptr = cont->headers; *ptr; ptr++)
1380
fputs(" <th align=\"center\">", fout);
1381
html_escaped_print(*ptr, fout);
1382
fputs("</th>\n", fout);
1384
fputs(" </tr>\n", fout);
1389
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1391
if (i % cont->ncolumns == 0)
1395
fputs(" <tr valign=\"top\">\n", fout);
1398
fprintf(fout, " <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
1399
/* is string only whitespace? */
1400
if ((*ptr)[strspn(*ptr, " \t")] == '\0')
1401
fputs(" ", fout);
1403
html_escaped_print(*ptr, fout);
1405
fputs("</td>\n", fout);
1407
if ((i + 1) % cont->ncolumns == 0)
1408
fputs(" </tr>\n", fout);
1411
if (cont->opt->stop_table)
1413
fputs("</table>\n", fout);
1416
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
1418
printTableFooter *f;
1421
for (f = cont->footers; f; f = f->next)
1423
html_escaped_print(f->data, fout);
1424
fputs("<br />\n", fout);
1426
fputs("</p>", fout);
1435
print_html_vertical(const printTableContent *cont, FILE *fout)
1437
bool opt_tuples_only = cont->opt->tuples_only;
1438
unsigned short opt_border = cont->opt->border;
1439
const char *opt_table_attr = cont->opt->tableAttr;
1440
unsigned long record = cont->opt->prior_records + 1;
1442
const char *const * ptr;
1447
if (cont->opt->start_table)
1449
fprintf(fout, "<table border=\"%d\"", opt_border);
1451
fprintf(fout, " %s", opt_table_attr);
1455
if (!opt_tuples_only && cont->title)
1457
fputs(" <caption>", fout);
1458
html_escaped_print(cont->title, fout);
1459
fputs("</caption>\n", fout);
1464
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1466
if (i % cont->ncolumns == 0)
1470
if (!opt_tuples_only)
1472
"\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
1475
fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout);
1477
fputs(" <tr valign=\"top\">\n"
1479
html_escaped_print(cont->headers[i % cont->ncolumns], fout);
1480
fputs("</th>\n", fout);
1482
fprintf(fout, " <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
1483
/* is string only whitespace? */
1484
if ((*ptr)[strspn(*ptr, " \t")] == '\0')
1485
fputs(" ", fout);
1487
html_escaped_print(*ptr, fout);
1489
fputs("</td>\n </tr>\n", fout);
1492
if (cont->opt->stop_table)
1494
fputs("</table>\n", fout);
1497
if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
1499
printTableFooter *f;
1502
for (f = cont->footers; f; f = f->next)
1504
html_escaped_print(f->data, fout);
1505
fputs("<br />\n", fout);
1507
fputs("</p>", fout);
1515
/*************************/
1517
/*************************/
1521
latex_escaped_print(const char *in, FILE *fout)
1525
for (p = in; *p; p++)
1547
fputs("\\backslash", fout);
1550
fputs("\\\\", fout);
1559
print_latex_text(const printTableContent *cont, FILE *fout)
1561
bool opt_tuples_only = cont->opt->tuples_only;
1562
unsigned short opt_border = cont->opt->border;
1564
const char *const * ptr;
1572
if (cont->opt->start_table)
1575
if (!opt_tuples_only && cont->title)
1577
fputs("\\begin{center}\n", fout);
1578
latex_escaped_print(cont->title, fout);
1579
fputs("\n\\end{center}\n\n", fout);
1582
/* begin environment and set alignments and borders */
1583
fputs("\\begin{tabular}{", fout);
1585
if (opt_border == 2)
1587
for (i = 0; i < cont->ncolumns; i++)
1589
fputc(*(cont->aligns + i), fout);
1590
if (opt_border != 0 && i < cont->ncolumns - 1)
1593
if (opt_border == 2)
1598
if (!opt_tuples_only && opt_border == 2)
1599
fputs("\\hline\n", fout);
1602
if (!opt_tuples_only)
1604
for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
1608
fputs("\\textit{", fout);
1609
latex_escaped_print(*ptr, fout);
1612
fputs(" \\\\\n", fout);
1613
fputs("\\hline\n", fout);
1618
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1620
latex_escaped_print(*ptr, fout);
1622
if ((i + 1) % cont->ncolumns == 0)
1624
fputs(" \\\\\n", fout);
1632
if (cont->opt->stop_table)
1634
if (opt_border == 2)
1635
fputs("\\hline\n", fout);
1637
fputs("\\end{tabular}\n\n\\noindent ", fout);
1640
if (cont->footers && !opt_tuples_only && !cancel_pressed)
1642
printTableFooter *f;
1644
for (f = cont->footers; f; f = f->next)
1646
latex_escaped_print(f->data, fout);
1647
fputs(" \\\\\n", fout);
1657
print_latex_vertical(const printTableContent *cont, FILE *fout)
1659
bool opt_tuples_only = cont->opt->tuples_only;
1660
unsigned short opt_border = cont->opt->border;
1661
unsigned long record = cont->opt->prior_records + 1;
1663
const char *const * ptr;
1671
if (cont->opt->start_table)
1674
if (!opt_tuples_only && cont->title)
1676
fputs("\\begin{center}\n", fout);
1677
latex_escaped_print(cont->title, fout);
1678
fputs("\n\\end{center}\n\n", fout);
1681
/* begin environment and set alignments and borders */
1682
fputs("\\begin{tabular}{", fout);
1683
if (opt_border == 0)
1685
else if (opt_border == 1)
1687
else if (opt_border == 2)
1688
fputs("|c|l|", fout);
1693
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1696
if (i % cont->ncolumns == 0)
1700
if (!opt_tuples_only)
1702
if (opt_border == 2)
1704
fputs("\\hline\n", fout);
1705
fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
1708
fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
1710
if (opt_border >= 1)
1711
fputs("\\hline\n", fout);
1714
latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
1716
latex_escaped_print(*ptr, fout);
1717
fputs(" \\\\\n", fout);
1720
if (cont->opt->stop_table)
1722
if (opt_border == 2)
1723
fputs("\\hline\n", fout);
1725
fputs("\\end{tabular}\n\n\\noindent ", fout);
1728
if (cont->footers && !opt_tuples_only && !cancel_pressed)
1730
printTableFooter *f;
1732
for (f = cont->footers; f; f = f->next)
1734
latex_escaped_print(f->data, fout);
1735
fputs(" \\\\\n", fout);
1744
/*************************/
1746
/*************************/
1750
troff_ms_escaped_print(const char *in, FILE *fout)
1754
for (p = in; *p; p++)
1758
fputs("\\(rs", fout);
1767
print_troff_ms_text(const printTableContent *cont, FILE *fout)
1769
bool opt_tuples_only = cont->opt->tuples_only;
1770
unsigned short opt_border = cont->opt->border;
1772
const char *const * ptr;
1780
if (cont->opt->start_table)
1783
if (!opt_tuples_only && cont->title)
1785
fputs(".LP\n.DS C\n", fout);
1786
troff_ms_escaped_print(cont->title, fout);
1787
fputs("\n.DE\n", fout);
1790
/* begin environment and set alignments and borders */
1791
fputs(".LP\n.TS\n", fout);
1792
if (opt_border == 2)
1793
fputs("center box;\n", fout);
1795
fputs("center;\n", fout);
1797
for (i = 0; i < cont->ncolumns; i++)
1799
fputc(*(cont->aligns + i), fout);
1800
if (opt_border > 0 && i < cont->ncolumns - 1)
1806
if (!opt_tuples_only)
1808
for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
1812
fputs("\\fI", fout);
1813
troff_ms_escaped_print(*ptr, fout);
1814
fputs("\\fP", fout);
1816
fputs("\n_\n", fout);
1821
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1823
troff_ms_escaped_print(*ptr, fout);
1825
if ((i + 1) % cont->ncolumns == 0)
1835
if (cont->opt->stop_table)
1837
fputs(".TE\n.DS L\n", fout);
1840
if (cont->footers && !opt_tuples_only && !cancel_pressed)
1842
printTableFooter *f;
1844
for (f = cont->footers; f; f = f->next)
1846
troff_ms_escaped_print(f->data, fout);
1851
fputs(".DE\n", fout);
1857
print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
1859
bool opt_tuples_only = cont->opt->tuples_only;
1860
unsigned short opt_border = cont->opt->border;
1861
unsigned long record = cont->opt->prior_records + 1;
1863
const char *const * ptr;
1864
unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
1872
if (cont->opt->start_table)
1875
if (!opt_tuples_only && cont->title)
1877
fputs(".LP\n.DS C\n", fout);
1878
troff_ms_escaped_print(cont->title, fout);
1879
fputs("\n.DE\n", fout);
1882
/* begin environment and set alignments and borders */
1883
fputs(".LP\n.TS\n", fout);
1884
if (opt_border == 2)
1885
fputs("center box;\n", fout);
1887
fputs("center;\n", fout);
1890
if (opt_tuples_only)
1891
fputs("c l;\n", fout);
1894
current_format = 2; /* assume tuples printed already */
1897
for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
1900
if (i % cont->ncolumns == 0)
1904
if (!opt_tuples_only)
1906
if (current_format != 1)
1908
if (opt_border == 2 && record > 1)
1910
if (current_format != 0)
1911
fputs(".T&\n", fout);
1912
fputs("c s.\n", fout);
1915
fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
1917
if (opt_border >= 1)
1921
if (!opt_tuples_only)
1923
if (current_format != 2)
1925
if (current_format != 0)
1926
fputs(".T&\n", fout);
1927
if (opt_border != 1)
1928
fputs("c l.\n", fout);
1930
fputs("c | l.\n", fout);
1935
troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
1937
troff_ms_escaped_print(*ptr, fout);
1942
if (cont->opt->stop_table)
1944
fputs(".TE\n.DS L\n", fout);
1947
if (cont->footers && !opt_tuples_only && !cancel_pressed)
1949
printTableFooter *f;
1951
for (f = cont->footers; f; f = f->next)
1953
troff_ms_escaped_print(f->data, fout);
1958
fputs(".DE\n", fout);
1963
/********************************/
1964
/* Public functions */
1965
/********************************/
1971
* Tests if pager is needed and returns appropriate FILE pointer.
1974
PageOutput(int lines, unsigned short int pager)
1976
/* check whether we need / can / are supposed to use pager */
1977
if (pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
1979
const char *pagerprog;
1984
struct winsize screen_size;
1986
result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
1988
/* >= accounts for a one-line prompt */
1989
if (result == -1 || lines >= screen_size.ws_row || pager > 1)
1992
pagerprog = getenv("PAGER");
1994
pagerprog = DEFAULT_PAGER;
1996
pqsignal(SIGPIPE, SIG_IGN);
1998
pagerpipe = popen(pagerprog, "w");
2012
* Close previously opened pager pipe, if any
2015
ClosePager(FILE *pagerpipe)
2017
if (pagerpipe && pagerpipe != stdout)
2020
* If printing was canceled midstream, warn about it.
2022
* Some pagers like less use Ctrl-C as part of their command set. Even
2023
* so, we abort our processing and warn the user what we did. If the
2024
* pager quit as a result of the SIGINT, this message won't go
2028
fprintf(pagerpipe, _("Interrupted\n"));
2032
pqsignal(SIGPIPE, SIG_DFL);
2038
* Initialise a table contents struct.
2039
* Must be called before any other printTable method is used.
2041
* The title is not duplicated; the caller must ensure that the buffer
2042
* is available for the lifetime of the printTableContent struct.
2044
* If you call this, you must call printTableCleanup once you're done with the
2048
printTableInit(printTableContent *const content, const printTableOpt *opt,
2049
const char *title, const int ncolumns, const int nrows)
2052
content->title = title;
2053
content->ncolumns = ncolumns;
2054
content->nrows = nrows;
2056
content->headers = pg_local_calloc(ncolumns + 1,
2057
sizeof(*content->headers));
2059
content->cells = pg_local_calloc(ncolumns * nrows + 1,
2060
sizeof(*content->cells));
2062
content->cellmustfree = NULL;
2063
content->footers = NULL;
2065
content->aligns = pg_local_calloc(ncolumns + 1,
2066
sizeof(*content->align));
2068
content->header = content->headers;
2069
content->cell = content->cells;
2070
content->footer = content->footers;
2071
content->align = content->aligns;
2072
content->cellsadded = 0;
2076
* Add a header to the table.
2078
* Headers are not duplicated; you must ensure that the header string is
2079
* available for the lifetime of the printTableContent struct.
2081
* If translate is true, the function will pass the header through gettext.
2082
* Otherwise, the header will not be translated.
2084
* align is either 'l' or 'r', and specifies the alignment for cells in this
2088
printTableAddHeader(printTableContent *const content, const char *header,
2089
const bool translate, const char align)
2092
(void) translate; /* unused parameter */
2095
if (content->header >= content->headers + content->ncolumns)
2097
fprintf(stderr, _("Cannot add header to table content: "
2098
"column count of %d exceeded.\n"),
2103
*content->header = (char *) mbvalidate((unsigned char *) header,
2104
content->opt->encoding);
2107
*content->header = _(*content->header);
2111
*content->align = align;
2116
* Add a cell to the table.
2118
* Cells are not duplicated; you must ensure that the cell string is available
2119
* for the lifetime of the printTableContent struct.
2121
* If translate is true, the function will pass the cell through gettext.
2122
* Otherwise, the cell will not be translated.
2124
* If mustfree is true, the cell string is freed by printTableCleanup().
2125
* Note: Automatic freeing of translatable strings is not supported.
2128
printTableAddCell(printTableContent *const content, const char *cell,
2129
const bool translate, const bool mustfree)
2132
(void) translate; /* unused parameter */
2135
if (content->cellsadded >= content->ncolumns * content->nrows)
2137
fprintf(stderr, _("Cannot add cell to table content: "
2138
"total cell count of %d exceeded.\n"),
2139
content->ncolumns * content->nrows);
2143
*content->cell = (char *) mbvalidate((unsigned char *) cell,
2144
content->opt->encoding);
2148
*content->cell = _(*content->cell);
2153
if (content->cellmustfree == NULL)
2154
content->cellmustfree = pg_local_calloc(
2155
content->ncolumns * content->nrows + 1, sizeof(bool));
2157
content->cellmustfree[content->cellsadded] = true;
2160
content->cellsadded++;
2164
* Add a footer to the table.
2166
* Footers are added as elements of a singly-linked list, and the content is
2167
* strdup'd, so there is no need to keep the original footer string around.
2169
* Footers are never translated by the function. If you want the footer
2170
* translated you must do so yourself, before calling printTableAddFooter. The
2171
* reason this works differently to headers and cells is that footers tend to
2172
* be made of up individually translated components, rather than being
2173
* translated as a whole.
2176
printTableAddFooter(printTableContent *const content, const char *footer)
2178
printTableFooter *f;
2180
f = pg_local_calloc(1, sizeof(*f));
2181
f->data = pg_strdup(footer);
2183
if (content->footers == NULL)
2184
content->footers = f;
2186
content->footer->next = f;
2188
content->footer = f;
2192
* Change the content of the last-added footer.
2194
* The current contents of the last-added footer are freed, and replaced by the
2195
* content given in *footer. If there was no previous footer, add a new one.
2197
* The content is strdup'd, so there is no need to keep the original string
2201
printTableSetFooter(printTableContent *const content, const char *footer)
2203
if (content->footers != NULL)
2205
free(content->footer->data);
2206
content->footer->data = pg_strdup(footer);
2209
printTableAddFooter(content, footer);
2213
* Free all memory allocated to this struct.
2215
* Once this has been called, the struct is unusable unless you pass it to
2216
* printTableInit() again.
2219
printTableCleanup(printTableContent *const content)
2221
if (content->cellmustfree)
2225
for (i = 0; i < content->nrows * content->ncolumns; i++)
2227
if (content->cellmustfree[i])
2228
free((char *) content->cells[i]);
2230
free(content->cellmustfree);
2231
content->cellmustfree = NULL;
2233
free(content->headers);
2234
free(content->cells);
2235
free(content->aligns);
2237
content->opt = NULL;
2238
content->title = NULL;
2239
content->headers = NULL;
2240
content->cells = NULL;
2241
content->aligns = NULL;
2242
content->header = NULL;
2243
content->cell = NULL;
2244
content->align = NULL;
2246
if (content->footers)
2248
for (content->footer = content->footers; content->footer;)
2250
printTableFooter *f;
2252
f = content->footer;
2253
content->footer = f->next;
2258
content->footers = NULL;
2259
content->footer = NULL;
2265
* Setup pager if required
2268
IsPagerNeeded(const printTableContent *cont, const int extra_lines, FILE **fout,
2271
if (*fout == stdout)
2275
if (cont->opt->expanded)
2276
lines = (cont->ncolumns + 1) * cont->nrows;
2278
lines = cont->nrows + 1;
2280
if (!cont->opt->tuples_only)
2282
printTableFooter *f;
2285
* FIXME -- this is slightly bogus: it counts the number of
2286
* footers, not the number of lines in them.
2288
for (f = cont->footers; f; f = f->next)
2292
*fout = PageOutput(lines + extra_lines, cont->opt->pager);
2293
*is_pager = (*fout != stdout);
2300
* Use this to print just any table in the supported formats.
2303
printTable(const printTableContent *cont, FILE *fout, FILE *flog)
2305
bool is_pager = false;
2310
if (cont->opt->format == PRINT_NOTHING)
2313
/* print_aligned_text() handles the pager itself */
2314
if ((cont->opt->format != PRINT_ALIGNED &&
2315
cont->opt->format != PRINT_WRAPPED) ||
2316
cont->opt->expanded)
2317
IsPagerNeeded(cont, 0, &fout, &is_pager);
2319
/* print the stuff */
2322
print_aligned_text(cont, flog);
2324
switch (cont->opt->format)
2326
case PRINT_UNALIGNED:
2327
if (cont->opt->expanded)
2328
print_unaligned_vertical(cont, fout);
2330
print_unaligned_text(cont, fout);
2334
if (cont->opt->expanded)
2335
print_aligned_vertical(cont, fout);
2337
print_aligned_text(cont, fout);
2340
if (cont->opt->expanded)
2341
print_html_vertical(cont, fout);
2343
print_html_text(cont, fout);
2346
if (cont->opt->expanded)
2347
print_latex_vertical(cont, fout);
2349
print_latex_text(cont, fout);
2351
case PRINT_TROFF_MS:
2352
if (cont->opt->expanded)
2353
print_troff_ms_vertical(cont, fout);
2355
print_troff_ms_text(cont, fout);
2358
fprintf(stderr, _("invalid output format (internal error): %d"),
2368
* Use this to print query results
2370
* It calls printTable with all the things set straight.
2373
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
2375
printTableContent cont;
2383
printTableInit(&cont, &opt->topt, opt->title,
2384
PQnfields(result), PQntuples(result));
2386
for (i = 0; i < cont.ncolumns; i++)
2389
Oid ftype = PQftype(result, i);
2410
printTableAddHeader(&cont, PQfname(result, i),
2411
opt->translate_header, align);
2415
for (r = 0; r < cont.nrows; r++)
2417
for (c = 0; c < cont.ncolumns; c++)
2420
bool mustfree = false;
2423
if (PQgetisnull(result, r, c))
2424
cell = opt->nullPrint ? opt->nullPrint : "";
2427
cell = PQgetvalue(result, r, c);
2428
if (cont.aligns[c] == 'r' && opt->topt.numericLocale)
2430
cell = format_numeric_locale(cell);
2435
translate = (opt->translate_columns && opt->translate_columns[c]);
2436
printTableAddCell(&cont, cell, translate, mustfree);
2445
for (footer = opt->footers; *footer; footer++)
2446
printTableAddFooter(&cont, *footer);
2448
else if (!opt->topt.expanded && opt->default_footer)
2450
unsigned long total_records;
2451
char default_footer[100];
2453
total_records = opt->topt.prior_records + cont.nrows;
2454
snprintf(default_footer, sizeof(default_footer),
2455
ngettext("(%lu row)", "(%lu rows)", total_records),
2458
printTableAddFooter(&cont, default_footer);
2461
printTable(&cont, fout, flog);
2462
printTableCleanup(&cont);
2467
setDecimalLocale(void)
2469
struct lconv *extlconv;
2471
extlconv = localeconv();
2473
if (*extlconv->decimal_point)
2474
decimal_point = pg_strdup(extlconv->decimal_point);
2476
decimal_point = "."; /* SQL output standard */
2477
if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
2478
grouping = pg_strdup(extlconv->grouping);
2480
grouping = "3"; /* most common */
2482
/* similar code exists in formatting.c */
2483
if (*extlconv->thousands_sep)
2484
thousands_sep = pg_strdup(extlconv->thousands_sep);
2485
/* Make sure thousands separator doesn't match decimal point symbol. */
2486
else if (strcmp(decimal_point, ",") != 0)
2487
thousands_sep = ",";
2489
thousands_sep = ".";
2492
/* get selected or default line style */
2493
const printTextFormat *
2494
get_line_style(const printTableOpt *opt)
2497
* Note: this function mainly exists to preserve the convention that a
2498
* printTableOpt struct can be initialized to zeroes to get default
2501
if (opt->line_style != NULL)
2502
return opt->line_style;
2504
return &pg_asciiformat;
2508
* Compute the byte distance to the end of the string or *target_width
2509
* display character positions, whichever comes first. Update *target_width
2510
* to be the number of display character positions actually filled.
2513
strlen_max_width(unsigned char *str, int *target_width, int encoding)
2515
unsigned char *start = str;
2516
unsigned char *end = str + strlen((char *) str);
2521
int char_width = PQdsplen((char *) str, encoding);
2524
* If the display width of the new character causes the string to
2525
* exceed its target width, skip it and return. However, if this is
2526
* the first character of the string (curr_width == 0), we have to
2529
if (*target_width < curr_width + char_width && curr_width != 0)
2532
curr_width += char_width;
2534
str += PQmblen((char *) str, encoding);
2537
*target_width = curr_width;