00001
00002
00003
00004
00005
00006
00007
00008 #include "postgres_fe.h"
00009
00010 #include <limits.h>
00011 #include <math.h>
00012 #include <signal.h>
00013 #include <unistd.h>
00014
00015 #ifndef WIN32
00016 #include <sys/ioctl.h>
00017 #endif
00018
00019 #ifdef HAVE_TERMIOS_H
00020 #include <termios.h>
00021 #endif
00022
00023 #include <locale.h>
00024
00025 #include "catalog/pg_type.h"
00026
00027 #include "common.h"
00028 #include "mbprint.h"
00029 #include "print.h"
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 volatile bool cancel_pressed = false;
00041
00042 static char *decimal_point;
00043 static char *grouping;
00044 static char *thousands_sep;
00045
00046 static char default_footer[100];
00047 static printTableFooter default_footer_cell = {default_footer, NULL};
00048
00049
00050 const printTextFormat pg_asciiformat =
00051 {
00052 "ascii",
00053 {
00054 {"-", "+", "+", "+"},
00055 {"-", "+", "+", "+"},
00056 {"-", "+", "+", "+"},
00057 {"", "|", "|", "|"}
00058 },
00059 "|",
00060 "|",
00061 "|",
00062 " ",
00063 "+",
00064 " ",
00065 "+",
00066 ".",
00067 ".",
00068 true
00069 };
00070
00071 const printTextFormat pg_asciiformat_old =
00072 {
00073 "old-ascii",
00074 {
00075 {"-", "+", "+", "+"},
00076 {"-", "+", "+", "+"},
00077 {"-", "+", "+", "+"},
00078 {"", "|", "|", "|"}
00079 },
00080 ":",
00081 ";",
00082 " ",
00083 "+",
00084 " ",
00085 " ",
00086 " ",
00087 " ",
00088 " ",
00089 false
00090 };
00091
00092 const printTextFormat pg_utf8format =
00093 {
00094 "unicode",
00095 {
00096
00097 {"\342\224\200", "\342\224\214", "\342\224\254", "\342\224\220"},
00098
00099 {"\342\224\200", "\342\224\234", "\342\224\274", "\342\224\244"},
00100
00101 {"\342\224\200", "\342\224\224", "\342\224\264", "\342\224\230"},
00102
00103 {"", "\342\224\202", "\342\224\202", "\342\224\202"}
00104 },
00105
00106 "\342\224\202",
00107
00108 "\342\224\202",
00109
00110 "\342\224\202",
00111 " ",
00112
00113 "\342\206\265",
00114 " ",
00115
00116 "\342\206\265",
00117
00118 "\342\200\246",
00119
00120 "\342\200\246",
00121 true
00122 };
00123
00124
00125
00126 static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
00127 static void IsPagerNeeded(const printTableContent *cont, const int extra_lines, bool expanded,
00128 FILE **fout, bool *is_pager);
00129
00130 static void print_aligned_vertical(const printTableContent *cont, FILE *fout);
00131
00132
00133 static int
00134 integer_digits(const char *my_str)
00135 {
00136 int frac_len;
00137
00138 if (my_str[0] == '-')
00139 my_str++;
00140
00141 frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
00142
00143 return strlen(my_str) - frac_len;
00144 }
00145
00146
00147 static int
00148 additional_numeric_locale_len(const char *my_str)
00149 {
00150 int int_len = integer_digits(my_str),
00151 len = 0;
00152 int groupdigits = atoi(grouping);
00153
00154 if (int_len > 0)
00155
00156 len = (int_len / groupdigits - (int_len % groupdigits == 0)) *
00157 strlen(thousands_sep);
00158
00159 if (strchr(my_str, '.') != NULL)
00160 len += strlen(decimal_point) - strlen(".");
00161
00162 return len;
00163 }
00164
00165 static int
00166 strlen_with_numeric_locale(const char *my_str)
00167 {
00168 return strlen(my_str) + additional_numeric_locale_len(my_str);
00169 }
00170
00171
00172
00173
00174
00175 static char *
00176 format_numeric_locale(const char *my_str)
00177 {
00178 int i,
00179 j,
00180 int_len = integer_digits(my_str),
00181 leading_digits;
00182 int groupdigits = atoi(grouping);
00183 int new_str_start = 0;
00184 char *new_str = pg_malloc(strlen_with_numeric_locale(my_str) + 1);
00185
00186 leading_digits = (int_len % groupdigits != 0) ?
00187 int_len % groupdigits : groupdigits;
00188
00189 if (my_str[0] == '-')
00190
00191 {
00192 new_str[0] = my_str[0];
00193 my_str++;
00194 new_str_start = 1;
00195 }
00196
00197 for (i = 0, j = new_str_start;; i++, j++)
00198 {
00199
00200 if (my_str[i] == '.')
00201 {
00202 strcpy(&new_str[j], decimal_point);
00203 j += strlen(decimal_point);
00204
00205 strcpy(&new_str[j], &my_str[i] + 1);
00206 break;
00207 }
00208
00209
00210 if (my_str[i] == '\0')
00211 {
00212 new_str[j] = '\0';
00213 break;
00214 }
00215
00216
00217 if (i != 0 && (i - leading_digits) % groupdigits == 0)
00218 {
00219 strcpy(&new_str[j], thousands_sep);
00220 j += strlen(thousands_sep);
00221 }
00222
00223 new_str[j] = my_str[i];
00224 }
00225
00226 return new_str;
00227 }
00228
00229
00230
00231
00232
00233
00234
00235
00236 static void
00237 fputnbytes(FILE *f, const char *str, size_t n)
00238 {
00239 while (n-- > 0)
00240 fputc(*str++, f);
00241 }
00242
00243
00244 static void
00245 print_separator(struct separator sep, FILE *fout)
00246 {
00247 if (sep.separator_zero)
00248 fputc('\000', fout);
00249 else if (sep.separator)
00250 fputs(sep.separator, fout);
00251 }
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263 static printTableFooter *
00264 footers_with_default(const printTableContent *cont)
00265 {
00266 if (cont->footers == NULL && cont->opt->default_footer)
00267 {
00268 unsigned long total_records;
00269
00270 total_records = cont->opt->prior_records + cont->nrows;
00271 snprintf(default_footer, sizeof(default_footer),
00272 ngettext("(%lu row)", "(%lu rows)", total_records),
00273 total_records);
00274
00275 return &default_footer_cell;
00276 }
00277 else
00278 return cont->footers;
00279 }
00280
00281
00282
00283
00284
00285
00286
00287 static void
00288 print_unaligned_text(const printTableContent *cont, FILE *fout)
00289 {
00290 bool opt_tuples_only = cont->opt->tuples_only;
00291 unsigned int i;
00292 const char *const * ptr;
00293 bool need_recordsep = false;
00294
00295 if (cancel_pressed)
00296 return;
00297
00298 if (cont->opt->start_table)
00299 {
00300
00301 if (!opt_tuples_only && cont->title)
00302 {
00303 fputs(cont->title, fout);
00304 print_separator(cont->opt->recordSep, fout);
00305 }
00306
00307
00308 if (!opt_tuples_only)
00309 {
00310 for (ptr = cont->headers; *ptr; ptr++)
00311 {
00312 if (ptr != cont->headers)
00313 print_separator(cont->opt->fieldSep, fout);
00314 fputs(*ptr, fout);
00315 }
00316 need_recordsep = true;
00317 }
00318 }
00319 else
00320
00321 need_recordsep = true;
00322
00323
00324 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
00325 {
00326 if (need_recordsep)
00327 {
00328 print_separator(cont->opt->recordSep, fout);
00329 need_recordsep = false;
00330 if (cancel_pressed)
00331 break;
00332 }
00333 fputs(*ptr, fout);
00334
00335 if ((i + 1) % cont->ncolumns)
00336 print_separator(cont->opt->fieldSep, fout);
00337 else
00338 need_recordsep = true;
00339 }
00340
00341
00342 if (cont->opt->stop_table)
00343 {
00344 printTableFooter *footers = footers_with_default(cont);
00345
00346 if (!opt_tuples_only && footers != NULL && !cancel_pressed)
00347 {
00348 printTableFooter *f;
00349
00350 for (f = footers; f; f = f->next)
00351 {
00352 if (need_recordsep)
00353 {
00354 print_separator(cont->opt->recordSep, fout);
00355 need_recordsep = false;
00356 }
00357 fputs(f->data, fout);
00358 need_recordsep = true;
00359 }
00360 }
00361
00362
00363
00364
00365
00366
00367 if (need_recordsep)
00368 {
00369 if (cont->opt->recordSep.separator_zero)
00370 print_separator(cont->opt->recordSep, fout);
00371 else
00372 fputc('\n', fout);
00373 }
00374 }
00375 }
00376
00377
00378 static void
00379 print_unaligned_vertical(const printTableContent *cont, FILE *fout)
00380 {
00381 bool opt_tuples_only = cont->opt->tuples_only;
00382 unsigned int i;
00383 const char *const * ptr;
00384 bool need_recordsep = false;
00385
00386 if (cancel_pressed)
00387 return;
00388
00389 if (cont->opt->start_table)
00390 {
00391
00392 if (!opt_tuples_only && cont->title)
00393 {
00394 fputs(cont->title, fout);
00395 need_recordsep = true;
00396 }
00397 }
00398 else
00399
00400 need_recordsep = true;
00401
00402
00403 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
00404 {
00405 if (need_recordsep)
00406 {
00407
00408 print_separator(cont->opt->recordSep, fout);
00409 print_separator(cont->opt->recordSep, fout);
00410 need_recordsep = false;
00411 if (cancel_pressed)
00412 break;
00413 }
00414
00415 fputs(cont->headers[i % cont->ncolumns], fout);
00416 print_separator(cont->opt->fieldSep, fout);
00417 fputs(*ptr, fout);
00418
00419 if ((i + 1) % cont->ncolumns)
00420 print_separator(cont->opt->recordSep, fout);
00421 else
00422 need_recordsep = true;
00423 }
00424
00425 if (cont->opt->stop_table)
00426 {
00427
00428 if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
00429 {
00430 printTableFooter *f;
00431
00432 print_separator(cont->opt->recordSep, fout);
00433 for (f = cont->footers; f; f = f->next)
00434 {
00435 print_separator(cont->opt->recordSep, fout);
00436 fputs(f->data, fout);
00437 }
00438 }
00439
00440
00441 if (need_recordsep)
00442 {
00443 if (cont->opt->recordSep.separator_zero)
00444 print_separator(cont->opt->recordSep, fout);
00445 else
00446 fputc('\n', fout);
00447 }
00448 }
00449 }
00450
00451
00452
00453
00454
00455
00456
00457
00458 static void
00459 _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths,
00460 unsigned short border, printTextRule pos,
00461 const printTextFormat *format,
00462 FILE *fout)
00463 {
00464 const printTextLineFormat *lformat = &format->lrule[pos];
00465 unsigned int i,
00466 j;
00467
00468 if (border == 1)
00469 fputs(lformat->hrule, fout);
00470 else if (border == 2)
00471 fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
00472
00473 for (i = 0; i < ncolumns; i++)
00474 {
00475 for (j = 0; j < widths[i]; j++)
00476 fputs(lformat->hrule, fout);
00477
00478 if (i < ncolumns - 1)
00479 {
00480 if (border == 0)
00481 fputc(' ', fout);
00482 else
00483 fprintf(fout, "%s%s%s", lformat->hrule,
00484 lformat->midvrule, lformat->hrule);
00485 }
00486 }
00487
00488 if (border == 2)
00489 fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
00490 else if (border == 1)
00491 fputs(lformat->hrule, fout);
00492
00493 fputc('\n', fout);
00494 }
00495
00496
00497
00498
00499
00500 static void
00501 print_aligned_text(const printTableContent *cont, FILE *fout)
00502 {
00503 bool opt_tuples_only = cont->opt->tuples_only;
00504 int encoding = cont->opt->encoding;
00505 unsigned short opt_border = cont->opt->border;
00506 const printTextFormat *format = get_line_style(cont->opt);
00507 const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
00508
00509 unsigned int col_count = 0,
00510 cell_count = 0;
00511
00512 unsigned int i,
00513 j;
00514
00515 unsigned int *width_header,
00516 *max_width,
00517 *width_wrap,
00518 *width_average;
00519 unsigned int *max_nl_lines,
00520 *curr_nl_line,
00521 *max_bytes;
00522 unsigned char **format_buf;
00523 unsigned int width_total;
00524 unsigned int total_header_width;
00525 unsigned int extra_row_output_lines = 0;
00526 unsigned int extra_output_lines = 0;
00527
00528 const char *const * ptr;
00529
00530 struct lineptr **col_lineptrs;
00531
00532 bool *header_done;
00533 int *bytes_output;
00534 printTextLineWrap *wrap;
00535 int output_columns = 0;
00536 bool is_pager = false;
00537
00538 if (cancel_pressed)
00539 return;
00540
00541 if (opt_border > 2)
00542 opt_border = 2;
00543
00544 if (cont->ncolumns > 0)
00545 {
00546 col_count = cont->ncolumns;
00547 width_header = pg_malloc0(col_count * sizeof(*width_header));
00548 width_average = pg_malloc0(col_count * sizeof(*width_average));
00549 max_width = pg_malloc0(col_count * sizeof(*max_width));
00550 width_wrap = pg_malloc0(col_count * sizeof(*width_wrap));
00551 max_nl_lines = pg_malloc0(col_count * sizeof(*max_nl_lines));
00552 curr_nl_line = pg_malloc0(col_count * sizeof(*curr_nl_line));
00553 col_lineptrs = pg_malloc0(col_count * sizeof(*col_lineptrs));
00554 max_bytes = pg_malloc0(col_count * sizeof(*max_bytes));
00555 format_buf = pg_malloc0(col_count * sizeof(*format_buf));
00556 header_done = pg_malloc0(col_count * sizeof(*header_done));
00557 bytes_output = pg_malloc0(col_count * sizeof(*bytes_output));
00558 wrap = pg_malloc0(col_count * sizeof(*wrap));
00559 }
00560 else
00561 {
00562 width_header = NULL;
00563 width_average = NULL;
00564 max_width = NULL;
00565 width_wrap = NULL;
00566 max_nl_lines = NULL;
00567 curr_nl_line = NULL;
00568 col_lineptrs = NULL;
00569 max_bytes = NULL;
00570 format_buf = NULL;
00571 header_done = NULL;
00572 bytes_output = NULL;
00573 wrap = NULL;
00574 }
00575
00576
00577 for (i = 0; i < col_count; i++)
00578 {
00579 int width,
00580 nl_lines,
00581 bytes_required;
00582
00583 pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
00584 encoding, &width, &nl_lines, &bytes_required);
00585 if (width > max_width[i])
00586 max_width[i] = width;
00587 if (nl_lines > max_nl_lines[i])
00588 max_nl_lines[i] = nl_lines;
00589 if (bytes_required > max_bytes[i])
00590 max_bytes[i] = bytes_required;
00591 if (nl_lines > extra_row_output_lines)
00592 extra_row_output_lines = nl_lines;
00593
00594 width_header[i] = width;
00595 }
00596
00597 extra_output_lines += extra_row_output_lines;
00598 extra_row_output_lines = 0;
00599
00600
00601 for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
00602 {
00603 int width,
00604 nl_lines,
00605 bytes_required;
00606
00607 pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
00608 &width, &nl_lines, &bytes_required);
00609
00610 if (width > max_width[i % col_count])
00611 max_width[i % col_count] = width;
00612 if (nl_lines > max_nl_lines[i % col_count])
00613 max_nl_lines[i % col_count] = nl_lines;
00614 if (bytes_required > max_bytes[i % col_count])
00615 max_bytes[i % col_count] = bytes_required;
00616
00617 width_average[i % col_count] += width;
00618 }
00619
00620
00621 if (col_count != 0 && cell_count != 0)
00622 {
00623 int rows = cell_count / col_count;
00624
00625 for (i = 0; i < col_count; i++)
00626 width_average[i] /= rows;
00627 }
00628
00629
00630 if (opt_border == 0)
00631 width_total = col_count;
00632 else if (opt_border == 1)
00633 width_total = col_count * 3 - 1;
00634 else
00635 width_total = col_count * 3 + 1;
00636 total_header_width = width_total;
00637
00638 for (i = 0; i < col_count; i++)
00639 {
00640 width_total += max_width[i];
00641 total_header_width += width_header[i];
00642 }
00643
00644
00645
00646
00647
00648
00649
00650
00651 for (i = 0; i < col_count; i++)
00652 {
00653
00654 col_lineptrs[i] = pg_malloc0((max_nl_lines[i] + 1) *
00655 sizeof(**col_lineptrs));
00656
00657 format_buf[i] = pg_malloc(max_bytes[i] + 1);
00658
00659 col_lineptrs[i]->ptr = format_buf[i];
00660 }
00661
00662
00663 for (i = 0; i < col_count; i++)
00664 width_wrap[i] = max_width[i];
00665
00666
00667
00668
00669 if (cont->opt->columns > 0)
00670 output_columns = cont->opt->columns;
00671 else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
00672 {
00673 if (cont->opt->env_columns > 0)
00674 output_columns = cont->opt->env_columns;
00675 #ifdef TIOCGWINSZ
00676 else
00677 {
00678 struct winsize screen_size;
00679
00680 if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
00681 output_columns = screen_size.ws_col;
00682 }
00683 #endif
00684 }
00685
00686 if (cont->opt->format == PRINT_WRAPPED)
00687 {
00688
00689
00690
00691
00692
00693
00694
00695 if (output_columns > 0 && output_columns >= total_header_width)
00696 {
00697
00698 while (width_total > output_columns)
00699 {
00700 double max_ratio = 0;
00701 int worst_col = -1;
00702
00703
00704
00705
00706
00707
00708
00709 for (i = 0; i < col_count; i++)
00710 {
00711 if (width_average[i] && width_wrap[i] > width_header[i])
00712 {
00713
00714 double ratio;
00715
00716 ratio = (double) width_wrap[i] / width_average[i] +
00717 max_width[i] * 0.01;
00718 if (ratio > max_ratio)
00719 {
00720 max_ratio = ratio;
00721 worst_col = i;
00722 }
00723 }
00724 }
00725
00726
00727 if (worst_col == -1)
00728 break;
00729
00730
00731 width_wrap[worst_col]--;
00732 width_total--;
00733 }
00734 }
00735 }
00736
00737
00738
00739
00740
00741 if (cont->opt->expanded == 2 && output_columns > 0 &&
00742 (output_columns < total_header_width || output_columns < width_total))
00743 {
00744 print_aligned_vertical(cont, fout);
00745 goto cleanup;
00746 }
00747
00748
00749 if (!is_pager && fout == stdout && output_columns > 0 &&
00750 (output_columns < total_header_width || output_columns < width_total))
00751 {
00752 fout = PageOutput(INT_MAX, cont->opt->pager);
00753 is_pager = true;
00754 }
00755
00756
00757 if (!is_pager)
00758 {
00759
00760 for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
00761 {
00762 int width,
00763 nl_lines,
00764 bytes_required;
00765
00766 pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
00767 &width, &nl_lines, &bytes_required);
00768
00769
00770
00771
00772
00773 if (width > 0 && width_wrap[i])
00774 {
00775 unsigned int extra_lines;
00776
00777 extra_lines = (width - 1) / width_wrap[i] + nl_lines;
00778 if (extra_lines > extra_row_output_lines)
00779 extra_row_output_lines = extra_lines;
00780 }
00781
00782
00783 if (++i >= col_count)
00784 {
00785 i = 0;
00786
00787 extra_output_lines += extra_row_output_lines;
00788 extra_row_output_lines = 0;
00789 }
00790 }
00791 IsPagerNeeded(cont, extra_output_lines, false, &fout, &is_pager);
00792 }
00793
00794
00795 if (cont->opt->start_table)
00796 {
00797
00798 if (cont->title && !opt_tuples_only)
00799 {
00800 int width,
00801 height;
00802
00803 pg_wcssize((const unsigned char *) cont->title, strlen(cont->title),
00804 encoding, &width, &height, NULL);
00805 if (width >= width_total)
00806
00807 fprintf(fout, "%s\n", cont->title);
00808 else
00809
00810 fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
00811 cont->title);
00812 }
00813
00814
00815 if (!opt_tuples_only)
00816 {
00817 int more_col_wrapping;
00818 int curr_nl_line;
00819
00820 if (opt_border == 2)
00821 _print_horizontal_line(col_count, width_wrap, opt_border,
00822 PRINT_RULE_TOP, format, fout);
00823
00824 for (i = 0; i < col_count; i++)
00825 pg_wcsformat((const unsigned char *) cont->headers[i],
00826 strlen(cont->headers[i]), encoding,
00827 col_lineptrs[i], max_nl_lines[i]);
00828
00829 more_col_wrapping = col_count;
00830 curr_nl_line = 0;
00831 memset(header_done, false, col_count * sizeof(bool));
00832 while (more_col_wrapping)
00833 {
00834 if (opt_border == 2)
00835 fputs(dformat->leftvrule, fout);
00836
00837 for (i = 0; i < cont->ncolumns; i++)
00838 {
00839 struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
00840 unsigned int nbspace;
00841
00842 if (opt_border != 0 ||
00843 (!format->wrap_right_border && i > 0))
00844 fputs(curr_nl_line ? format->header_nl_left : " ",
00845 fout);
00846
00847 if (!header_done[i])
00848 {
00849 nbspace = width_wrap[i] - this_line->width;
00850
00851
00852 fprintf(fout, "%-*s%s%-*s",
00853 nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
00854
00855 if (!(this_line + 1)->ptr)
00856 {
00857 more_col_wrapping--;
00858 header_done[i] = 1;
00859 }
00860 }
00861 else
00862 fprintf(fout, "%*s", width_wrap[i], "");
00863
00864 if (opt_border != 0 || format->wrap_right_border)
00865 fputs(!header_done[i] ? format->header_nl_right : " ",
00866 fout);
00867
00868 if (opt_border != 0 && i < col_count - 1)
00869 fputs(dformat->midvrule, fout);
00870 }
00871 curr_nl_line++;
00872
00873 if (opt_border == 2)
00874 fputs(dformat->rightvrule, fout);
00875 fputc('\n', fout);
00876 }
00877
00878 _print_horizontal_line(col_count, width_wrap, opt_border,
00879 PRINT_RULE_MIDDLE, format, fout);
00880 }
00881 }
00882
00883
00884 for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
00885 {
00886 bool more_lines;
00887
00888 if (cancel_pressed)
00889 break;
00890
00891
00892
00893
00894 for (j = 0; j < col_count; j++)
00895 {
00896 pg_wcsformat((const unsigned char *) ptr[j], strlen(ptr[j]), encoding,
00897 col_lineptrs[j], max_nl_lines[j]);
00898 curr_nl_line[j] = 0;
00899 }
00900
00901 memset(bytes_output, 0, col_count * sizeof(int));
00902
00903
00904
00905
00906
00907
00908 do
00909 {
00910 more_lines = false;
00911
00912
00913 if (opt_border == 2)
00914 fputs(dformat->leftvrule, fout);
00915
00916
00917 for (j = 0; j < col_count; j++)
00918 {
00919
00920 struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
00921 int bytes_to_output;
00922 int chars_to_output = width_wrap[j];
00923 bool finalspaces = (opt_border == 2 || j < col_count - 1);
00924
00925
00926 if (opt_border != 0)
00927 {
00928 if (wrap[j] == PRINT_LINE_WRAP_WRAP)
00929 fputs(format->wrap_left, fout);
00930 else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
00931 fputs(format->nl_left, fout);
00932 else
00933 fputc(' ', fout);
00934 }
00935
00936 if (!this_line->ptr)
00937 {
00938
00939 if (finalspaces)
00940 fprintf(fout, "%*s", chars_to_output, "");
00941 }
00942 else
00943 {
00944
00945 bytes_to_output =
00946 strlen_max_width(this_line->ptr + bytes_output[j],
00947 &chars_to_output, encoding);
00948
00949
00950
00951
00952
00953
00954
00955 if (chars_to_output > width_wrap[j])
00956 chars_to_output = width_wrap[j];
00957
00958 if (cont->aligns[j] == 'r')
00959 {
00960
00961 fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
00962 fputnbytes(fout,
00963 (char *) (this_line->ptr + bytes_output[j]),
00964 bytes_to_output);
00965 }
00966 else
00967 {
00968
00969 fputnbytes(fout,
00970 (char *) (this_line->ptr + bytes_output[j]),
00971 bytes_to_output);
00972 }
00973
00974 bytes_output[j] += bytes_to_output;
00975
00976
00977 if (*(this_line->ptr + bytes_output[j]) != '\0')
00978 more_lines = true;
00979 else
00980 {
00981
00982 curr_nl_line[j]++;
00983 if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
00984 more_lines = true;
00985 bytes_output[j] = 0;
00986 }
00987 }
00988
00989
00990 wrap[j] = PRINT_LINE_WRAP_NONE;
00991 if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
00992 {
00993 if (bytes_output[j] != 0)
00994 wrap[j] = PRINT_LINE_WRAP_WRAP;
00995 else if (curr_nl_line[j] != 0)
00996 wrap[j] = PRINT_LINE_WRAP_NEWLINE;
00997 }
00998
00999
01000
01001
01002
01003 if (cont->aligns[j] != 'r')
01004 {
01005 if (finalspaces ||
01006 wrap[j] == PRINT_LINE_WRAP_WRAP ||
01007 wrap[j] == PRINT_LINE_WRAP_NEWLINE)
01008 fprintf(fout, "%*s",
01009 width_wrap[j] - chars_to_output, "");
01010 }
01011
01012
01013 if (wrap[j] == PRINT_LINE_WRAP_WRAP)
01014 fputs(format->wrap_right, fout);
01015 else if (wrap[j] == PRINT_LINE_WRAP_NEWLINE)
01016 fputs(format->nl_right, fout);
01017 else if (opt_border == 2 || j < col_count - 1)
01018 fputc(' ', fout);
01019
01020
01021 if (opt_border != 0 && j < col_count - 1)
01022 {
01023 if (wrap[j + 1] == PRINT_LINE_WRAP_WRAP)
01024 fputs(format->midvrule_wrap, fout);
01025 else if (wrap[j + 1] == PRINT_LINE_WRAP_NEWLINE)
01026 fputs(format->midvrule_nl, fout);
01027 else if (col_lineptrs[j + 1][curr_nl_line[j + 1]].ptr == NULL)
01028 fputs(format->midvrule_blank, fout);
01029 else
01030 fputs(dformat->midvrule, fout);
01031 }
01032 }
01033
01034
01035 if (opt_border == 2)
01036 fputs(dformat->rightvrule, fout);
01037 fputc('\n', fout);
01038
01039 } while (more_lines);
01040 }
01041
01042 if (cont->opt->stop_table)
01043 {
01044 printTableFooter *footers = footers_with_default(cont);
01045
01046 if (opt_border == 2 && !cancel_pressed)
01047 _print_horizontal_line(col_count, width_wrap, opt_border,
01048 PRINT_RULE_BOTTOM, format, fout);
01049
01050
01051 if (footers && !opt_tuples_only && !cancel_pressed)
01052 {
01053 printTableFooter *f;
01054
01055 for (f = footers; f; f = f->next)
01056 fprintf(fout, "%s\n", f->data);
01057 }
01058
01059 fputc('\n', fout);
01060 }
01061
01062 cleanup:
01063
01064 for (i = 0; i < col_count; i++)
01065 {
01066 free(col_lineptrs[i]);
01067 free(format_buf[i]);
01068 }
01069 free(width_header);
01070 free(width_average);
01071 free(max_width);
01072 free(width_wrap);
01073 free(max_nl_lines);
01074 free(curr_nl_line);
01075 free(col_lineptrs);
01076 free(max_bytes);
01077 free(format_buf);
01078 free(header_done);
01079 free(bytes_output);
01080 free(wrap);
01081
01082 if (is_pager)
01083 ClosePager(fout);
01084 }
01085
01086
01087 static void
01088 print_aligned_vertical_line(const printTableContent *cont,
01089 unsigned long record,
01090 unsigned int hwidth,
01091 unsigned int dwidth,
01092 printTextRule pos,
01093 FILE *fout)
01094 {
01095 const printTextFormat *format = get_line_style(cont->opt);
01096 const printTextLineFormat *lformat = &format->lrule[pos];
01097 unsigned short opt_border = cont->opt->border;
01098 unsigned int i;
01099 int reclen = 0;
01100
01101 if (opt_border == 2)
01102 fprintf(fout, "%s%s", lformat->leftvrule, lformat->hrule);
01103 else if (opt_border == 1)
01104 fputs(lformat->hrule, fout);
01105
01106 if (record)
01107 {
01108 if (opt_border == 0)
01109 reclen = fprintf(fout, "* Record %lu", record);
01110 else
01111 reclen = fprintf(fout, "[ RECORD %lu ]", record);
01112 }
01113 if (opt_border != 2)
01114 reclen++;
01115 if (reclen < 0)
01116 reclen = 0;
01117 for (i = reclen; i < hwidth; i++)
01118 fputs(opt_border > 0 ? lformat->hrule : " ", fout);
01119 reclen -= hwidth;
01120
01121 if (opt_border > 0)
01122 {
01123 if (reclen-- <= 0)
01124 fputs(lformat->hrule, fout);
01125 if (reclen-- <= 0)
01126 fputs(lformat->midvrule, fout);
01127 if (reclen-- <= 0)
01128 fputs(lformat->hrule, fout);
01129 }
01130 else
01131 {
01132 if (reclen-- <= 0)
01133 fputc(' ', fout);
01134 }
01135 if (reclen < 0)
01136 reclen = 0;
01137 for (i = reclen; i < dwidth; i++)
01138 fputs(opt_border > 0 ? lformat->hrule : " ", fout);
01139 if (opt_border == 2)
01140 fprintf(fout, "%s%s", lformat->hrule, lformat->rightvrule);
01141 fputc('\n', fout);
01142 }
01143
01144 static void
01145 print_aligned_vertical(const printTableContent *cont, FILE *fout)
01146 {
01147 bool opt_tuples_only = cont->opt->tuples_only;
01148 unsigned short opt_border = cont->opt->border;
01149 const printTextFormat *format = get_line_style(cont->opt);
01150 const printTextLineFormat *dformat = &format->lrule[PRINT_RULE_DATA];
01151 int encoding = cont->opt->encoding;
01152 unsigned long record = cont->opt->prior_records + 1;
01153 const char *const * ptr;
01154 unsigned int i,
01155 hwidth = 0,
01156 dwidth = 0,
01157 hheight = 1,
01158 dheight = 1,
01159 hformatsize = 0,
01160 dformatsize = 0;
01161 struct lineptr *hlineptr,
01162 *dlineptr;
01163 bool is_pager = false;
01164
01165 if (cancel_pressed)
01166 return;
01167
01168 if (opt_border > 2)
01169 opt_border = 2;
01170
01171 if (cont->cells[0] == NULL && cont->opt->start_table &&
01172 cont->opt->stop_table)
01173 {
01174 if (!opt_tuples_only)
01175 fprintf(fout, _("(No rows)\n"));
01176 return;
01177 }
01178
01179
01180
01181
01182
01183
01184 IsPagerNeeded(cont, 0, true, &fout, &is_pager);
01185
01186
01187 for (i = 0; i < cont->ncolumns; i++)
01188 {
01189 int width,
01190 height,
01191 fs;
01192
01193 pg_wcssize((const unsigned char *) cont->headers[i], strlen(cont->headers[i]),
01194 encoding, &width, &height, &fs);
01195 if (width > hwidth)
01196 hwidth = width;
01197 if (height > hheight)
01198 hheight = height;
01199 if (fs > hformatsize)
01200 hformatsize = fs;
01201 }
01202
01203
01204 for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
01205 {
01206 int width,
01207 height,
01208 fs;
01209
01210 pg_wcssize((const unsigned char *) *ptr, strlen(*ptr), encoding,
01211 &width, &height, &fs);
01212 if (width > dwidth)
01213 dwidth = width;
01214 if (height > dheight)
01215 dheight = height;
01216 if (fs > dformatsize)
01217 dformatsize = fs;
01218 }
01219
01220
01221
01222
01223
01224 dlineptr = pg_malloc((sizeof(*dlineptr)) * (dheight + 1));
01225 hlineptr = pg_malloc((sizeof(*hlineptr)) * (hheight + 1));
01226
01227 dlineptr->ptr = pg_malloc(dformatsize);
01228 hlineptr->ptr = pg_malloc(hformatsize);
01229
01230 if (cont->opt->start_table)
01231 {
01232
01233 if (!opt_tuples_only && cont->title)
01234 fprintf(fout, "%s\n", cont->title);
01235 }
01236
01237
01238 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01239 {
01240 printTextRule pos;
01241 int line_count,
01242 dcomplete,
01243 hcomplete;
01244
01245 if (cancel_pressed)
01246 break;
01247
01248 if (i == 0)
01249 pos = PRINT_RULE_TOP;
01250 else if (!(*(ptr + 1)))
01251 pos = PRINT_RULE_BOTTOM;
01252 else
01253 pos = PRINT_RULE_MIDDLE;
01254
01255 if (i % cont->ncolumns == 0)
01256 {
01257 if (!opt_tuples_only)
01258 print_aligned_vertical_line(cont, record++, hwidth, dwidth,
01259 pos, fout);
01260 else if (i != 0 || !cont->opt->start_table || opt_border == 2)
01261 print_aligned_vertical_line(cont, 0, hwidth, dwidth,
01262 pos, fout);
01263 }
01264
01265
01266 pg_wcsformat((const unsigned char *) cont->headers[i % cont->ncolumns],
01267 strlen(cont->headers[i % cont->ncolumns]),
01268 encoding, hlineptr, hheight);
01269
01270 pg_wcsformat((const unsigned char *) *ptr, strlen(*ptr), encoding,
01271 dlineptr, dheight);
01272
01273 line_count = 0;
01274 dcomplete = hcomplete = 0;
01275 while (!dcomplete || !hcomplete)
01276 {
01277 if (opt_border == 2)
01278 fprintf(fout, "%s ", dformat->leftvrule);
01279 if (!hcomplete)
01280 {
01281 fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
01282 hwidth - hlineptr[line_count].width, "");
01283
01284 if (!hlineptr[line_count + 1].ptr)
01285 hcomplete = 1;
01286 }
01287 else
01288 fprintf(fout, "%*s", hwidth, "");
01289
01290 if (opt_border > 0)
01291 fprintf(fout, " %s ", dformat->midvrule);
01292 else
01293 fputc(' ', fout);
01294
01295 if (!dcomplete)
01296 {
01297 if (opt_border < 2)
01298 fprintf(fout, "%s\n", dlineptr[line_count].ptr);
01299 else
01300 fprintf(fout, "%-s%*s %s\n", dlineptr[line_count].ptr,
01301 dwidth - dlineptr[line_count].width, "",
01302 dformat->rightvrule);
01303
01304 if (!dlineptr[line_count + 1].ptr)
01305 dcomplete = 1;
01306 }
01307 else
01308 {
01309 if (opt_border < 2)
01310 fputc('\n', fout);
01311 else
01312 fprintf(fout, "%*s %s\n", dwidth, "", dformat->rightvrule);
01313 }
01314 line_count++;
01315 }
01316 }
01317
01318 if (cont->opt->stop_table)
01319 {
01320 if (opt_border == 2 && !cancel_pressed)
01321 print_aligned_vertical_line(cont, 0, hwidth, dwidth,
01322 PRINT_RULE_BOTTOM, fout);
01323
01324
01325 if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
01326 {
01327 printTableFooter *f;
01328
01329 if (opt_border < 2)
01330 fputc('\n', fout);
01331 for (f = cont->footers; f; f = f->next)
01332 fprintf(fout, "%s\n", f->data);
01333 }
01334
01335 fputc('\n', fout);
01336 }
01337
01338 free(hlineptr->ptr);
01339 free(dlineptr->ptr);
01340 free(hlineptr);
01341 free(dlineptr);
01342
01343 if (is_pager)
01344 ClosePager(fout);
01345 }
01346
01347
01348
01349
01350
01351
01352
01353 void
01354 html_escaped_print(const char *in, FILE *fout)
01355 {
01356 const char *p;
01357 bool leading_space = true;
01358
01359 for (p = in; *p; p++)
01360 {
01361 switch (*p)
01362 {
01363 case '&':
01364 fputs("&", fout);
01365 break;
01366 case '<':
01367 fputs("<", fout);
01368 break;
01369 case '>':
01370 fputs(">", fout);
01371 break;
01372 case '\n':
01373 fputs("<br />\n", fout);
01374 break;
01375 case '"':
01376 fputs(""", fout);
01377 break;
01378 case ' ':
01379
01380 if (leading_space)
01381 fputs(" ", fout);
01382 else
01383 fputs(" ", fout);
01384 break;
01385 default:
01386 fputc(*p, fout);
01387 }
01388 if (*p != ' ')
01389 leading_space = false;
01390 }
01391 }
01392
01393
01394 static void
01395 print_html_text(const printTableContent *cont, FILE *fout)
01396 {
01397 bool opt_tuples_only = cont->opt->tuples_only;
01398 unsigned short opt_border = cont->opt->border;
01399 const char *opt_table_attr = cont->opt->tableAttr;
01400 unsigned int i;
01401 const char *const * ptr;
01402
01403 if (cancel_pressed)
01404 return;
01405
01406 if (cont->opt->start_table)
01407 {
01408 fprintf(fout, "<table border=\"%d\"", opt_border);
01409 if (opt_table_attr)
01410 fprintf(fout, " %s", opt_table_attr);
01411 fputs(">\n", fout);
01412
01413
01414 if (!opt_tuples_only && cont->title)
01415 {
01416 fputs(" <caption>", fout);
01417 html_escaped_print(cont->title, fout);
01418 fputs("</caption>\n", fout);
01419 }
01420
01421
01422 if (!opt_tuples_only)
01423 {
01424 fputs(" <tr>\n", fout);
01425 for (ptr = cont->headers; *ptr; ptr++)
01426 {
01427 fputs(" <th align=\"center\">", fout);
01428 html_escaped_print(*ptr, fout);
01429 fputs("</th>\n", fout);
01430 }
01431 fputs(" </tr>\n", fout);
01432 }
01433 }
01434
01435
01436 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01437 {
01438 if (i % cont->ncolumns == 0)
01439 {
01440 if (cancel_pressed)
01441 break;
01442 fputs(" <tr valign=\"top\">\n", fout);
01443 }
01444
01445 fprintf(fout, " <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
01446
01447 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
01448 fputs(" ", fout);
01449 else
01450 html_escaped_print(*ptr, fout);
01451
01452 fputs("</td>\n", fout);
01453
01454 if ((i + 1) % cont->ncolumns == 0)
01455 fputs(" </tr>\n", fout);
01456 }
01457
01458 if (cont->opt->stop_table)
01459 {
01460 printTableFooter *footers = footers_with_default(cont);
01461
01462 fputs("</table>\n", fout);
01463
01464
01465 if (!opt_tuples_only && footers != NULL && !cancel_pressed)
01466 {
01467 printTableFooter *f;
01468
01469 fputs("<p>", fout);
01470 for (f = footers; f; f = f->next)
01471 {
01472 html_escaped_print(f->data, fout);
01473 fputs("<br />\n", fout);
01474 }
01475 fputs("</p>", fout);
01476 }
01477
01478 fputc('\n', fout);
01479 }
01480 }
01481
01482
01483 static void
01484 print_html_vertical(const printTableContent *cont, FILE *fout)
01485 {
01486 bool opt_tuples_only = cont->opt->tuples_only;
01487 unsigned short opt_border = cont->opt->border;
01488 const char *opt_table_attr = cont->opt->tableAttr;
01489 unsigned long record = cont->opt->prior_records + 1;
01490 unsigned int i;
01491 const char *const * ptr;
01492
01493 if (cancel_pressed)
01494 return;
01495
01496 if (cont->opt->start_table)
01497 {
01498 fprintf(fout, "<table border=\"%d\"", opt_border);
01499 if (opt_table_attr)
01500 fprintf(fout, " %s", opt_table_attr);
01501 fputs(">\n", fout);
01502
01503
01504 if (!opt_tuples_only && cont->title)
01505 {
01506 fputs(" <caption>", fout);
01507 html_escaped_print(cont->title, fout);
01508 fputs("</caption>\n", fout);
01509 }
01510 }
01511
01512
01513 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01514 {
01515 if (i % cont->ncolumns == 0)
01516 {
01517 if (cancel_pressed)
01518 break;
01519 if (!opt_tuples_only)
01520 fprintf(fout,
01521 "\n <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
01522 record++);
01523 else
01524 fputs("\n <tr><td colspan=\"2\"> </td></tr>\n", fout);
01525 }
01526 fputs(" <tr valign=\"top\">\n"
01527 " <th>", fout);
01528 html_escaped_print(cont->headers[i % cont->ncolumns], fout);
01529 fputs("</th>\n", fout);
01530
01531 fprintf(fout, " <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
01532
01533 if ((*ptr)[strspn(*ptr, " \t")] == '\0')
01534 fputs(" ", fout);
01535 else
01536 html_escaped_print(*ptr, fout);
01537
01538 fputs("</td>\n </tr>\n", fout);
01539 }
01540
01541 if (cont->opt->stop_table)
01542 {
01543 fputs("</table>\n", fout);
01544
01545
01546 if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
01547 {
01548 printTableFooter *f;
01549
01550 fputs("<p>", fout);
01551 for (f = cont->footers; f; f = f->next)
01552 {
01553 html_escaped_print(f->data, fout);
01554 fputs("<br />\n", fout);
01555 }
01556 fputs("</p>", fout);
01557 }
01558
01559 fputc('\n', fout);
01560 }
01561 }
01562
01563
01564
01565
01566
01567
01568
01569 static void
01570 latex_escaped_print(const char *in, FILE *fout)
01571 {
01572 const char *p;
01573
01574 for (p = in; *p; p++)
01575 switch (*p)
01576 {
01577 case '&':
01578 fputs("\\&", fout);
01579 break;
01580 case '%':
01581 fputs("\\%", fout);
01582 break;
01583 case '$':
01584 fputs("\\$", fout);
01585 break;
01586 case '_':
01587 fputs("\\_", fout);
01588 break;
01589 case '{':
01590 fputs("\\{", fout);
01591 break;
01592 case '}':
01593 fputs("\\}", fout);
01594 break;
01595 case '\\':
01596 fputs("\\backslash", fout);
01597 break;
01598 case '\n':
01599 fputs("\\\\", fout);
01600 break;
01601 default:
01602 fputc(*p, fout);
01603 }
01604 }
01605
01606
01607 static void
01608 print_latex_text(const printTableContent *cont, FILE *fout)
01609 {
01610 bool opt_tuples_only = cont->opt->tuples_only;
01611 unsigned short opt_border = cont->opt->border;
01612 unsigned int i;
01613 const char *const * ptr;
01614
01615 if (cancel_pressed)
01616 return;
01617
01618 if (opt_border > 3)
01619 opt_border = 3;
01620
01621 if (cont->opt->start_table)
01622 {
01623
01624 if (!opt_tuples_only && cont->title)
01625 {
01626 fputs("\\begin{center}\n", fout);
01627 latex_escaped_print(cont->title, fout);
01628 fputs("\n\\end{center}\n\n", fout);
01629 }
01630
01631
01632 fputs("\\begin{tabular}{", fout);
01633
01634 if (opt_border >= 2)
01635 fputs("| ", fout);
01636 for (i = 0; i < cont->ncolumns; i++)
01637 {
01638 fputc(*(cont->aligns + i), fout);
01639 if (opt_border != 0 && i < cont->ncolumns - 1)
01640 fputs(" | ", fout);
01641 }
01642 if (opt_border >= 2)
01643 fputs(" |", fout);
01644
01645 fputs("}\n", fout);
01646
01647 if (!opt_tuples_only && opt_border >= 2)
01648 fputs("\\hline\n", fout);
01649
01650
01651 if (!opt_tuples_only)
01652 {
01653 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
01654 {
01655 if (i != 0)
01656 fputs(" & ", fout);
01657 fputs("\\textit{", fout);
01658 latex_escaped_print(*ptr, fout);
01659 fputc('}', fout);
01660 }
01661 fputs(" \\\\\n", fout);
01662 fputs("\\hline\n", fout);
01663 }
01664 }
01665
01666
01667 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01668 {
01669 latex_escaped_print(*ptr, fout);
01670
01671 if ((i + 1) % cont->ncolumns == 0)
01672 {
01673 fputs(" \\\\\n", fout);
01674 if (opt_border == 3)
01675 fputs("\\hline\n", fout);
01676 if (cancel_pressed)
01677 break;
01678 }
01679 else
01680 fputs(" & ", fout);
01681 }
01682
01683 if (cont->opt->stop_table)
01684 {
01685 printTableFooter *footers = footers_with_default(cont);
01686
01687 if (opt_border == 2)
01688 fputs("\\hline\n", fout);
01689
01690 fputs("\\end{tabular}\n\n\\noindent ", fout);
01691
01692
01693 if (footers && !opt_tuples_only && !cancel_pressed)
01694 {
01695 printTableFooter *f;
01696
01697 for (f = footers; f; f = f->next)
01698 {
01699 latex_escaped_print(f->data, fout);
01700 fputs(" \\\\\n", fout);
01701 }
01702 }
01703
01704 fputc('\n', fout);
01705 }
01706 }
01707
01708
01709 static void
01710 print_latex_longtable_text(const printTableContent *cont, FILE *fout)
01711 {
01712 bool opt_tuples_only = cont->opt->tuples_only;
01713 unsigned short opt_border = cont->opt->border;
01714 unsigned int i;
01715 const char *opt_table_attr = cont->opt->tableAttr;
01716 const char *next_opt_table_attr_char = opt_table_attr;
01717 const char *last_opt_table_attr_char = NULL;
01718 const char *const * ptr;
01719
01720 if (cancel_pressed)
01721 return;
01722
01723 if (opt_border > 3)
01724 opt_border = 3;
01725
01726 if (cont->opt->start_table)
01727 {
01728
01729 fputs("\\begin{longtable}{", fout);
01730
01731 if (opt_border >= 2)
01732 fputs("| ", fout);
01733
01734 for (i = 0; i < cont->ncolumns; i++)
01735 {
01736
01737
01738 if (*(cont->aligns + i) == 'l' && opt_table_attr)
01739 {
01740 #define LONGTABLE_WHITESPACE " \t\n"
01741
01742
01743 next_opt_table_attr_char += strspn(next_opt_table_attr_char,
01744 LONGTABLE_WHITESPACE);
01745
01746 if (next_opt_table_attr_char[0] != '\0')
01747 {
01748 fputs("p{", fout);
01749 fwrite(next_opt_table_attr_char, strcspn(next_opt_table_attr_char,
01750 LONGTABLE_WHITESPACE), 1, fout);
01751 last_opt_table_attr_char = next_opt_table_attr_char;
01752 next_opt_table_attr_char += strcspn(next_opt_table_attr_char,
01753 LONGTABLE_WHITESPACE);
01754 fputs("\\textwidth}", fout);
01755 }
01756
01757 else if (last_opt_table_attr_char != NULL)
01758 {
01759 fputs("p{", fout);
01760 fwrite(last_opt_table_attr_char, strcspn(last_opt_table_attr_char,
01761 LONGTABLE_WHITESPACE), 1, fout);
01762 fputs("\\textwidth}", fout);
01763 }
01764 else
01765 fputc('l', fout);
01766 }
01767 else
01768 fputc(*(cont->aligns + i), fout);
01769
01770 if (opt_border != 0 && i < cont->ncolumns - 1)
01771 fputs(" | ", fout);
01772 }
01773
01774 if (opt_border >= 2)
01775 fputs(" |", fout);
01776
01777 fputs("}\n", fout);
01778
01779
01780 if (!opt_tuples_only)
01781 {
01782
01783 if (opt_border >= 2)
01784 fputs("\\toprule\n", fout);
01785 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
01786 {
01787 if (i != 0)
01788 fputs(" & ", fout);
01789 fputs("\\small\\textbf{\\textit{", fout);
01790 latex_escaped_print(*ptr, fout);
01791 fputs("}}", fout);
01792 }
01793 fputs(" \\\\\n", fout);
01794 fputs("\\midrule\n\\endfirsthead\n", fout);
01795
01796
01797 if (opt_border >= 2)
01798 fputs("\\toprule\n", fout);
01799 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
01800 {
01801 if (i != 0)
01802 fputs(" & ", fout);
01803 fputs("\\small\\textbf{\\textit{", fout);
01804 latex_escaped_print(*ptr, fout);
01805 fputs("}}", fout);
01806 }
01807 fputs(" \\\\\n", fout);
01808
01809 if (opt_border != 3)
01810 fputs("\\midrule\n", fout);
01811 fputs("\\endhead\n", fout);
01812
01813
01814 if (!opt_tuples_only && cont->title)
01815 {
01816
01817 if (opt_border == 2)
01818 fputs("\\bottomrule\n", fout);
01819 fputs("\\caption[", fout);
01820 latex_escaped_print(cont->title, fout);
01821 fputs(" (Continued)]{", fout);
01822 latex_escaped_print(cont->title, fout);
01823 fputs("}\n\\endfoot\n", fout);
01824 if (opt_border == 2)
01825 fputs("\\bottomrule\n", fout);
01826 fputs("\\caption[", fout);
01827 latex_escaped_print(cont->title, fout);
01828 fputs("]{", fout);
01829 latex_escaped_print(cont->title, fout);
01830 fputs("}\n\\endlastfoot\n", fout);
01831 }
01832
01833 else if (opt_border >= 2)
01834 {
01835 fputs("\\bottomrule\n\\endfoot\n", fout);
01836 fputs("\\bottomrule\n\\endlastfoot\n", fout);
01837 }
01838 }
01839 }
01840
01841
01842 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01843 {
01844
01845 if (i != 0 && i % cont->ncolumns != 0)
01846 fputs("\n&\n", fout);
01847 fputs("\\raggedright{", fout);
01848 latex_escaped_print(*ptr, fout);
01849 fputc('}', fout);
01850 if ((i + 1) % cont->ncolumns == 0)
01851 {
01852 fputs(" \\tabularnewline\n", fout);
01853 if (opt_border == 3)
01854 fputs(" \\hline\n", fout);
01855 }
01856 if (cancel_pressed)
01857 break;
01858 }
01859
01860 if (cont->opt->stop_table)
01861 fputs("\\end{longtable}\n", fout);
01862 }
01863
01864
01865 static void
01866 print_latex_vertical(const printTableContent *cont, FILE *fout)
01867 {
01868 bool opt_tuples_only = cont->opt->tuples_only;
01869 unsigned short opt_border = cont->opt->border;
01870 unsigned long record = cont->opt->prior_records + 1;
01871 unsigned int i;
01872 const char *const * ptr;
01873
01874 if (cancel_pressed)
01875 return;
01876
01877 if (opt_border > 2)
01878 opt_border = 2;
01879
01880 if (cont->opt->start_table)
01881 {
01882
01883 if (!opt_tuples_only && cont->title)
01884 {
01885 fputs("\\begin{center}\n", fout);
01886 latex_escaped_print(cont->title, fout);
01887 fputs("\n\\end{center}\n\n", fout);
01888 }
01889
01890
01891 fputs("\\begin{tabular}{", fout);
01892 if (opt_border == 0)
01893 fputs("cl", fout);
01894 else if (opt_border == 1)
01895 fputs("c|l", fout);
01896 else if (opt_border == 2)
01897 fputs("|c|l|", fout);
01898 fputs("}\n", fout);
01899 }
01900
01901
01902 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01903 {
01904
01905 if (i % cont->ncolumns == 0)
01906 {
01907 if (cancel_pressed)
01908 break;
01909 if (!opt_tuples_only)
01910 {
01911 if (opt_border == 2)
01912 {
01913 fputs("\\hline\n", fout);
01914 fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
01915 }
01916 else
01917 fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
01918 }
01919 if (opt_border >= 1)
01920 fputs("\\hline\n", fout);
01921 }
01922
01923 latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
01924 fputs(" & ", fout);
01925 latex_escaped_print(*ptr, fout);
01926 fputs(" \\\\\n", fout);
01927 }
01928
01929 if (cont->opt->stop_table)
01930 {
01931 if (opt_border == 2)
01932 fputs("\\hline\n", fout);
01933
01934 fputs("\\end{tabular}\n\n\\noindent ", fout);
01935
01936
01937 if (cont->footers && !opt_tuples_only && !cancel_pressed)
01938 {
01939 printTableFooter *f;
01940
01941 for (f = cont->footers; f; f = f->next)
01942 {
01943 latex_escaped_print(f->data, fout);
01944 fputs(" \\\\\n", fout);
01945 }
01946 }
01947
01948 fputc('\n', fout);
01949 }
01950 }
01951
01952
01953
01954
01955
01956
01957
01958 static void
01959 troff_ms_escaped_print(const char *in, FILE *fout)
01960 {
01961 const char *p;
01962
01963 for (p = in; *p; p++)
01964 switch (*p)
01965 {
01966 case '\\':
01967 fputs("\\(rs", fout);
01968 break;
01969 default:
01970 fputc(*p, fout);
01971 }
01972 }
01973
01974
01975 static void
01976 print_troff_ms_text(const printTableContent *cont, FILE *fout)
01977 {
01978 bool opt_tuples_only = cont->opt->tuples_only;
01979 unsigned short opt_border = cont->opt->border;
01980 unsigned int i;
01981 const char *const * ptr;
01982
01983 if (cancel_pressed)
01984 return;
01985
01986 if (opt_border > 2)
01987 opt_border = 2;
01988
01989 if (cont->opt->start_table)
01990 {
01991
01992 if (!opt_tuples_only && cont->title)
01993 {
01994 fputs(".LP\n.DS C\n", fout);
01995 troff_ms_escaped_print(cont->title, fout);
01996 fputs("\n.DE\n", fout);
01997 }
01998
01999
02000 fputs(".LP\n.TS\n", fout);
02001 if (opt_border == 2)
02002 fputs("center box;\n", fout);
02003 else
02004 fputs("center;\n", fout);
02005
02006 for (i = 0; i < cont->ncolumns; i++)
02007 {
02008 fputc(*(cont->aligns + i), fout);
02009 if (opt_border > 0 && i < cont->ncolumns - 1)
02010 fputs(" | ", fout);
02011 }
02012 fputs(".\n", fout);
02013
02014
02015 if (!opt_tuples_only)
02016 {
02017 for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
02018 {
02019 if (i != 0)
02020 fputc('\t', fout);
02021 fputs("\\fI", fout);
02022 troff_ms_escaped_print(*ptr, fout);
02023 fputs("\\fP", fout);
02024 }
02025 fputs("\n_\n", fout);
02026 }
02027 }
02028
02029
02030 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
02031 {
02032 troff_ms_escaped_print(*ptr, fout);
02033
02034 if ((i + 1) % cont->ncolumns == 0)
02035 {
02036 fputc('\n', fout);
02037 if (cancel_pressed)
02038 break;
02039 }
02040 else
02041 fputc('\t', fout);
02042 }
02043
02044 if (cont->opt->stop_table)
02045 {
02046 printTableFooter *footers = footers_with_default(cont);
02047
02048 fputs(".TE\n.DS L\n", fout);
02049
02050
02051 if (footers && !opt_tuples_only && !cancel_pressed)
02052 {
02053 printTableFooter *f;
02054
02055 for (f = footers; f; f = f->next)
02056 {
02057 troff_ms_escaped_print(f->data, fout);
02058 fputc('\n', fout);
02059 }
02060 }
02061
02062 fputs(".DE\n", fout);
02063 }
02064 }
02065
02066
02067 static void
02068 print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
02069 {
02070 bool opt_tuples_only = cont->opt->tuples_only;
02071 unsigned short opt_border = cont->opt->border;
02072 unsigned long record = cont->opt->prior_records + 1;
02073 unsigned int i;
02074 const char *const * ptr;
02075 unsigned short current_format = 0;
02076
02077 if (cancel_pressed)
02078 return;
02079
02080 if (opt_border > 2)
02081 opt_border = 2;
02082
02083 if (cont->opt->start_table)
02084 {
02085
02086 if (!opt_tuples_only && cont->title)
02087 {
02088 fputs(".LP\n.DS C\n", fout);
02089 troff_ms_escaped_print(cont->title, fout);
02090 fputs("\n.DE\n", fout);
02091 }
02092
02093
02094 fputs(".LP\n.TS\n", fout);
02095 if (opt_border == 2)
02096 fputs("center box;\n", fout);
02097 else
02098 fputs("center;\n", fout);
02099
02100
02101 if (opt_tuples_only)
02102 fputs("c l;\n", fout);
02103 }
02104 else
02105 current_format = 2;
02106
02107
02108 for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
02109 {
02110
02111 if (i % cont->ncolumns == 0)
02112 {
02113 if (cancel_pressed)
02114 break;
02115 if (!opt_tuples_only)
02116 {
02117 if (current_format != 1)
02118 {
02119 if (opt_border == 2 && record > 1)
02120 fputs("_\n", fout);
02121 if (current_format != 0)
02122 fputs(".T&\n", fout);
02123 fputs("c s.\n", fout);
02124 current_format = 1;
02125 }
02126 fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
02127 }
02128 if (opt_border >= 1)
02129 fputs("_\n", fout);
02130 }
02131
02132 if (!opt_tuples_only)
02133 {
02134 if (current_format != 2)
02135 {
02136 if (current_format != 0)
02137 fputs(".T&\n", fout);
02138 if (opt_border != 1)
02139 fputs("c l.\n", fout);
02140 else
02141 fputs("c | l.\n", fout);
02142 current_format = 2;
02143 }
02144 }
02145
02146 troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
02147 fputc('\t', fout);
02148 troff_ms_escaped_print(*ptr, fout);
02149
02150 fputc('\n', fout);
02151 }
02152
02153 if (cont->opt->stop_table)
02154 {
02155 fputs(".TE\n.DS L\n", fout);
02156
02157
02158 if (cont->footers && !opt_tuples_only && !cancel_pressed)
02159 {
02160 printTableFooter *f;
02161
02162 for (f = cont->footers; f; f = f->next)
02163 {
02164 troff_ms_escaped_print(f->data, fout);
02165 fputc('\n', fout);
02166 }
02167 }
02168
02169 fputs(".DE\n", fout);
02170 }
02171 }
02172
02173
02174
02175
02176
02177
02178
02179
02180
02181
02182
02183
02184 FILE *
02185 PageOutput(int lines, unsigned short int pager)
02186 {
02187
02188 if (pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
02189 {
02190 const char *pagerprog;
02191 FILE *pagerpipe;
02192
02193 #ifdef TIOCGWINSZ
02194 int result;
02195 struct winsize screen_size;
02196
02197 result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
02198
02199
02200 if (result == -1 || lines >= screen_size.ws_row || pager > 1)
02201 {
02202 #endif
02203 pagerprog = getenv("PAGER");
02204 if (!pagerprog)
02205 pagerprog = DEFAULT_PAGER;
02206 #ifndef WIN32
02207 pqsignal(SIGPIPE, SIG_IGN);
02208 #endif
02209 pagerpipe = popen(pagerprog, "w");
02210 if (pagerpipe)
02211 return pagerpipe;
02212 #ifdef TIOCGWINSZ
02213 }
02214 #endif
02215 }
02216
02217 return stdout;
02218 }
02219
02220
02221
02222
02223
02224
02225 void
02226 ClosePager(FILE *pagerpipe)
02227 {
02228 if (pagerpipe && pagerpipe != stdout)
02229 {
02230
02231
02232
02233
02234
02235
02236
02237
02238 if (cancel_pressed)
02239 fprintf(pagerpipe, _("Interrupted\n"));
02240
02241 pclose(pagerpipe);
02242 #ifndef WIN32
02243 pqsignal(SIGPIPE, SIG_DFL);
02244 #endif
02245 }
02246 }
02247
02248
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258 void
02259 printTableInit(printTableContent *const content, const printTableOpt *opt,
02260 const char *title, const int ncolumns, const int nrows)
02261 {
02262 content->opt = opt;
02263 content->title = title;
02264 content->ncolumns = ncolumns;
02265 content->nrows = nrows;
02266
02267 content->headers = pg_malloc0((ncolumns + 1) * sizeof(*content->headers));
02268
02269 content->cells = pg_malloc0((ncolumns * nrows + 1) * sizeof(*content->cells));
02270
02271 content->cellmustfree = NULL;
02272 content->footers = NULL;
02273
02274 content->aligns = pg_malloc0((ncolumns + 1) * sizeof(*content->align));
02275
02276 content->header = content->headers;
02277 content->cell = content->cells;
02278 content->footer = content->footers;
02279 content->align = content->aligns;
02280 content->cellsadded = 0;
02281 }
02282
02283
02284
02285
02286
02287
02288
02289
02290
02291
02292
02293
02294
02295 void
02296 printTableAddHeader(printTableContent *const content, char *header,
02297 const bool translate, const char align)
02298 {
02299 #ifndef ENABLE_NLS
02300 (void) translate;
02301 #endif
02302
02303 if (content->header >= content->headers + content->ncolumns)
02304 {
02305 fprintf(stderr, _("Cannot add header to table content: "
02306 "column count of %d exceeded.\n"),
02307 content->ncolumns);
02308 exit(EXIT_FAILURE);
02309 }
02310
02311 *content->header = (char *) mbvalidate((unsigned char *) header,
02312 content->opt->encoding);
02313 #ifdef ENABLE_NLS
02314 if (translate)
02315 *content->header = _(*content->header);
02316 #endif
02317 content->header++;
02318
02319 *content->align = align;
02320 content->align++;
02321 }
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335 void
02336 printTableAddCell(printTableContent *const content, char *cell,
02337 const bool translate, const bool mustfree)
02338 {
02339 #ifndef ENABLE_NLS
02340 (void) translate;
02341 #endif
02342
02343 if (content->cellsadded >= content->ncolumns * content->nrows)
02344 {
02345 fprintf(stderr, _("Cannot add cell to table content: "
02346 "total cell count of %d exceeded.\n"),
02347 content->ncolumns * content->nrows);
02348 exit(EXIT_FAILURE);
02349 }
02350
02351 *content->cell = (char *) mbvalidate((unsigned char *) cell,
02352 content->opt->encoding);
02353
02354 #ifdef ENABLE_NLS
02355 if (translate)
02356 *content->cell = _(*content->cell);
02357 #endif
02358
02359 if (mustfree)
02360 {
02361 if (content->cellmustfree == NULL)
02362 content->cellmustfree =
02363 pg_malloc0((content->ncolumns * content->nrows + 1) * sizeof(bool));
02364
02365 content->cellmustfree[content->cellsadded] = true;
02366 }
02367 content->cell++;
02368 content->cellsadded++;
02369 }
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380
02381
02382
02383 void
02384 printTableAddFooter(printTableContent *const content, const char *footer)
02385 {
02386 printTableFooter *f;
02387
02388 f = pg_malloc0(sizeof(*f));
02389 f->data = pg_strdup(footer);
02390
02391 if (content->footers == NULL)
02392 content->footers = f;
02393 else
02394 content->footer->next = f;
02395
02396 content->footer = f;
02397 }
02398
02399
02400
02401
02402
02403
02404
02405
02406
02407
02408 void
02409 printTableSetFooter(printTableContent *const content, const char *footer)
02410 {
02411 if (content->footers != NULL)
02412 {
02413 free(content->footer->data);
02414 content->footer->data = pg_strdup(footer);
02415 }
02416 else
02417 printTableAddFooter(content, footer);
02418 }
02419
02420
02421
02422
02423
02424
02425
02426 void
02427 printTableCleanup(printTableContent *const content)
02428 {
02429 if (content->cellmustfree)
02430 {
02431 int i;
02432
02433 for (i = 0; i < content->nrows * content->ncolumns; i++)
02434 {
02435 if (content->cellmustfree[i])
02436 free((char *) content->cells[i]);
02437 }
02438 free(content->cellmustfree);
02439 content->cellmustfree = NULL;
02440 }
02441 free(content->headers);
02442 free(content->cells);
02443 free(content->aligns);
02444
02445 content->opt = NULL;
02446 content->title = NULL;
02447 content->headers = NULL;
02448 content->cells = NULL;
02449 content->aligns = NULL;
02450 content->header = NULL;
02451 content->cell = NULL;
02452 content->align = NULL;
02453
02454 if (content->footers)
02455 {
02456 for (content->footer = content->footers; content->footer;)
02457 {
02458 printTableFooter *f;
02459
02460 f = content->footer;
02461 content->footer = f->next;
02462 free(f->data);
02463 free(f);
02464 }
02465 }
02466 content->footers = NULL;
02467 content->footer = NULL;
02468 }
02469
02470
02471
02472
02473
02474
02475 static void
02476 IsPagerNeeded(const printTableContent *cont, const int extra_lines, bool expanded, FILE **fout,
02477 bool *is_pager)
02478 {
02479 if (*fout == stdout)
02480 {
02481 int lines;
02482
02483 if (expanded)
02484 lines = (cont->ncolumns + 1) * cont->nrows;
02485 else
02486 lines = cont->nrows + 1;
02487
02488 if (!cont->opt->tuples_only)
02489 {
02490 printTableFooter *f;
02491
02492
02493
02494
02495
02496 for (f = cont->footers; f; f = f->next)
02497 lines++;
02498 }
02499
02500 *fout = PageOutput(lines + extra_lines, cont->opt->pager);
02501 *is_pager = (*fout != stdout);
02502 }
02503 else
02504 *is_pager = false;
02505 }
02506
02507
02508
02509
02510 void
02511 printTable(const printTableContent *cont, FILE *fout, FILE *flog)
02512 {
02513 bool is_pager = false;
02514
02515 if (cancel_pressed)
02516 return;
02517
02518 if (cont->opt->format == PRINT_NOTHING)
02519 return;
02520
02521
02522 if (cont->opt->format != PRINT_ALIGNED &&
02523 cont->opt->format != PRINT_WRAPPED)
02524 IsPagerNeeded(cont, 0, (cont->opt->expanded == 1), &fout, &is_pager);
02525
02526
02527
02528 if (flog)
02529 print_aligned_text(cont, flog);
02530
02531 switch (cont->opt->format)
02532 {
02533 case PRINT_UNALIGNED:
02534 if (cont->opt->expanded == 1)
02535 print_unaligned_vertical(cont, fout);
02536 else
02537 print_unaligned_text(cont, fout);
02538 break;
02539 case PRINT_ALIGNED:
02540 case PRINT_WRAPPED:
02541 if (cont->opt->expanded == 1)
02542 print_aligned_vertical(cont, fout);
02543 else
02544 print_aligned_text(cont, fout);
02545 break;
02546 case PRINT_HTML:
02547 if (cont->opt->expanded == 1)
02548 print_html_vertical(cont, fout);
02549 else
02550 print_html_text(cont, fout);
02551 break;
02552 case PRINT_LATEX:
02553 if (cont->opt->expanded == 1)
02554 print_latex_vertical(cont, fout);
02555 else
02556 print_latex_text(cont, fout);
02557 break;
02558 case PRINT_LATEX_LONGTABLE:
02559 if (cont->opt->expanded == 1)
02560 print_latex_vertical(cont, fout);
02561 else
02562 print_latex_longtable_text(cont, fout);
02563 break;
02564 case PRINT_TROFF_MS:
02565 if (cont->opt->expanded == 1)
02566 print_troff_ms_vertical(cont, fout);
02567 else
02568 print_troff_ms_text(cont, fout);
02569 break;
02570 default:
02571 fprintf(stderr, _("invalid output format (internal error): %d"),
02572 cont->opt->format);
02573 exit(EXIT_FAILURE);
02574 }
02575
02576 if (is_pager)
02577 ClosePager(fout);
02578 }
02579
02580
02581
02582
02583
02584
02585 void
02586 printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
02587 {
02588 printTableContent cont;
02589 int i,
02590 r,
02591 c;
02592
02593 if (cancel_pressed)
02594 return;
02595
02596 printTableInit(&cont, &opt->topt, opt->title,
02597 PQnfields(result), PQntuples(result));
02598
02599 for (i = 0; i < cont.ncolumns; i++)
02600 {
02601 char align;
02602 Oid ftype = PQftype(result, i);
02603
02604 switch (ftype)
02605 {
02606 case INT2OID:
02607 case INT4OID:
02608 case INT8OID:
02609 case FLOAT4OID:
02610 case FLOAT8OID:
02611 case NUMERICOID:
02612 case OIDOID:
02613 case XIDOID:
02614 case CIDOID:
02615 case CASHOID:
02616 align = 'r';
02617 break;
02618 default:
02619 align = 'l';
02620 break;
02621 }
02622
02623 printTableAddHeader(&cont, PQfname(result, i),
02624 opt->translate_header, align);
02625 }
02626
02627
02628 for (r = 0; r < cont.nrows; r++)
02629 {
02630 for (c = 0; c < cont.ncolumns; c++)
02631 {
02632 char *cell;
02633 bool mustfree = false;
02634 bool translate;
02635
02636 if (PQgetisnull(result, r, c))
02637 cell = opt->nullPrint ? opt->nullPrint : "";
02638 else
02639 {
02640 cell = PQgetvalue(result, r, c);
02641 if (cont.aligns[c] == 'r' && opt->topt.numericLocale)
02642 {
02643 cell = format_numeric_locale(cell);
02644 mustfree = true;
02645 }
02646 }
02647
02648 translate = (opt->translate_columns && opt->translate_columns[c]);
02649 printTableAddCell(&cont, cell, translate, mustfree);
02650 }
02651 }
02652
02653
02654 if (opt->footers)
02655 {
02656 char **footer;
02657
02658 for (footer = opt->footers; *footer; footer++)
02659 printTableAddFooter(&cont, *footer);
02660 }
02661
02662 printTable(&cont, fout, flog);
02663 printTableCleanup(&cont);
02664 }
02665
02666
02667 void
02668 setDecimalLocale(void)
02669 {
02670 struct lconv *extlconv;
02671
02672 extlconv = localeconv();
02673
02674 if (*extlconv->decimal_point)
02675 decimal_point = pg_strdup(extlconv->decimal_point);
02676 else
02677 decimal_point = ".";
02678 if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
02679 grouping = pg_strdup(extlconv->grouping);
02680 else
02681 grouping = "3";
02682
02683
02684 if (*extlconv->thousands_sep)
02685 thousands_sep = pg_strdup(extlconv->thousands_sep);
02686
02687 else if (strcmp(decimal_point, ",") != 0)
02688 thousands_sep = ",";
02689 else
02690 thousands_sep = ".";
02691 }
02692
02693
02694 const printTextFormat *
02695 get_line_style(const printTableOpt *opt)
02696 {
02697
02698
02699
02700
02701
02702 if (opt->line_style != NULL)
02703 return opt->line_style;
02704 else
02705 return &pg_asciiformat;
02706 }
02707
02708
02709
02710
02711
02712
02713 static int
02714 strlen_max_width(unsigned char *str, int *target_width, int encoding)
02715 {
02716 unsigned char *start = str;
02717 unsigned char *end = str + strlen((char *) str);
02718 int curr_width = 0;
02719
02720 while (str < end)
02721 {
02722 int char_width = PQdsplen((char *) str, encoding);
02723
02724
02725
02726
02727
02728
02729
02730 if (*target_width < curr_width + char_width && curr_width != 0)
02731 break;
02732
02733 curr_width += char_width;
02734
02735 str += PQmblen((char *) str, encoding);
02736 }
02737
02738 *target_width = curr_width;
02739
02740 return str - start;
02741 }