Header And Logo

PostgreSQL
| The world's most advanced open source database.

print.c

Go to the documentation of this file.
00001 /*
00002  * psql - the PostgreSQL interactive terminal
00003  *
00004  * Copyright (c) 2000-2013, PostgreSQL Global Development Group
00005  *
00006  * src/bin/psql/print.c
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>          /* for ioctl() */
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  * We define the cancel_pressed flag in this file, rather than common.c where
00033  * it naturally belongs, because this file is also used by non-psql programs
00034  * (see the bin/scripts/ directory).  In those programs cancel_pressed will
00035  * never become set and will have no effect.
00036  *
00037  * Note: print.c's general strategy for when to check cancel_pressed is to do
00038  * so at completion of each row of output.
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 /* Line style control structures */
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         /* N/A, │, │, │ */
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 /* Local functions */
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 /* Return additional length required for locale-aware numeric output */
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         /* Don't count a leading separator */
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  * Returns the appropriately formatted string in a new allocated block,
00173  * caller must free
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] == '-')       /* skip over sign, affects grouping
00190                                  * calculations */
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         /* Hit decimal point? */
00200         if (my_str[i] == '.')
00201         {
00202             strcpy(&new_str[j], decimal_point);
00203             j += strlen(decimal_point);
00204             /* add fractional part */
00205             strcpy(&new_str[j], &my_str[i] + 1);
00206             break;
00207         }
00208 
00209         /* End of string? */
00210         if (my_str[i] == '\0')
00211         {
00212             new_str[j] = '\0';
00213             break;
00214         }
00215 
00216         /* Add separator? */
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  * fputnbytes: print exactly N bytes to a file
00232  *
00233  * We avoid using %.*s here because it can misbehave if the data
00234  * is not valid in what libc thinks is the prevailing encoding.
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  * Return the list of explicitly-requested footers or, when applicable, the
00256  * default "(xx rows)" footer.  Always omit the default footer when given
00257  * non-default footers, "\pset footer off", or a specific instruction to that
00258  * effect from a calling backslash command.  Vertical formats number each row,
00259  * making the default footer redundant; they do not call this function.
00260  *
00261  * The return value may point to static storage; do not keep it across calls.
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 /* Unaligned text        */
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         /* print title */
00301         if (!opt_tuples_only && cont->title)
00302         {
00303             fputs(cont->title, fout);
00304             print_separator(cont->opt->recordSep, fout);
00305         }
00306 
00307         /* print headers */
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         /* assume continuing printout */
00321         need_recordsep = true;
00322 
00323     /* print cells */
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     /* print footers */
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          * The last record is terminated by a newline, independent of the set
00364          * record separator.  But when the record separator is a zero byte, we
00365          * use that (compatible with find -print0 and xargs).
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         /* print title */
00392         if (!opt_tuples_only && cont->title)
00393         {
00394             fputs(cont->title, fout);
00395             need_recordsep = true;
00396         }
00397     }
00398     else
00399         /* assume continuing printout */
00400         need_recordsep = true;
00401 
00402     /* print records */
00403     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
00404     {
00405         if (need_recordsep)
00406         {
00407             /* record separator is 2 occurrences of recordsep in this mode */
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         /* print footers */
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         /* see above in print_unaligned_text() */
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 /* Aligned text     */
00454 /********************/
00455 
00456 
00457 /* draw "line" */
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  *  Print pretty boxes around cells.
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, /* value split by newlines */
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;      /* pointers to line pointer per column */
00531 
00532     bool       *header_done;    /* Have all header lines been output? */
00533     int        *bytes_output;   /* Bytes output for column value */
00534     printTextLineWrap *wrap;    /* Wrap status for each column */
00535     int         output_columns = 0;     /* Width of interactive console */
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     /* scan all column headers, find maximum width and max max_nl_lines */
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     /* Add height of tallest header column */
00597     extra_output_lines += extra_row_output_lines;
00598     extra_row_output_lines = 0;
00599 
00600     /* scan all cells, find maximum width, compute cell_count */
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     /* If we have rows, compute average */
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     /* adjust the total display width based on border style */
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      * At this point: max_width[] contains the max width of each column,
00646      * max_nl_lines[] contains the max number of lines in each column,
00647      * max_bytes[] contains the maximum storage space for formatting strings,
00648      * width_total contains the giant width sum.  Now we allocate some memory
00649      * for line pointers.
00650      */
00651     for (i = 0; i < col_count; i++)
00652     {
00653         /* Add entry for ptr == NULL array termination */
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     /* Default word wrap to the full width, i.e. no word wrap */
00663     for (i = 0; i < col_count; i++)
00664         width_wrap[i] = max_width[i];
00665 
00666     /*
00667      * Choose target output width: \pset columns, or $COLUMNS, or ioctl
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          * Optional optimized word wrap. Shrink columns with a high max/avg
00690          * ratio.  Slighly bias against wider columns. (Increases chance a
00691          * narrow column will fit in its cell.)  If available columns is
00692          * positive...  and greater than the width of the unshrinkable column
00693          * headers
00694          */
00695         if (output_columns > 0 && output_columns >= total_header_width)
00696         {
00697             /* While there is still excess width... */
00698             while (width_total > output_columns)
00699             {
00700                 double      max_ratio = 0;
00701                 int         worst_col = -1;
00702 
00703                 /*
00704                  * Find column that has the highest ratio of its maximum width
00705                  * compared to its average width.  This tells us which column
00706                  * will produce the fewest wrapped values if shortened.
00707                  * width_wrap starts as equal to max_width.
00708                  */
00709                 for (i = 0; i < col_count; i++)
00710                 {
00711                     if (width_average[i] && width_wrap[i] > width_header[i])
00712                     {
00713                         /* Penalize wide columns by 1% of their width */
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                 /* Exit loop if we can't squeeze any more. */
00727                 if (worst_col == -1)
00728                     break;
00729 
00730                 /* Decrease width of target column by one. */
00731                 width_wrap[worst_col]--;
00732                 width_total--;
00733             }
00734         }
00735     }
00736 
00737     /*
00738      * If in expanded auto mode, we have now calculated the expected width, so
00739      * we can now escape to vertical mode if necessary.
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     /* If we wrapped beyond the display width, use the pager */
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);   /* force pager */
00753         is_pager = true;
00754     }
00755 
00756     /* Check if newlines or our wrapping now need the pager */
00757     if (!is_pager)
00758     {
00759         /* scan all cells, find maximum width, compute cell_count */
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              * A row can have both wrapping and newlines that cause it to
00771              * display across multiple lines.  We check for both cases below.
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             /* i is the current column number: increment with wrap */
00783             if (++i >= col_count)
00784             {
00785                 i = 0;
00786                 /* At last column of each row, add tallest column height */
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     /* time to output */
00795     if (cont->opt->start_table)
00796     {
00797         /* print title */
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                 /* Aligned */
00807                 fprintf(fout, "%s\n", cont->title);
00808             else
00809                 /* Centered */
00810                 fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
00811                         cont->title);
00812         }
00813 
00814         /* print headers */
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                         /* centered */
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     /* print cells, one loop per row */
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          * Format each cell.
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          * Each time through this loop, one display line is output. It can
00905          * either be a full value or a partial value if embedded newlines
00906          * exist or if 'format=wrapping' mode is enabled.
00907          */
00908         do
00909         {
00910             more_lines = false;
00911 
00912             /* left border */
00913             if (opt_border == 2)
00914                 fputs(dformat->leftvrule, fout);
00915 
00916             /* for each column */
00917             for (j = 0; j < col_count; j++)
00918             {
00919                 /* We have a valid array element, so index it */
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                 /* Print left-hand wrap or newline mark */
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                     /* Past newline lines so just pad for other columns */
00939                     if (finalspaces)
00940                         fprintf(fout, "%*s", chars_to_output, "");
00941                 }
00942                 else
00943                 {
00944                     /* Get strlen() of the characters up to width_wrap */
00945                     bytes_to_output =
00946                         strlen_max_width(this_line->ptr + bytes_output[j],
00947                                          &chars_to_output, encoding);
00948 
00949                     /*
00950                      * If we exceeded width_wrap, it means the display width
00951                      * of a single character was wider than our target width.
00952                      * In that case, we have to pretend we are only printing
00953                      * the target display width and make the best of it.
00954                      */
00955                     if (chars_to_output > width_wrap[j])
00956                         chars_to_output = width_wrap[j];
00957 
00958                     if (cont->aligns[j] == 'r') /* Right aligned cell */
00959                     {
00960                         /* spaces first */
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    /* Left aligned cell */
00967                     {
00968                         /* spaces second */
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                     /* Do we have more text to wrap? */
00977                     if (*(this_line->ptr + bytes_output[j]) != '\0')
00978                         more_lines = true;
00979                     else
00980                     {
00981                         /* Advance to next newline line */
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                 /* Determine next line's wrap status for this column */
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                  * If left-aligned, pad out remaining space if needed (not
01001                  * last column, and/or wrap marks required).
01002                  */
01003                 if (cont->aligns[j] != 'r')     /* Left aligned cell */
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                 /* Print right-hand wrap or newline mark */
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                 /* Print column divider, if not the last column */
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             /* end-of-row border */
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         /* print footers */
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     /* clean up */
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      * Deal with the pager here instead of in printTable(), because we could
01181      * get here via print_aligned_text() in expanded auto mode, and so we have
01182      * to recalcuate the pager requirement based on vertical output.
01183      */
01184     IsPagerNeeded(cont, 0, true, &fout, &is_pager);
01185 
01186     /* Find the maximum dimensions for the headers */
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     /* find longest data cell */
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      * We now have all the information we need to setup the formatting
01222      * structures
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         /* print title */
01233         if (!opt_tuples_only && cont->title)
01234             fprintf(fout, "%s\n", cont->title);
01235     }
01236 
01237     /* print records */
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         /* Format the header */
01266         pg_wcsformat((const unsigned char *) cont->headers[i % cont->ncolumns],
01267                      strlen(cont->headers[i % cont->ncolumns]),
01268                      encoding, hlineptr, hheight);
01269         /* Format the data */
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         /* print footers */
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 /* HTML printing ******/
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("&amp;", fout);
01365                 break;
01366             case '<':
01367                 fputs("&lt;", fout);
01368                 break;
01369             case '>':
01370                 fputs("&gt;", fout);
01371                 break;
01372             case '\n':
01373                 fputs("<br />\n", fout);
01374                 break;
01375             case '"':
01376                 fputs("&quot;", fout);
01377                 break;
01378             case ' ':
01379                 /* protect leading space, for EXPLAIN output */
01380                 if (leading_space)
01381                     fputs("&nbsp;", 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         /* print title */
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         /* print headers */
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     /* print cells */
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         /* is string only whitespace? */
01447         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
01448             fputs("&nbsp; ", 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         /* print footers */
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         /* print title */
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     /* print records */
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\">&nbsp;</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         /* is string only whitespace? */
01533         if ((*ptr)[strspn(*ptr, " \t")] == '\0')
01534             fputs("&nbsp; ", 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         /* print footers */
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 /* LaTeX                 */
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         /* print title */
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         /* begin environment and set alignments and borders */
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         /* print headers */
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     /* print cells */
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         /* print footers */
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         /* begin environment and set alignments and borders */
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             /* longtable supports either a width (p) or an alignment (l/r) */
01737             /* Are we left-justified and was a proportional width specified? */
01738             if (*(cont->aligns + i) == 'l' && opt_table_attr)
01739             {
01740 #define LONGTABLE_WHITESPACE    " \t\n"
01741 
01742                 /* advance over whitespace */
01743                 next_opt_table_attr_char += strspn(next_opt_table_attr_char,
01744                                                    LONGTABLE_WHITESPACE);
01745                 /* We have a value? */
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                 /* use previous value */
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         /* print headers */
01780         if (!opt_tuples_only)
01781         {
01782             /* firsthead */
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             /* secondary heads */
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             /* If the line under the row already appeared, don't do another */
01809             if (opt_border != 3)
01810                 fputs("\\midrule\n", fout);
01811             fputs("\\endhead\n", fout);
01812 
01813             /* table name, caption? */
01814             if (!opt_tuples_only && cont->title)
01815             {
01816                 /* Don't output if we are printing a line under each row */
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             /* output bottom table line? */
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     /* print cells */
01842     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01843     {
01844         /* Add a line under each row? */
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         /* print title */
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         /* begin environment and set alignments and borders */
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     /* print records */
01902     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
01903     {
01904         /* new record */
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         /* print footers */
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 /* Troff -ms         */
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         /* print title */
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         /* begin environment and set alignments and borders */
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         /* print headers */
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     /* print cells */
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         /* print footers */
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;  /* 0=none, 1=header, 2=body */
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         /* print title */
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         /* begin environment and set alignments and borders */
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         /* basic format */
02101         if (opt_tuples_only)
02102             fputs("c l;\n", fout);
02103     }
02104     else
02105         current_format = 2;     /* assume tuples printed already */
02106 
02107     /* print records */
02108     for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
02109     {
02110         /* new record */
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         /* print footers */
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 /* Public functions     */
02176 /********************************/
02177 
02178 
02179 /*
02180  * PageOutput
02181  *
02182  * Tests if pager is needed and returns appropriate FILE pointer.
02183  */
02184 FILE *
02185 PageOutput(int lines, unsigned short int pager)
02186 {
02187     /* check whether we need / can / are supposed to use pager */
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         /* >= accounts for a one-line prompt */
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  * ClosePager
02222  *
02223  * Close previously opened pager pipe, if any
02224  */
02225 void
02226 ClosePager(FILE *pagerpipe)
02227 {
02228     if (pagerpipe && pagerpipe != stdout)
02229     {
02230         /*
02231          * If printing was canceled midstream, warn about it.
02232          *
02233          * Some pagers like less use Ctrl-C as part of their command set. Even
02234          * so, we abort our processing and warn the user what we did.  If the
02235          * pager quit as a result of the SIGINT, this message won't go
02236          * anywhere ...
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  * Initialise a table contents struct.
02250  *      Must be called before any other printTable method is used.
02251  *
02252  * The title is not duplicated; the caller must ensure that the buffer
02253  * is available for the lifetime of the printTableContent struct.
02254  *
02255  * If you call this, you must call printTableCleanup once you're done with the
02256  * table.
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  * Add a header to the table.
02285  *
02286  * Headers are not duplicated; you must ensure that the header string is
02287  * available for the lifetime of the printTableContent struct.
02288  *
02289  * If translate is true, the function will pass the header through gettext.
02290  * Otherwise, the header will not be translated.
02291  *
02292  * align is either 'l' or 'r', and specifies the alignment for cells in this
02293  * column.
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;           /* unused parameter */
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  * Add a cell to the table.
02325  *
02326  * Cells are not duplicated; you must ensure that the cell string is available
02327  * for the lifetime of the printTableContent struct.
02328  *
02329  * If translate is true, the function will pass the cell through gettext.
02330  * Otherwise, the cell will not be translated.
02331  *
02332  * If mustfree is true, the cell string is freed by printTableCleanup().
02333  * Note: Automatic freeing of translatable strings is not supported.
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;           /* unused parameter */
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  * Add a footer to the table.
02373  *
02374  * Footers are added as elements of a singly-linked list, and the content is
02375  * strdup'd, so there is no need to keep the original footer string around.
02376  *
02377  * Footers are never translated by the function.  If you want the footer
02378  * translated you must do so yourself, before calling printTableAddFooter.  The
02379  * reason this works differently to headers and cells is that footers tend to
02380  * be made of up individually translated components, rather than being
02381  * translated as a whole.
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  * Change the content of the last-added footer.
02401  *
02402  * The current contents of the last-added footer are freed, and replaced by the
02403  * content given in *footer.  If there was no previous footer, add a new one.
02404  *
02405  * The content is strdup'd, so there is no need to keep the original string
02406  * around.
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  * Free all memory allocated to this struct.
02422  *
02423  * Once this has been called, the struct is unusable unless you pass it to
02424  * printTableInit() again.
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  * IsPagerNeeded
02472  *
02473  * Setup pager if required
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              * FIXME -- this is slightly bogus: it counts the number of
02494              * footers, not the number of lines in them.
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  * Use this to print just any table in the supported formats.
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     /* print_aligned_*() handles the pager themselves */
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     /* print the stuff */
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  * Use this to print query results
02582  *
02583  * It calls printTable with all the things set straight.
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     /* set cells */
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     /* set footers */
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 = ".";    /* SQL output standard */
02678     if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
02679         grouping = pg_strdup(extlconv->grouping);
02680     else
02681         grouping = "3";         /* most common */
02682 
02683     /* similar code exists in formatting.c */
02684     if (*extlconv->thousands_sep)
02685         thousands_sep = pg_strdup(extlconv->thousands_sep);
02686     /* Make sure thousands separator doesn't match decimal point symbol. */
02687     else if (strcmp(decimal_point, ",") != 0)
02688         thousands_sep = ",";
02689     else
02690         thousands_sep = ".";
02691 }
02692 
02693 /* get selected or default line style */
02694 const printTextFormat *
02695 get_line_style(const printTableOpt *opt)
02696 {
02697     /*
02698      * Note: this function mainly exists to preserve the convention that a
02699      * printTableOpt struct can be initialized to zeroes to get default
02700      * behavior.
02701      */
02702     if (opt->line_style != NULL)
02703         return opt->line_style;
02704     else
02705         return &pg_asciiformat;
02706 }
02707 
02708 /*
02709  * Compute the byte distance to the end of the string or *target_width
02710  * display character positions, whichever comes first.  Update *target_width
02711  * to be the number of display character positions actually filled.
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          * If the display width of the new character causes the string to
02726          * exceed its target width, skip it and return.  However, if this is
02727          * the first character of the string (curr_width == 0), we have to
02728          * accept it.
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 }