~ubuntu-branches/ubuntu/lucid/postgresql-8.4/lucid-security

« back to all changes in this revision

Viewing changes to src/bin/psql/print.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psql - the PostgreSQL interactive terminal
 
3
 *
 
4
 * Copyright (c) 2000-2009, PostgreSQL Global Development Group
 
5
 *
 
6
 * $PostgreSQL$
 
7
 */
 
8
#include "postgres_fe.h"
 
9
 
 
10
#include <limits.h>
 
11
#include <math.h>
 
12
#include <signal.h>
 
13
#include <unistd.h>
 
14
 
 
15
#ifndef WIN32
 
16
#include <sys/ioctl.h>                  /* for ioctl() */
 
17
#endif
 
18
 
 
19
#ifdef HAVE_TERMIOS_H
 
20
#include <termios.h>
 
21
#endif
 
22
 
 
23
#include <locale.h>
 
24
 
 
25
#include "catalog/pg_type.h"
 
26
#include "pqsignal.h"
 
27
 
 
28
#include "common.h"
 
29
#include "mbprint.h"
 
30
#include "print.h"
 
31
 
 
32
/*
 
33
 * We define the cancel_pressed flag in this file, rather than common.c where
 
34
 * it naturally belongs, because this file is also used by non-psql programs
 
35
 * (see the bin/scripts/ directory).  In those programs cancel_pressed will
 
36
 * never become set and will have no effect.
 
37
 *
 
38
 * Note: print.c's general strategy for when to check cancel_pressed is to do
 
39
 * so at completion of each row of output.
 
40
 */
 
41
volatile bool cancel_pressed = false;
 
42
 
 
43
static char *decimal_point;
 
44
static char *grouping;
 
45
static char *thousands_sep;
 
46
 
 
47
/* Local functions */
 
48
static int strlen_max_width(unsigned char *str, int *target_width, int encoding);
 
49
static void IsPagerNeeded(const printTableContent *cont, const int extra_lines,
 
50
                                                  FILE **fout, bool *is_pager);
 
51
 
 
52
 
 
53
static void *
 
54
pg_local_malloc(size_t size)
 
55
{
 
56
        void       *tmp;
 
57
 
 
58
        tmp = malloc(size);
 
59
        if (!tmp)
 
60
        {
 
61
                fprintf(stderr, _("out of memory\n"));
 
62
                exit(EXIT_FAILURE);
 
63
        }
 
64
        return tmp;
 
65
}
 
66
 
 
67
static void *
 
68
pg_local_calloc(int count, size_t size)
 
69
{
 
70
        void       *tmp;
 
71
 
 
72
        tmp = calloc(count, size);
 
73
        if (!tmp)
 
74
        {
 
75
                fprintf(stderr, _("out of memory\n"));
 
76
                exit(EXIT_FAILURE);
 
77
        }
 
78
        return tmp;
 
79
}
 
80
 
 
81
static int
 
82
integer_digits(const char *my_str)
 
83
{
 
84
        int                     frac_len;
 
85
 
 
86
        if (my_str[0] == '-')
 
87
                my_str++;
 
88
 
 
89
        frac_len = strchr(my_str, '.') ? strlen(strchr(my_str, '.')) : 0;
 
90
 
 
91
        return strlen(my_str) - frac_len;
 
92
}
 
93
 
 
94
/* Return additional length required for locale-aware numeric output */
 
95
static int
 
96
additional_numeric_locale_len(const char *my_str)
 
97
{
 
98
        int                     int_len = integer_digits(my_str),
 
99
                                len = 0;
 
100
        int                     groupdigits = atoi(grouping);
 
101
 
 
102
        if (int_len > 0)
 
103
                /* Don't count a leading separator */
 
104
                len = (int_len / groupdigits - (int_len % groupdigits == 0)) *
 
105
                        strlen(thousands_sep);
 
106
 
 
107
        if (strchr(my_str, '.') != NULL)
 
108
                len += strlen(decimal_point) - strlen(".");
 
109
 
 
110
        return len;
 
111
}
 
112
 
 
113
static int
 
114
strlen_with_numeric_locale(const char *my_str)
 
115
{
 
116
        return strlen(my_str) + additional_numeric_locale_len(my_str);
 
117
}
 
118
 
 
119
/* Returns the appropriately formatted string in a new allocated block, caller must free */
 
120
static char *
 
121
format_numeric_locale(const char *my_str)
 
122
{
 
123
        int                     i,
 
124
                                j,
 
125
                                int_len = integer_digits(my_str),
 
126
                                leading_digits;
 
127
        int                     groupdigits = atoi(grouping);
 
128
        int                     new_str_start = 0;
 
129
        char       *new_str = new_str = pg_local_malloc(
 
130
                                                                         strlen_with_numeric_locale(my_str) + 1);
 
131
 
 
132
        leading_digits = (int_len % groupdigits != 0) ?
 
133
                int_len % groupdigits : groupdigits;
 
134
 
 
135
        if (my_str[0] == '-')           /* skip over sign, affects grouping
 
136
                                                                 * calculations */
 
137
        {
 
138
                new_str[0] = my_str[0];
 
139
                my_str++;
 
140
                new_str_start = 1;
 
141
        }
 
142
 
 
143
        for (i = 0, j = new_str_start;; i++, j++)
 
144
        {
 
145
                /* Hit decimal point? */
 
146
                if (my_str[i] == '.')
 
147
                {
 
148
                        strcpy(&new_str[j], decimal_point);
 
149
                        j += strlen(decimal_point);
 
150
                        /* add fractional part */
 
151
                        strcpy(&new_str[j], &my_str[i] + 1);
 
152
                        break;
 
153
                }
 
154
 
 
155
                /* End of string? */
 
156
                if (my_str[i] == '\0')
 
157
                {
 
158
                        new_str[j] = '\0';
 
159
                        break;
 
160
                }
 
161
 
 
162
                /* Add separator? */
 
163
                if (i != 0 && (i - leading_digits) % groupdigits == 0)
 
164
                {
 
165
                        strcpy(&new_str[j], thousands_sep);
 
166
                        j += strlen(thousands_sep);
 
167
                }
 
168
 
 
169
                new_str[j] = my_str[i];
 
170
        }
 
171
 
 
172
        return new_str;
 
173
}
 
174
 
 
175
/*************************/
 
176
/* Unaligned text                */
 
177
/*************************/
 
178
 
 
179
 
 
180
static void
 
181
print_unaligned_text(const printTableContent *cont, FILE *fout)
 
182
{
 
183
        const char *opt_fieldsep = cont->opt->fieldSep;
 
184
        const char *opt_recordsep = cont->opt->recordSep;
 
185
        bool            opt_tuples_only = cont->opt->tuples_only;
 
186
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
187
        unsigned int i;
 
188
        const char *const * ptr;
 
189
        bool            need_recordsep = false;
 
190
 
 
191
        if (cancel_pressed)
 
192
                return;
 
193
 
 
194
        if (!opt_fieldsep)
 
195
                opt_fieldsep = "";
 
196
        if (!opt_recordsep)
 
197
                opt_recordsep = "";
 
198
 
 
199
        if (cont->opt->start_table)
 
200
        {
 
201
                /* print title */
 
202
                if (!opt_tuples_only && cont->title)
 
203
                        fprintf(fout, "%s%s", cont->title, opt_recordsep);
 
204
 
 
205
                /* print headers */
 
206
                if (!opt_tuples_only)
 
207
                {
 
208
                        for (ptr = cont->headers; *ptr; ptr++)
 
209
                        {
 
210
                                if (ptr != cont->headers)
 
211
                                        fputs(opt_fieldsep, fout);
 
212
                                fputs(*ptr, fout);
 
213
                        }
 
214
                        need_recordsep = true;
 
215
                }
 
216
        }
 
217
        else
 
218
                /* assume continuing printout */
 
219
                need_recordsep = true;
 
220
 
 
221
        /* print cells */
 
222
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
223
        {
 
224
                if (need_recordsep)
 
225
                {
 
226
                        fputs(opt_recordsep, fout);
 
227
                        need_recordsep = false;
 
228
                        if (cancel_pressed)
 
229
                                break;
 
230
                }
 
231
                if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
232
                {
 
233
                        char       *my_cell = format_numeric_locale(*ptr);
 
234
 
 
235
                        fputs(my_cell, fout);
 
236
                        free(my_cell);
 
237
                }
 
238
                else
 
239
                        fputs(*ptr, fout);
 
240
 
 
241
                if ((i + 1) % cont->ncolumns)
 
242
                        fputs(opt_fieldsep, fout);
 
243
                else
 
244
                        need_recordsep = true;
 
245
        }
 
246
 
 
247
        /* print footers */
 
248
        if (cont->opt->stop_table)
 
249
        {
 
250
                if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
 
251
                {
 
252
                        printTableFooter *f;
 
253
 
 
254
                        for (f = cont->footers; f; f = f->next)
 
255
                        {
 
256
                                if (need_recordsep)
 
257
                                {
 
258
                                        fputs(opt_recordsep, fout);
 
259
                                        need_recordsep = false;
 
260
                                }
 
261
                                fputs(f->data, fout);
 
262
                                need_recordsep = true;
 
263
                        }
 
264
                }
 
265
                /* the last record needs to be concluded with a newline */
 
266
                if (need_recordsep)
 
267
                        fputc('\n', fout);
 
268
        }
 
269
}
 
270
 
 
271
 
 
272
static void
 
273
print_unaligned_vertical(const printTableContent *cont, FILE *fout)
 
274
{
 
275
        const char *opt_fieldsep = cont->opt->fieldSep;
 
276
        const char *opt_recordsep = cont->opt->recordSep;
 
277
        bool            opt_tuples_only = cont->opt->tuples_only;
 
278
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
279
        unsigned int i;
 
280
        const char *const * ptr;
 
281
        bool            need_recordsep = false;
 
282
 
 
283
        if (cancel_pressed)
 
284
                return;
 
285
 
 
286
        if (!opt_fieldsep)
 
287
                opt_fieldsep = "";
 
288
        if (!opt_recordsep)
 
289
                opt_recordsep = "";
 
290
 
 
291
        if (cont->opt->start_table)
 
292
        {
 
293
                /* print title */
 
294
                if (!opt_tuples_only && cont->title)
 
295
                {
 
296
                        fputs(cont->title, fout);
 
297
                        need_recordsep = true;
 
298
                }
 
299
        }
 
300
        else
 
301
                /* assume continuing printout */
 
302
                need_recordsep = true;
 
303
 
 
304
        /* print records */
 
305
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
306
        {
 
307
                if (need_recordsep)
 
308
                {
 
309
                        /* record separator is 2 occurrences of recordsep in this mode */
 
310
                        fputs(opt_recordsep, fout);
 
311
                        fputs(opt_recordsep, fout);
 
312
                        need_recordsep = false;
 
313
                        if (cancel_pressed)
 
314
                                break;
 
315
                }
 
316
 
 
317
                fputs(cont->headers[i % cont->ncolumns], fout);
 
318
                fputs(opt_fieldsep, fout);
 
319
                if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
320
                {
 
321
                        char       *my_cell = format_numeric_locale(*ptr);
 
322
 
 
323
                        fputs(my_cell, fout);
 
324
                        free(my_cell);
 
325
                }
 
326
                else
 
327
                        fputs(*ptr, fout);
 
328
 
 
329
                if ((i + 1) % cont->ncolumns)
 
330
                        fputs(opt_recordsep, fout);
 
331
                else
 
332
                        need_recordsep = true;
 
333
        }
 
334
 
 
335
        if (cont->opt->stop_table)
 
336
        {
 
337
                /* print footers */
 
338
                if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
 
339
                {
 
340
                        printTableFooter *f;
 
341
 
 
342
                        fputs(opt_recordsep, fout);
 
343
                        for (f = cont->footers; f; f = f->next)
 
344
                        {
 
345
                                fputs(opt_recordsep, fout);
 
346
                                fputs(f->data, fout);
 
347
                        }
 
348
                }
 
349
 
 
350
                fputc('\n', fout);
 
351
        }
 
352
}
 
353
 
 
354
 
 
355
/********************/
 
356
/* Aligned text         */
 
357
/********************/
 
358
 
 
359
 
 
360
/* draw "line" */
 
361
static void
 
362
_print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths,
 
363
                                           unsigned short border, FILE *fout)
 
364
{
 
365
        unsigned int i,
 
366
                                j;
 
367
 
 
368
        if (border == 1)
 
369
                fputc('-', fout);
 
370
        else if (border == 2)
 
371
                fputs("+-", fout);
 
372
 
 
373
        for (i = 0; i < ncolumns; i++)
 
374
        {
 
375
                for (j = 0; j < widths[i]; j++)
 
376
                        fputc('-', fout);
 
377
 
 
378
                if (i < ncolumns - 1)
 
379
                {
 
380
                        if (border == 0)
 
381
                                fputc(' ', fout);
 
382
                        else
 
383
                                fputs("-+-", fout);
 
384
                }
 
385
        }
 
386
 
 
387
        if (border == 2)
 
388
                fputs("-+", fout);
 
389
        else if (border == 1)
 
390
                fputc('-', fout);
 
391
 
 
392
        fputc('\n', fout);
 
393
}
 
394
 
 
395
 
 
396
/*
 
397
 *      Print pretty boxes around cells.
 
398
 */
 
399
static void
 
400
print_aligned_text(const printTableContent *cont, FILE *fout)
 
401
{
 
402
        bool            opt_tuples_only = cont->opt->tuples_only;
 
403
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
404
        int                     encoding = cont->opt->encoding;
 
405
        unsigned short opt_border = cont->opt->border;
 
406
 
 
407
        unsigned int col_count = 0, cell_count = 0;
 
408
 
 
409
        unsigned int i,
 
410
                                j;
 
411
 
 
412
        unsigned int *width_header,
 
413
                           *max_width,
 
414
                           *width_wrap,
 
415
                           *width_average;
 
416
        unsigned int *max_nl_lines,     /* value split by newlines */
 
417
                                *curr_nl_line,
 
418
                                *max_bytes;
 
419
        unsigned char **format_buf;
 
420
        unsigned int width_total;
 
421
        unsigned int total_header_width;
 
422
        unsigned int extra_row_output_lines = 0;
 
423
        unsigned int extra_output_lines = 0;
 
424
 
 
425
        const char * const *ptr;
 
426
 
 
427
        struct lineptr **col_lineptrs;          /* pointers to line pointer per column */
 
428
 
 
429
        bool       *header_done;        /* Have all header lines been output? */
 
430
        int                *bytes_output;       /* Bytes output for column value */
 
431
        int                     output_columns = 0;     /* Width of interactive console */
 
432
        bool            is_pager = false;
 
433
 
 
434
        if (cancel_pressed)
 
435
                return;
 
436
 
 
437
        if (opt_border > 2)
 
438
                opt_border = 2;
 
439
 
 
440
        if (cont->ncolumns > 0)
 
441
        {
 
442
                col_count = cont->ncolumns;
 
443
                width_header = pg_local_calloc(col_count, sizeof(*width_header));
 
444
                width_average = pg_local_calloc(col_count, sizeof(*width_average));
 
445
                max_width = pg_local_calloc(col_count, sizeof(*max_width));
 
446
                width_wrap = pg_local_calloc(col_count, sizeof(*width_wrap));
 
447
                max_nl_lines = pg_local_calloc(col_count, sizeof(*max_nl_lines));
 
448
                curr_nl_line = pg_local_calloc(col_count, sizeof(*curr_nl_line));
 
449
                col_lineptrs = pg_local_calloc(col_count, sizeof(*col_lineptrs));
 
450
                max_bytes = pg_local_calloc(col_count, sizeof(*max_bytes));
 
451
                format_buf = pg_local_calloc(col_count, sizeof(*format_buf));
 
452
                header_done = pg_local_calloc(col_count, sizeof(*header_done));
 
453
                bytes_output = pg_local_calloc(col_count, sizeof(*bytes_output));
 
454
        }
 
455
        else
 
456
        {
 
457
                width_header = NULL;
 
458
                width_average = NULL;
 
459
                max_width = NULL;
 
460
                width_wrap = NULL;
 
461
                max_nl_lines = NULL;
 
462
                curr_nl_line = NULL;
 
463
                col_lineptrs = NULL;
 
464
                max_bytes = NULL;
 
465
                format_buf = NULL;
 
466
                header_done = NULL;
 
467
                bytes_output = NULL;
 
468
        }
 
469
 
 
470
        /* scan all column headers, find maximum width and max max_nl_lines */
 
471
        for (i = 0; i < col_count; i++)
 
472
        {
 
473
                int                     width,
 
474
                                        nl_lines,
 
475
                                        bytes_required;
 
476
 
 
477
                pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
 
478
                                   encoding, &width, &nl_lines, &bytes_required);
 
479
                if (width > max_width[i])
 
480
                        max_width[i] = width;
 
481
                if (nl_lines > max_nl_lines[i])
 
482
                        max_nl_lines[i] = nl_lines;
 
483
                if (bytes_required > max_bytes[i])
 
484
                        max_bytes[i] = bytes_required;
 
485
                if (nl_lines > extra_row_output_lines)
 
486
                        extra_row_output_lines = nl_lines;
 
487
 
 
488
                width_header[i] = width;
 
489
        }
 
490
        /* Add height of tallest header column */
 
491
        extra_output_lines += extra_row_output_lines;
 
492
        extra_row_output_lines = 0;
 
493
 
 
494
        /* scan all cells, find maximum width, compute cell_count */
 
495
        for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
 
496
        {
 
497
                int                     width,
 
498
                                        nl_lines,
 
499
                                        bytes_required;
 
500
 
 
501
                pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
 
502
                                   &width, &nl_lines, &bytes_required);
 
503
                if (opt_numeric_locale && cont->aligns[i % col_count] == 'r')
 
504
                {
 
505
                        width += additional_numeric_locale_len(*ptr);
 
506
                        bytes_required += additional_numeric_locale_len(*ptr);
 
507
                }
 
508
 
 
509
                if (width > max_width[i % col_count])
 
510
                        max_width[i % col_count] = width;
 
511
                if (nl_lines > max_nl_lines[i % col_count])
 
512
                        max_nl_lines[i % col_count] = nl_lines;
 
513
                if (bytes_required > max_bytes[i % col_count])
 
514
                        max_bytes[i % col_count] = bytes_required;
 
515
 
 
516
                width_average[i % col_count] += width;
 
517
        }
 
518
 
 
519
        /* If we have rows, compute average */
 
520
        if (col_count != 0 && cell_count != 0)
 
521
        {
 
522
                int rows = cell_count / col_count;
 
523
                
 
524
                for (i = 0; i < col_count; i++)
 
525
                        width_average[i] /= rows;
 
526
        }
 
527
 
 
528
        /* adjust the total display width based on border style */
 
529
        if (opt_border == 0)
 
530
                width_total = col_count - 1;
 
531
        else if (opt_border == 1)
 
532
                width_total = col_count * 3 - 1;
 
533
        else
 
534
                width_total = col_count * 3 + 1;
 
535
        total_header_width = width_total;
 
536
 
 
537
        for (i = 0; i < col_count; i++)
 
538
        {
 
539
                width_total += max_width[i];
 
540
                total_header_width += width_header[i];
 
541
        }
 
542
 
 
543
        /*
 
544
         * At this point: max_width[] contains the max width of each column,
 
545
         * max_nl_lines[] contains the max number of lines in each column,
 
546
         * max_bytes[] contains the maximum storage space for formatting
 
547
         * strings, width_total contains the giant width sum.  Now we allocate
 
548
         * some memory for line pointers.
 
549
         */
 
550
        for (i = 0; i < col_count; i++)
 
551
        {
 
552
                /* Add entry for ptr == NULL array termination */
 
553
                col_lineptrs[i] = pg_local_calloc(max_nl_lines[i] + 1,
 
554
                                                                                        sizeof(**col_lineptrs));
 
555
 
 
556
                format_buf[i] = pg_local_malloc(max_bytes[i] + 1);
 
557
 
 
558
                col_lineptrs[i]->ptr = format_buf[i];
 
559
        }
 
560
 
 
561
        /* Default word wrap to the full width, i.e. no word wrap */
 
562
        for (i = 0; i < col_count; i++)
 
563
                width_wrap[i] = max_width[i];
 
564
 
 
565
        /*
 
566
         * Choose target output width: \pset columns, or $COLUMNS, or ioctl
 
567
         */
 
568
        if (cont->opt->columns > 0)
 
569
                output_columns = cont->opt->columns;
 
570
        else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
 
571
        {
 
572
                if (cont->opt->env_columns > 0)
 
573
                        output_columns = cont->opt->env_columns;
 
574
#ifdef TIOCGWINSZ
 
575
                else
 
576
                {
 
577
                        struct winsize screen_size;
 
578
 
 
579
                        if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) != -1)
 
580
                                output_columns = screen_size.ws_col;
 
581
                }
 
582
#endif
 
583
        }
 
584
 
 
585
        if (cont->opt->format == PRINT_WRAPPED)
 
586
        {
 
587
                /*
 
588
                 * Optional optimized word wrap. Shrink columns with a high max/avg
 
589
                 * ratio.  Slighly bias against wider columns. (Increases chance a
 
590
                 * narrow column will fit in its cell.)  If available columns is
 
591
                 * positive...  and greater than the width of the unshrinkable column
 
592
                 * headers
 
593
                 */
 
594
                if (output_columns > 0 && output_columns >= total_header_width)
 
595
                {
 
596
                        /* While there is still excess width... */
 
597
                        while (width_total > output_columns)
 
598
                        {
 
599
                                double          max_ratio = 0;
 
600
                                int                     worst_col = -1;
 
601
 
 
602
                                /*
 
603
                                 *      Find column that has the highest ratio of its maximum
 
604
                                 *      width compared to its average width.  This tells us which
 
605
                                 *      column will produce the fewest wrapped values if shortened.
 
606
                                 *      width_wrap starts as equal to max_width.
 
607
                                 */
 
608
                                for (i = 0; i < col_count; i++)
 
609
                                {
 
610
                                        if (width_average[i] && width_wrap[i] > width_header[i])
 
611
                                        {
 
612
                                                /* Penalize wide columns by 1% of their width */
 
613
                                                double ratio;
 
614
 
 
615
                                                ratio = (double) width_wrap[i] / width_average[i] +
 
616
                                                        max_width[i] * 0.01;
 
617
                                                if (ratio > max_ratio)
 
618
                                                {
 
619
                                                        max_ratio = ratio;
 
620
                                                        worst_col = i;
 
621
                                                }
 
622
                                        }
 
623
                                }
 
624
 
 
625
                                /* Exit loop if we can't squeeze any more. */
 
626
                                if (worst_col == -1)
 
627
                                        break;
 
628
 
 
629
                                /* Decrease width of target column by one. */
 
630
                                width_wrap[worst_col]--;
 
631
                                width_total--;
 
632
                        }
 
633
                }
 
634
        }
 
635
 
 
636
        /* If we wrapped beyond the display width, use the pager */
 
637
        if (!is_pager && fout == stdout && output_columns > 0 &&
 
638
                (output_columns < total_header_width || output_columns < width_total))
 
639
        {
 
640
                fout = PageOutput(INT_MAX, cont->opt->pager);   /* force pager */
 
641
                is_pager = true;
 
642
        }
 
643
        
 
644
        /* Check if newlines or our wrapping now need the pager */
 
645
        if (!is_pager)
 
646
        {
 
647
                /* scan all cells, find maximum width, compute cell_count */
 
648
                for (i = 0, ptr = cont->cells; *ptr; ptr++, cell_count++)
 
649
                {
 
650
                        int                     width,
 
651
                                                nl_lines,
 
652
                                                bytes_required;
 
653
        
 
654
                        pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
 
655
                                           &width, &nl_lines, &bytes_required);
 
656
                        if (opt_numeric_locale && cont->align[i] == 'r')
 
657
                                width += additional_numeric_locale_len(*ptr);
 
658
        
 
659
                        /*
 
660
                         *      A row can have both wrapping and newlines that cause
 
661
                         *      it to display across multiple lines.  We check
 
662
                         *      for both cases below.
 
663
                         */
 
664
                        if (width > 0 && width_wrap[i])
 
665
                        {
 
666
                                unsigned int extra_lines;
 
667
 
 
668
                                extra_lines = (width-1) / width_wrap[i] + nl_lines;
 
669
                                if (extra_lines > extra_row_output_lines)
 
670
                                        extra_row_output_lines = extra_lines;
 
671
                        }
 
672
 
 
673
                        /* i is the current column number: increment with wrap */
 
674
                        if (++i >= col_count)
 
675
                        {
 
676
                                i = 0;
 
677
                                /* At last column of each row, add tallest column height */
 
678
                                extra_output_lines += extra_row_output_lines;
 
679
                                extra_row_output_lines = 0;
 
680
                        }
 
681
                }
 
682
                IsPagerNeeded(cont, extra_output_lines, &fout, &is_pager);
 
683
        }
 
684
        
 
685
        /* time to output */
 
686
        if (cont->opt->start_table)
 
687
        {
 
688
                /* print title */
 
689
                if (cont->title && !opt_tuples_only)
 
690
                {
 
691
                        int                     width, height;
 
692
 
 
693
                        pg_wcssize((unsigned char *) cont->title, strlen(cont->title),
 
694
                                           encoding, &width, &height, NULL);
 
695
                        if (width >= width_total)
 
696
                                /* Aligned */
 
697
                                fprintf(fout, "%s\n", cont->title);
 
698
                        else
 
699
                                /* Centered */
 
700
                                fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
 
701
                                                cont->title);
 
702
                }
 
703
 
 
704
                /* print headers */
 
705
                if (!opt_tuples_only)
 
706
                {
 
707
                        int                     more_col_wrapping;
 
708
                        int                     curr_nl_line;
 
709
 
 
710
                        if (opt_border == 2)
 
711
                                _print_horizontal_line(col_count, width_wrap, opt_border, fout);
 
712
 
 
713
                        for (i = 0; i < col_count; i++)
 
714
                                pg_wcsformat((unsigned char *) cont->headers[i],
 
715
                                                         strlen(cont->headers[i]), encoding,
 
716
                                                         col_lineptrs[i], max_nl_lines[i]);
 
717
 
 
718
                        more_col_wrapping = col_count;
 
719
                        curr_nl_line = 0;
 
720
                        memset(header_done, false, col_count * sizeof(bool));
 
721
                        while (more_col_wrapping)
 
722
                        {
 
723
                                if (opt_border == 2)
 
724
                                        fprintf(fout, "|%c", curr_nl_line ? '+' : ' ');
 
725
                                else if (opt_border == 1)
 
726
                                        fputc(curr_nl_line ? '+' : ' ', fout);
 
727
 
 
728
                                for (i = 0; i < cont->ncolumns; i++)
 
729
                                {
 
730
                                        unsigned int nbspace;
 
731
 
 
732
                                        struct lineptr *this_line = col_lineptrs[i] + curr_nl_line;
 
733
 
 
734
                                        if (!header_done[i])
 
735
                                        {
 
736
                                                nbspace = width_wrap[i] - this_line->width;
 
737
 
 
738
                                                /* centered */
 
739
                                                fprintf(fout, "%-*s%s%-*s",
 
740
                                                                nbspace / 2, "", this_line->ptr, (nbspace + 1) / 2, "");
 
741
 
 
742
                                                if (!(this_line + 1)->ptr)
 
743
                                                {
 
744
                                                        more_col_wrapping--;
 
745
                                                        header_done[i] = 1;
 
746
                                                }
 
747
                                        }
 
748
                                        else
 
749
                                                fprintf(fout, "%*s", width_wrap[i], "");
 
750
                                        if (i < col_count - 1)
 
751
                                        {
 
752
                                                if (opt_border == 0)
 
753
                                                        fputc(curr_nl_line ? '+' : ' ', fout);
 
754
                                                else
 
755
                                                        fprintf(fout, " |%c", curr_nl_line ? '+' : ' ');
 
756
                                        }
 
757
                                }
 
758
                                curr_nl_line++;
 
759
 
 
760
                                if (opt_border == 2)
 
761
                                        fputs(" |", fout);
 
762
                                else if (opt_border == 1)
 
763
                                        fputc(' ', fout);
 
764
                                fputc('\n', fout);
 
765
                        }
 
766
 
 
767
                        _print_horizontal_line(col_count, width_wrap, opt_border, fout);
 
768
                }
 
769
        }
 
770
 
 
771
        /* print cells, one loop per row */
 
772
        for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
 
773
        {
 
774
                bool            more_lines;
 
775
 
 
776
                if (cancel_pressed)
 
777
                        break;
 
778
 
 
779
                /*
 
780
                 * Format each cell.  Format again, if it's a numeric formatting locale
 
781
                 * (e.g. 123,456 vs. 123456)
 
782
                 */
 
783
                for (j = 0; j < col_count; j++)
 
784
                {
 
785
                        pg_wcsformat((unsigned char *) ptr[j], strlen(ptr[j]), encoding,
 
786
                                                 col_lineptrs[j], max_nl_lines[j]);
 
787
                        curr_nl_line[j] = 0;
 
788
 
 
789
                        if (opt_numeric_locale && cont->aligns[j] == 'r')
 
790
                        {
 
791
                                char       *my_cell;
 
792
 
 
793
                                my_cell = format_numeric_locale((char *) col_lineptrs[j]->ptr);
 
794
                                /* Buffer IS large enough... now */
 
795
                                strcpy((char *) col_lineptrs[j]->ptr, my_cell);
 
796
                                free(my_cell);
 
797
                        }
 
798
                }
 
799
 
 
800
                memset(bytes_output, 0, col_count * sizeof(int));
 
801
 
 
802
                /*
 
803
                 *      Each time through this loop, one display line is output.
 
804
                 *      It can either be a full value or a partial value if embedded
 
805
                 *      newlines exist or if 'format=wrapping' mode is enabled.
 
806
                 */
 
807
                do
 
808
                {
 
809
                        more_lines = false;
 
810
 
 
811
                        /* left border */
 
812
                        if (opt_border == 2)
 
813
                                fputs("| ", fout);
 
814
                        else if (opt_border == 1)
 
815
                                fputc(' ', fout);
 
816
 
 
817
                        /* for each column */
 
818
                        for (j = 0; j < col_count; j++)
 
819
                        {
 
820
                                /* We have a valid array element, so index it */
 
821
                                struct lineptr *this_line = &col_lineptrs[j][curr_nl_line[j]];
 
822
                                int             bytes_to_output;
 
823
                                int             chars_to_output = width_wrap[j];
 
824
                                bool    finalspaces = (opt_border == 2 || j < col_count - 1);
 
825
 
 
826
                                if (!this_line->ptr)
 
827
                                {
 
828
                                        /* Past newline lines so just pad for other columns */
 
829
                                        if (finalspaces)
 
830
                                                fprintf(fout, "%*s", chars_to_output, "");
 
831
                                }
 
832
                                else
 
833
                                {
 
834
                                        /* Get strlen() of the characters up to width_wrap */
 
835
                                        bytes_to_output =
 
836
                                                strlen_max_width(this_line->ptr + bytes_output[j],
 
837
                                                                                 &chars_to_output, encoding);
 
838
 
 
839
                                        /*
 
840
                                         *      If we exceeded width_wrap, it means the display width
 
841
                                         *      of a single character was wider than our target width.
 
842
                                         *      In that case, we have to pretend we are only printing
 
843
                                         *      the target display width and make the best of it.
 
844
                                         */
 
845
                                        if (chars_to_output > width_wrap[j])
 
846
                                                chars_to_output = width_wrap[j];
 
847
 
 
848
                                        if (cont->aligns[j] == 'r')             /* Right aligned cell */
 
849
                                        {
 
850
                                                /* spaces first */
 
851
                                                fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
 
852
                                                fprintf(fout, "%.*s", bytes_to_output,
 
853
                                                                this_line->ptr + bytes_output[j]);
 
854
                                        }
 
855
                                        else    /* Left aligned cell */
 
856
                                        {
 
857
                                                /* spaces second */
 
858
                                                fprintf(fout, "%.*s", bytes_to_output,
 
859
                                                                this_line->ptr + bytes_output[j]);
 
860
                                                if (finalspaces)
 
861
                                                        fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
 
862
                                        }
 
863
 
 
864
                                        bytes_output[j] += bytes_to_output;
 
865
 
 
866
                                        /* Do we have more text to wrap? */
 
867
                                        if (*(this_line->ptr + bytes_output[j]) != '\0')
 
868
                                                more_lines = true;
 
869
                                        else
 
870
                                        {
 
871
                                                /* Advance to next newline line */
 
872
                                                curr_nl_line[j]++;
 
873
                                                if (col_lineptrs[j][curr_nl_line[j]].ptr != NULL)
 
874
                                                        more_lines = true;
 
875
                                                bytes_output[j] = 0;
 
876
                                        }
 
877
                                }
 
878
 
 
879
                                /* print a divider, if not the last column */
 
880
                                if (j < col_count - 1)
 
881
                                {
 
882
                                        if (opt_border == 0)
 
883
                                                fputc(' ', fout);
 
884
                                        /* Next value is beyond past newlines? */
 
885
                                        else if (col_lineptrs[j+1][curr_nl_line[j+1]].ptr == NULL)
 
886
                                                fputs("   ", fout);
 
887
                                        /* In wrapping of value? */
 
888
                                        else if (bytes_output[j+1] != 0)
 
889
                                                fputs(" ; ", fout);
 
890
                                        /* After first newline value */
 
891
                                        else if (curr_nl_line[j+1] != 0)
 
892
                                                fputs(" : ", fout);
 
893
                                        else
 
894
                                        /* Ordinary line */
 
895
                                                fputs(" | ", fout);
 
896
                                }
 
897
                        }
 
898
 
 
899
                        /* end-of-row border */
 
900
                        if (opt_border == 2)
 
901
                                fputs(" |", fout);
 
902
                        fputc('\n', fout);
 
903
 
 
904
                } while (more_lines);
 
905
        }
 
906
 
 
907
        if (cont->opt->stop_table)
 
908
        {
 
909
                if (opt_border == 2 && !cancel_pressed)
 
910
                        _print_horizontal_line(col_count, width_wrap, opt_border, fout);
 
911
 
 
912
                /* print footers */
 
913
                if (cont->footers && !opt_tuples_only && !cancel_pressed)
 
914
                {
 
915
                        printTableFooter *f;
 
916
 
 
917
                        for (f = cont->footers; f; f = f->next)
 
918
                                fprintf(fout, "%s\n", f->data);
 
919
                }
 
920
 
 
921
                fputc('\n', fout);
 
922
        }
 
923
 
 
924
        /* clean up */
 
925
        free(width_header);
 
926
        free(width_average);
 
927
        free(max_width);
 
928
        free(width_wrap);
 
929
        free(max_nl_lines);
 
930
        free(curr_nl_line);
 
931
        free(col_lineptrs);
 
932
        free(max_bytes);
 
933
        free(header_done);
 
934
        free(bytes_output);
 
935
        for (i = 0; i < col_count; i++)
 
936
                free(format_buf[i]);
 
937
        free(format_buf);
 
938
 
 
939
        if (is_pager)
 
940
                ClosePager(fout);
 
941
}
 
942
 
 
943
 
 
944
static void
 
945
print_aligned_vertical(const printTableContent *cont, FILE *fout)
 
946
{
 
947
        bool            opt_tuples_only = cont->opt->tuples_only;
 
948
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
949
        unsigned short opt_border = cont->opt->border;
 
950
        int                     encoding = cont->opt->encoding;
 
951
        unsigned long record = cont->opt->prior_records + 1;
 
952
        const char *const * ptr;
 
953
        unsigned int i,
 
954
                                hwidth = 0,
 
955
                                dwidth = 0,
 
956
                                hheight = 1,
 
957
                                dheight = 1,
 
958
                                hformatsize = 0,
 
959
                                dformatsize = 0;
 
960
        char       *divider;
 
961
        struct lineptr *hlineptr,
 
962
                           *dlineptr;
 
963
 
 
964
        if (cancel_pressed)
 
965
                return;
 
966
 
 
967
        if (opt_border > 2)
 
968
                opt_border = 2;
 
969
 
 
970
        if (cont->cells[0] == NULL && cont->opt->start_table &&
 
971
                cont->opt->stop_table)
 
972
        {
 
973
                fprintf(fout, _("(No rows)\n"));
 
974
                return;
 
975
        }
 
976
 
 
977
        /* Find the maximum dimensions for the headers */
 
978
        for (i = 0; i < cont->ncolumns; i++)
 
979
        {
 
980
                int                     width,
 
981
                                        height,
 
982
                                        fs;
 
983
 
 
984
                pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
 
985
                                   encoding, &width, &height, &fs);
 
986
                if (width > hwidth)
 
987
                        hwidth = width;
 
988
                if (height > hheight)
 
989
                        hheight = height;
 
990
                if (fs > hformatsize)
 
991
                        hformatsize = fs;
 
992
        }
 
993
 
 
994
        /* find longest data cell */
 
995
        for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
 
996
        {
 
997
                int                     numeric_locale_len;
 
998
                int                     width,
 
999
                                        height,
 
1000
                                        fs;
 
1001
 
 
1002
                if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
1003
                        numeric_locale_len = additional_numeric_locale_len(*ptr);
 
1004
                else
 
1005
                        numeric_locale_len = 0;
 
1006
 
 
1007
                pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
 
1008
                                   &width, &height, &fs);
 
1009
                width += numeric_locale_len;
 
1010
                if (width > dwidth)
 
1011
                        dwidth = width;
 
1012
                if (height > dheight)
 
1013
                        dheight = height;
 
1014
                if (fs > dformatsize)
 
1015
                        dformatsize = fs;
 
1016
        }
 
1017
 
 
1018
        /*
 
1019
         * We now have all the information we need to setup the formatting
 
1020
         * structures
 
1021
         */
 
1022
        dlineptr = pg_local_malloc((sizeof(*dlineptr) + 1) * dheight);
 
1023
        hlineptr = pg_local_malloc((sizeof(*hlineptr) + 1) * hheight);
 
1024
 
 
1025
        dlineptr->ptr = pg_local_malloc(dformatsize);
 
1026
        hlineptr->ptr = pg_local_malloc(hformatsize);
 
1027
 
 
1028
        /* make horizontal border */
 
1029
        divider = pg_local_malloc(hwidth + dwidth + 10);
 
1030
        divider[0] = '\0';
 
1031
        if (opt_border == 2)
 
1032
                strcat(divider, "+-");
 
1033
        for (i = 0; i < hwidth; i++)
 
1034
                strcat(divider, opt_border > 0 ? "-" : " ");
 
1035
        if (opt_border > 0)
 
1036
                strcat(divider, "-+-");
 
1037
        else
 
1038
                strcat(divider, " ");
 
1039
        for (i = 0; i < dwidth; i++)
 
1040
                strcat(divider, opt_border > 0 ? "-" : " ");
 
1041
        if (opt_border == 2)
 
1042
                strcat(divider, "-+");
 
1043
 
 
1044
        if (cont->opt->start_table)
 
1045
        {
 
1046
                /* print title */
 
1047
                if (!opt_tuples_only && cont->title)
 
1048
                        fprintf(fout, "%s\n", cont->title);
 
1049
        }
 
1050
 
 
1051
        /* print records */
 
1052
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1053
        {
 
1054
                int                     line_count,
 
1055
                                        dcomplete,
 
1056
                                        hcomplete;
 
1057
 
 
1058
                if (i % cont->ncolumns == 0)
 
1059
                {
 
1060
                        if (cancel_pressed)
 
1061
                                break;
 
1062
                        if (!opt_tuples_only)
 
1063
                        {
 
1064
                                char            record_str[64];
 
1065
                                size_t          record_str_len;
 
1066
 
 
1067
                                if (opt_border == 0)
 
1068
                                        snprintf(record_str, 64, "* Record %lu", record++);
 
1069
                                else
 
1070
                                        snprintf(record_str, 64, "[ RECORD %lu ]", record++);
 
1071
                                record_str_len = strlen(record_str);
 
1072
 
 
1073
                                if (record_str_len + opt_border > strlen(divider))
 
1074
                                        fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
 
1075
                                else
 
1076
                                {
 
1077
                                        char       *div_copy = pg_strdup(divider);
 
1078
 
 
1079
                                        strncpy(div_copy + opt_border, record_str, record_str_len);
 
1080
                                        fprintf(fout, "%s\n", div_copy);
 
1081
                                        free(div_copy);
 
1082
                                }
 
1083
                        }
 
1084
                        else if (i != 0 || !cont->opt->start_table || opt_border == 2)
 
1085
                                fprintf(fout, "%s\n", divider);
 
1086
                }
 
1087
 
 
1088
                /* Format the header */
 
1089
                pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns],
 
1090
                                         strlen(cont->headers[i % cont->ncolumns]),
 
1091
                                         encoding, hlineptr, hheight);
 
1092
                /* Format the data */
 
1093
                pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding,
 
1094
                                         dlineptr, dheight);
 
1095
 
 
1096
                line_count = 0;
 
1097
                dcomplete = hcomplete = 0;
 
1098
                while (!dcomplete || !hcomplete)
 
1099
                {
 
1100
                        if (opt_border == 2)
 
1101
                                fputs("| ", fout);
 
1102
                        if (!hcomplete)
 
1103
                        {
 
1104
                                fprintf(fout, "%-s%*s", hlineptr[line_count].ptr,
 
1105
                                                hwidth - hlineptr[line_count].width, "");
 
1106
 
 
1107
                                if (!hlineptr[line_count + 1].ptr)
 
1108
                                        hcomplete = 1;
 
1109
                        }
 
1110
                        else
 
1111
                                fprintf(fout, "%*s", hwidth, "");
 
1112
 
 
1113
                        if (opt_border > 0)
 
1114
                                fprintf(fout, " %c ", (line_count == 0) ? '|' : ':');
 
1115
                        else
 
1116
                                fputs(" ", fout);
 
1117
 
 
1118
                        if (!dcomplete)
 
1119
                        {
 
1120
                                if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
1121
                                {
 
1122
                                        char       *my_cell = format_numeric_locale((char *) dlineptr[line_count].ptr);
 
1123
 
 
1124
                                        if (opt_border < 2)
 
1125
                                                fprintf(fout, "%s\n", my_cell);
 
1126
                                        else
 
1127
                                                fprintf(fout, "%-s%*s |\n", my_cell,
 
1128
                                                                (int) (dwidth - strlen(my_cell)), "");
 
1129
                                        free(my_cell);
 
1130
                                }
 
1131
                                else
 
1132
                                {
 
1133
                                        if (opt_border < 2)
 
1134
                                                fprintf(fout, "%s\n", dlineptr[line_count].ptr);
 
1135
                                        else
 
1136
                                                fprintf(fout, "%-s%*s |\n", dlineptr[line_count].ptr,
 
1137
                                                                dwidth - dlineptr[line_count].width, "");
 
1138
                                }
 
1139
 
 
1140
                                if (!dlineptr[line_count + 1].ptr)
 
1141
                                        dcomplete = 1;
 
1142
                        }
 
1143
                        else
 
1144
                        {
 
1145
                                if (opt_border < 2)
 
1146
                                        fputc('\n', fout);
 
1147
                                else
 
1148
                                        fprintf(fout, "%*s |\n", dwidth, "");
 
1149
                        }
 
1150
                        line_count++;
 
1151
                }
 
1152
        }
 
1153
 
 
1154
        if (cont->opt->stop_table)
 
1155
        {
 
1156
                if (opt_border == 2 && !cancel_pressed)
 
1157
                        fprintf(fout, "%s\n", divider);
 
1158
 
 
1159
                /* print footers */
 
1160
                if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
 
1161
                {
 
1162
                        printTableFooter *f;
 
1163
 
 
1164
                        if (opt_border < 2)
 
1165
                                fputc('\n', fout);
 
1166
                        for (f = cont->footers; f; f = f->next)
 
1167
                                fprintf(fout, "%s\n", f->data);
 
1168
                }
 
1169
 
 
1170
                fputc('\n', fout);
 
1171
        }
 
1172
 
 
1173
        free(divider);
 
1174
        free(hlineptr->ptr);
 
1175
        free(dlineptr->ptr);
 
1176
        free(hlineptr);
 
1177
        free(dlineptr);
 
1178
}
 
1179
 
 
1180
 
 
1181
/**********************/
 
1182
/* HTML printing ******/
 
1183
/**********************/
 
1184
 
 
1185
 
 
1186
void
 
1187
html_escaped_print(const char *in, FILE *fout)
 
1188
{
 
1189
        const char *p;
 
1190
        bool            leading_space = true;
 
1191
 
 
1192
        for (p = in; *p; p++)
 
1193
        {
 
1194
                switch (*p)
 
1195
                {
 
1196
                        case '&':
 
1197
                                fputs("&amp;", fout);
 
1198
                                break;
 
1199
                        case '<':
 
1200
                                fputs("&lt;", fout);
 
1201
                                break;
 
1202
                        case '>':
 
1203
                                fputs("&gt;", fout);
 
1204
                                break;
 
1205
                        case '\n':
 
1206
                                fputs("<br />\n", fout);
 
1207
                                break;
 
1208
                        case '"':
 
1209
                                fputs("&quot;", fout);
 
1210
                                break;
 
1211
                        case ' ':
 
1212
                                /* protect leading space, for EXPLAIN output */
 
1213
                                if (leading_space)
 
1214
                                        fputs("&nbsp;", fout);
 
1215
                                else
 
1216
                                        fputs(" ", fout);
 
1217
                                break;
 
1218
                        default:
 
1219
                                fputc(*p, fout);
 
1220
                }
 
1221
                if (*p != ' ')
 
1222
                        leading_space = false;
 
1223
        }
 
1224
}
 
1225
 
 
1226
 
 
1227
static void
 
1228
print_html_text(const printTableContent *cont, FILE *fout)
 
1229
{
 
1230
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1231
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1232
        unsigned short opt_border = cont->opt->border;
 
1233
        const char *opt_table_attr = cont->opt->tableAttr;
 
1234
        unsigned int i;
 
1235
        const char *const * ptr;
 
1236
 
 
1237
        if (cancel_pressed)
 
1238
                return;
 
1239
 
 
1240
        if (cont->opt->start_table)
 
1241
        {
 
1242
                fprintf(fout, "<table border=\"%d\"", opt_border);
 
1243
                if (opt_table_attr)
 
1244
                        fprintf(fout, " %s", opt_table_attr);
 
1245
                fputs(">\n", fout);
 
1246
 
 
1247
                /* print title */
 
1248
                if (!opt_tuples_only && cont->title)
 
1249
                {
 
1250
                        fputs("  <caption>", fout);
 
1251
                        html_escaped_print(cont->title, fout);
 
1252
                        fputs("</caption>\n", fout);
 
1253
                }
 
1254
 
 
1255
                /* print headers */
 
1256
                if (!opt_tuples_only)
 
1257
                {
 
1258
                        fputs("  <tr>\n", fout);
 
1259
                        for (ptr = cont->headers; *ptr; ptr++)
 
1260
                        {
 
1261
                                fputs("    <th align=\"center\">", fout);
 
1262
                                html_escaped_print(*ptr, fout);
 
1263
                                fputs("</th>\n", fout);
 
1264
                        }
 
1265
                        fputs("  </tr>\n", fout);
 
1266
                }
 
1267
        }
 
1268
 
 
1269
        /* print cells */
 
1270
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1271
        {
 
1272
                if (i % cont->ncolumns == 0)
 
1273
                {
 
1274
                        if (cancel_pressed)
 
1275
                                break;
 
1276
                        fputs("  <tr valign=\"top\">\n", fout);
 
1277
                }
 
1278
 
 
1279
                fprintf(fout, "    <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
 
1280
                /* is string only whitespace? */
 
1281
                if ((*ptr)[strspn(*ptr, " \t")] == '\0')
 
1282
                        fputs("&nbsp; ", fout);
 
1283
                else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
1284
                {
 
1285
                        char       *my_cell = format_numeric_locale(*ptr);
 
1286
 
 
1287
                        html_escaped_print(my_cell, fout);
 
1288
                        free(my_cell);
 
1289
                }
 
1290
                else
 
1291
                        html_escaped_print(*ptr, fout);
 
1292
 
 
1293
                fputs("</td>\n", fout);
 
1294
 
 
1295
                if ((i + 1) % cont->ncolumns == 0)
 
1296
                        fputs("  </tr>\n", fout);
 
1297
        }
 
1298
 
 
1299
        if (cont->opt->stop_table)
 
1300
        {
 
1301
                fputs("</table>\n", fout);
 
1302
 
 
1303
                /* print footers */
 
1304
                if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
 
1305
                {
 
1306
                        printTableFooter *f;
 
1307
 
 
1308
                        fputs("<p>", fout);
 
1309
                        for (f = cont->footers; f; f = f->next)
 
1310
                        {
 
1311
                                html_escaped_print(f->data, fout);
 
1312
                                fputs("<br />\n", fout);
 
1313
                        }
 
1314
                        fputs("</p>", fout);
 
1315
                }
 
1316
 
 
1317
                fputc('\n', fout);
 
1318
        }
 
1319
}
 
1320
 
 
1321
 
 
1322
static void
 
1323
print_html_vertical(const printTableContent *cont, FILE *fout)
 
1324
{
 
1325
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1326
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1327
        unsigned short opt_border = cont->opt->border;
 
1328
        const char *opt_table_attr = cont->opt->tableAttr;
 
1329
        unsigned long record = cont->opt->prior_records + 1;
 
1330
        unsigned int i;
 
1331
        const char *const * ptr;
 
1332
 
 
1333
        if (cancel_pressed)
 
1334
                return;
 
1335
 
 
1336
        if (cont->opt->start_table)
 
1337
        {
 
1338
                fprintf(fout, "<table border=\"%d\"", opt_border);
 
1339
                if (opt_table_attr)
 
1340
                        fprintf(fout, " %s", opt_table_attr);
 
1341
                fputs(">\n", fout);
 
1342
 
 
1343
                /* print title */
 
1344
                if (!opt_tuples_only && cont->title)
 
1345
                {
 
1346
                        fputs("  <caption>", fout);
 
1347
                        html_escaped_print(cont->title, fout);
 
1348
                        fputs("</caption>\n", fout);
 
1349
                }
 
1350
        }
 
1351
 
 
1352
        /* print records */
 
1353
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1354
        {
 
1355
                if (i % cont->ncolumns == 0)
 
1356
                {
 
1357
                        if (cancel_pressed)
 
1358
                                break;
 
1359
                        if (!opt_tuples_only)
 
1360
                                fprintf(fout,
 
1361
                                                "\n  <tr><td colspan=\"2\" align=\"center\">Record %lu</td></tr>\n",
 
1362
                                                record++);
 
1363
                        else
 
1364
                                fputs("\n  <tr><td colspan=\"2\">&nbsp;</td></tr>\n", fout);
 
1365
                }
 
1366
                fputs("  <tr valign=\"top\">\n"
 
1367
                          "    <th>", fout);
 
1368
                html_escaped_print(cont->headers[i % cont->ncolumns], fout);
 
1369
                fputs("</th>\n", fout);
 
1370
 
 
1371
                fprintf(fout, "    <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
 
1372
                /* is string only whitespace? */
 
1373
                if ((*ptr)[strspn(*ptr, " \t")] == '\0')
 
1374
                        fputs("&nbsp; ", fout);
 
1375
                else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
 
1376
                {
 
1377
                        char       *my_cell = format_numeric_locale(*ptr);
 
1378
 
 
1379
                        html_escaped_print(my_cell, fout);
 
1380
                        free(my_cell);
 
1381
                }
 
1382
                else
 
1383
                        html_escaped_print(*ptr, fout);
 
1384
 
 
1385
                fputs("</td>\n  </tr>\n", fout);
 
1386
        }
 
1387
 
 
1388
        if (cont->opt->stop_table)
 
1389
        {
 
1390
                fputs("</table>\n", fout);
 
1391
 
 
1392
                /* print footers */
 
1393
                if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
 
1394
                {
 
1395
                        printTableFooter *f;
 
1396
 
 
1397
                        fputs("<p>", fout);
 
1398
                        for (f = cont->footers; f; f = f->next)
 
1399
                        {
 
1400
                                html_escaped_print(f->data, fout);
 
1401
                                fputs("<br />\n", fout);
 
1402
                        }
 
1403
                        fputs("</p>", fout);
 
1404
                }
 
1405
 
 
1406
                fputc('\n', fout);
 
1407
        }
 
1408
}
 
1409
 
 
1410
 
 
1411
/*************************/
 
1412
/* LaTeX                                 */
 
1413
/*************************/
 
1414
 
 
1415
 
 
1416
static void
 
1417
latex_escaped_print(const char *in, FILE *fout)
 
1418
{
 
1419
        const char *p;
 
1420
 
 
1421
        for (p = in; *p; p++)
 
1422
                switch (*p)
 
1423
                {
 
1424
                        case '&':
 
1425
                                fputs("\\&", fout);
 
1426
                                break;
 
1427
                        case '%':
 
1428
                                fputs("\\%", fout);
 
1429
                                break;
 
1430
                        case '$':
 
1431
                                fputs("\\$", fout);
 
1432
                                break;
 
1433
                        case '_':
 
1434
                                fputs("\\_", fout);
 
1435
                                break;
 
1436
                        case '{':
 
1437
                                fputs("\\{", fout);
 
1438
                                break;
 
1439
                        case '}':
 
1440
                                fputs("\\}", fout);
 
1441
                                break;
 
1442
                        case '\\':
 
1443
                                fputs("\\backslash", fout);
 
1444
                                break;
 
1445
                        case '\n':
 
1446
                                fputs("\\\\", fout);
 
1447
                                break;
 
1448
                        default:
 
1449
                                fputc(*p, fout);
 
1450
                }
 
1451
}
 
1452
 
 
1453
 
 
1454
static void
 
1455
print_latex_text(const printTableContent *cont, FILE *fout)
 
1456
{
 
1457
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1458
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1459
        unsigned short opt_border = cont->opt->border;
 
1460
        unsigned int i;
 
1461
        const char *const * ptr;
 
1462
 
 
1463
        if (cancel_pressed)
 
1464
                return;
 
1465
 
 
1466
        if (opt_border > 2)
 
1467
                opt_border = 2;
 
1468
 
 
1469
        if (cont->opt->start_table)
 
1470
        {
 
1471
                /* print title */
 
1472
                if (!opt_tuples_only && cont->title)
 
1473
                {
 
1474
                        fputs("\\begin{center}\n", fout);
 
1475
                        latex_escaped_print(cont->title, fout);
 
1476
                        fputs("\n\\end{center}\n\n", fout);
 
1477
                }
 
1478
 
 
1479
                /* begin environment and set alignments and borders */
 
1480
                fputs("\\begin{tabular}{", fout);
 
1481
 
 
1482
                if (opt_border == 2)
 
1483
                        fputs("| ", fout);
 
1484
                for (i = 0; i < cont->ncolumns; i++)
 
1485
                {
 
1486
                        fputc(*(cont->aligns + i), fout);
 
1487
                        if (opt_border != 0 && i < cont->ncolumns - 1)
 
1488
                                fputs(" | ", fout);
 
1489
                }
 
1490
                if (opt_border == 2)
 
1491
                        fputs(" |", fout);
 
1492
 
 
1493
                fputs("}\n", fout);
 
1494
 
 
1495
                if (!opt_tuples_only && opt_border == 2)
 
1496
                        fputs("\\hline\n", fout);
 
1497
 
 
1498
                /* print headers */
 
1499
                if (!opt_tuples_only)
 
1500
                {
 
1501
                        for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
 
1502
                        {
 
1503
                                if (i != 0)
 
1504
                                        fputs(" & ", fout);
 
1505
                                fputs("\\textit{", fout);
 
1506
                                latex_escaped_print(*ptr, fout);
 
1507
                                fputc('}', fout);
 
1508
                        }
 
1509
                        fputs(" \\\\\n", fout);
 
1510
                        fputs("\\hline\n", fout);
 
1511
                }
 
1512
        }
 
1513
 
 
1514
        /* print cells */
 
1515
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1516
        {
 
1517
                if (opt_numeric_locale)
 
1518
                {
 
1519
                        char       *my_cell = format_numeric_locale(*ptr);
 
1520
 
 
1521
                        latex_escaped_print(my_cell, fout);
 
1522
                        free(my_cell);
 
1523
                }
 
1524
                else
 
1525
                        latex_escaped_print(*ptr, fout);
 
1526
 
 
1527
                if ((i + 1) % cont->ncolumns == 0)
 
1528
                {
 
1529
                        fputs(" \\\\\n", fout);
 
1530
                        if (cancel_pressed)
 
1531
                                break;
 
1532
                }
 
1533
                else
 
1534
                        fputs(" & ", fout);
 
1535
        }
 
1536
 
 
1537
        if (cont->opt->stop_table)
 
1538
        {
 
1539
                if (opt_border == 2)
 
1540
                        fputs("\\hline\n", fout);
 
1541
 
 
1542
                fputs("\\end{tabular}\n\n\\noindent ", fout);
 
1543
 
 
1544
                /* print footers */
 
1545
                if (cont->footers && !opt_tuples_only && !cancel_pressed)
 
1546
                {
 
1547
                        printTableFooter *f;
 
1548
 
 
1549
                        for (f = cont->footers; f; f = f->next)
 
1550
                        {
 
1551
                                latex_escaped_print(f->data, fout);
 
1552
                                fputs(" \\\\\n", fout);
 
1553
                        }
 
1554
                }
 
1555
 
 
1556
                fputc('\n', fout);
 
1557
        }
 
1558
}
 
1559
 
 
1560
 
 
1561
static void
 
1562
print_latex_vertical(const printTableContent *cont, FILE *fout)
 
1563
{
 
1564
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1565
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1566
        unsigned short opt_border = cont->opt->border;
 
1567
        unsigned long record = cont->opt->prior_records + 1;
 
1568
        unsigned int i;
 
1569
        const char *const * ptr;
 
1570
 
 
1571
        if (cancel_pressed)
 
1572
                return;
 
1573
 
 
1574
        if (opt_border > 2)
 
1575
                opt_border = 2;
 
1576
 
 
1577
        if (cont->opt->start_table)
 
1578
        {
 
1579
                /* print title */
 
1580
                if (!opt_tuples_only && cont->title)
 
1581
                {
 
1582
                        fputs("\\begin{center}\n", fout);
 
1583
                        latex_escaped_print(cont->title, fout);
 
1584
                        fputs("\n\\end{center}\n\n", fout);
 
1585
                }
 
1586
 
 
1587
                /* begin environment and set alignments and borders */
 
1588
                fputs("\\begin{tabular}{", fout);
 
1589
                if (opt_border == 0)
 
1590
                        fputs("cl", fout);
 
1591
                else if (opt_border == 1)
 
1592
                        fputs("c|l", fout);
 
1593
                else if (opt_border == 2)
 
1594
                        fputs("|c|l|", fout);
 
1595
                fputs("}\n", fout);
 
1596
        }
 
1597
 
 
1598
        /* print records */
 
1599
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1600
        {
 
1601
                /* new record */
 
1602
                if (i % cont->ncolumns == 0)
 
1603
                {
 
1604
                        if (cancel_pressed)
 
1605
                                break;
 
1606
                        if (!opt_tuples_only)
 
1607
                        {
 
1608
                                if (opt_border == 2)
 
1609
                                {
 
1610
                                        fputs("\\hline\n", fout);
 
1611
                                        fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %lu}} \\\\\n", record++);
 
1612
                                }
 
1613
                                else
 
1614
                                        fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %lu}} \\\\\n", record++);
 
1615
                        }
 
1616
                        if (opt_border >= 1)
 
1617
                                fputs("\\hline\n", fout);
 
1618
                }
 
1619
 
 
1620
                latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
 
1621
                fputs(" & ", fout);
 
1622
                latex_escaped_print(*ptr, fout);
 
1623
                fputs(" \\\\\n", fout);
 
1624
        }
 
1625
 
 
1626
        if (cont->opt->stop_table)
 
1627
        {
 
1628
                if (opt_border == 2)
 
1629
                        fputs("\\hline\n", fout);
 
1630
 
 
1631
                fputs("\\end{tabular}\n\n\\noindent ", fout);
 
1632
 
 
1633
                /* print footers */
 
1634
                if (cont->footers && !opt_tuples_only && !cancel_pressed)
 
1635
                {
 
1636
                        printTableFooter *f;
 
1637
 
 
1638
                        for (f = cont->footers; f; f = f->next)
 
1639
                        {
 
1640
                                if (opt_numeric_locale)
 
1641
                                {
 
1642
                                        char       *my_cell = format_numeric_locale(f->data);
 
1643
 
 
1644
                                        latex_escaped_print(my_cell, fout);
 
1645
                                        free(my_cell);
 
1646
                                }
 
1647
                                else
 
1648
                                        latex_escaped_print(f->data, fout);
 
1649
                                fputs(" \\\\\n", fout);
 
1650
                        }
 
1651
                }
 
1652
 
 
1653
                fputc('\n', fout);
 
1654
        }
 
1655
}
 
1656
 
 
1657
 
 
1658
/*************************/
 
1659
/* Troff -ms             */
 
1660
/*************************/
 
1661
 
 
1662
 
 
1663
static void
 
1664
troff_ms_escaped_print(const char *in, FILE *fout)
 
1665
{
 
1666
        const char *p;
 
1667
 
 
1668
        for (p = in; *p; p++)
 
1669
                switch (*p)
 
1670
                {
 
1671
                        case '\\':
 
1672
                                fputs("\\(rs", fout);
 
1673
                                break;
 
1674
                        default:
 
1675
                                fputc(*p, fout);
 
1676
                }
 
1677
}
 
1678
 
 
1679
 
 
1680
static void
 
1681
print_troff_ms_text(const printTableContent *cont, FILE *fout)
 
1682
{
 
1683
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1684
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1685
        unsigned short opt_border = cont->opt->border;
 
1686
        unsigned int i;
 
1687
        const char *const * ptr;
 
1688
 
 
1689
        if (cancel_pressed)
 
1690
                return;
 
1691
 
 
1692
        if (opt_border > 2)
 
1693
                opt_border = 2;
 
1694
 
 
1695
        if (cont->opt->start_table)
 
1696
        {
 
1697
                /* print title */
 
1698
                if (!opt_tuples_only && cont->title)
 
1699
                {
 
1700
                        fputs(".LP\n.DS C\n", fout);
 
1701
                        troff_ms_escaped_print(cont->title, fout);
 
1702
                        fputs("\n.DE\n", fout);
 
1703
                }
 
1704
 
 
1705
                /* begin environment and set alignments and borders */
 
1706
                fputs(".LP\n.TS\n", fout);
 
1707
                if (opt_border == 2)
 
1708
                        fputs("center box;\n", fout);
 
1709
                else
 
1710
                        fputs("center;\n", fout);
 
1711
 
 
1712
                for (i = 0; i < cont->ncolumns; i++)
 
1713
                {
 
1714
                        fputc(*(cont->aligns + i), fout);
 
1715
                        if (opt_border > 0 && i < cont->ncolumns - 1)
 
1716
                                fputs(" | ", fout);
 
1717
                }
 
1718
                fputs(".\n", fout);
 
1719
 
 
1720
                /* print headers */
 
1721
                if (!opt_tuples_only)
 
1722
                {
 
1723
                        for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
 
1724
                        {
 
1725
                                if (i != 0)
 
1726
                                        fputc('\t', fout);
 
1727
                                fputs("\\fI", fout);
 
1728
                                troff_ms_escaped_print(*ptr, fout);
 
1729
                                fputs("\\fP", fout);
 
1730
                        }
 
1731
                        fputs("\n_\n", fout);
 
1732
                }
 
1733
        }
 
1734
 
 
1735
        /* print cells */
 
1736
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1737
        {
 
1738
                if (opt_numeric_locale)
 
1739
                {
 
1740
                        char       *my_cell = format_numeric_locale(*ptr);
 
1741
 
 
1742
                        troff_ms_escaped_print(my_cell, fout);
 
1743
                        free(my_cell);
 
1744
                }
 
1745
                else
 
1746
                        troff_ms_escaped_print(*ptr, fout);
 
1747
 
 
1748
                if ((i + 1) % cont->ncolumns == 0)
 
1749
                {
 
1750
                        fputc('\n', fout);
 
1751
                        if (cancel_pressed)
 
1752
                                break;
 
1753
                }
 
1754
                else
 
1755
                        fputc('\t', fout);
 
1756
        }
 
1757
 
 
1758
        if (cont->opt->stop_table)
 
1759
        {
 
1760
                fputs(".TE\n.DS L\n", fout);
 
1761
 
 
1762
                /* print footers */
 
1763
                if (cont->footers && !opt_tuples_only && !cancel_pressed)
 
1764
                {
 
1765
                        printTableFooter *f;
 
1766
 
 
1767
                        for (f = cont->footers; f; f = f->next)
 
1768
                        {
 
1769
                                troff_ms_escaped_print(f->data, fout);
 
1770
                                fputc('\n', fout);
 
1771
                        }
 
1772
                }
 
1773
 
 
1774
                fputs(".DE\n", fout);
 
1775
        }
 
1776
}
 
1777
 
 
1778
 
 
1779
static void
 
1780
print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
 
1781
{
 
1782
        bool            opt_tuples_only = cont->opt->tuples_only;
 
1783
        bool            opt_numeric_locale = cont->opt->numericLocale;
 
1784
        unsigned short opt_border = cont->opt->border;
 
1785
        unsigned long record = cont->opt->prior_records + 1;
 
1786
        unsigned int i;
 
1787
        const char *const * ptr;
 
1788
        unsigned short current_format = 0;      /* 0=none, 1=header, 2=body */
 
1789
 
 
1790
        if (cancel_pressed)
 
1791
                return;
 
1792
 
 
1793
        if (opt_border > 2)
 
1794
                opt_border = 2;
 
1795
 
 
1796
        if (cont->opt->start_table)
 
1797
        {
 
1798
                /* print title */
 
1799
                if (!opt_tuples_only && cont->title)
 
1800
                {
 
1801
                        fputs(".LP\n.DS C\n", fout);
 
1802
                        troff_ms_escaped_print(cont->title, fout);
 
1803
                        fputs("\n.DE\n", fout);
 
1804
                }
 
1805
 
 
1806
                /* begin environment and set alignments and borders */
 
1807
                fputs(".LP\n.TS\n", fout);
 
1808
                if (opt_border == 2)
 
1809
                        fputs("center box;\n", fout);
 
1810
                else
 
1811
                        fputs("center;\n", fout);
 
1812
 
 
1813
                /* basic format */
 
1814
                if (opt_tuples_only)
 
1815
                        fputs("c l;\n", fout);
 
1816
        }
 
1817
        else
 
1818
                current_format = 2;             /* assume tuples printed already */
 
1819
 
 
1820
        /* print records */
 
1821
        for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
 
1822
        {
 
1823
                /* new record */
 
1824
                if (i % cont->ncolumns == 0)
 
1825
                {
 
1826
                        if (cancel_pressed)
 
1827
                                break;
 
1828
                        if (!opt_tuples_only)
 
1829
                        {
 
1830
                                if (current_format != 1)
 
1831
                                {
 
1832
                                        if (opt_border == 2 && record > 1)
 
1833
                                                fputs("_\n", fout);
 
1834
                                        if (current_format != 0)
 
1835
                                                fputs(".T&\n", fout);
 
1836
                                        fputs("c s.\n", fout);
 
1837
                                        current_format = 1;
 
1838
                                }
 
1839
                                fprintf(fout, "\\fIRecord %lu\\fP\n", record++);
 
1840
                        }
 
1841
                        if (opt_border >= 1)
 
1842
                                fputs("_\n", fout);
 
1843
                }
 
1844
 
 
1845
                if (!opt_tuples_only)
 
1846
                {
 
1847
                        if (current_format != 2)
 
1848
                        {
 
1849
                                if (current_format != 0)
 
1850
                                        fputs(".T&\n", fout);
 
1851
                                if (opt_border != 1)
 
1852
                                        fputs("c l.\n", fout);
 
1853
                                else
 
1854
                                        fputs("c | l.\n", fout);
 
1855
                                current_format = 2;
 
1856
                        }
 
1857
                }
 
1858
 
 
1859
                troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
 
1860
                fputc('\t', fout);
 
1861
                if (opt_numeric_locale)
 
1862
                {
 
1863
                        char       *my_cell = format_numeric_locale(*ptr);
 
1864
 
 
1865
                        troff_ms_escaped_print(my_cell, fout);
 
1866
                        free(my_cell);
 
1867
                }
 
1868
                else
 
1869
                        troff_ms_escaped_print(*ptr, fout);
 
1870
 
 
1871
                fputc('\n', fout);
 
1872
        }
 
1873
 
 
1874
        if (cont->opt->stop_table)
 
1875
        {
 
1876
                fputs(".TE\n.DS L\n", fout);
 
1877
 
 
1878
                /* print footers */
 
1879
                if (cont->footers && !opt_tuples_only && !cancel_pressed)
 
1880
                {
 
1881
                        printTableFooter *f;
 
1882
 
 
1883
                        for (f = cont->footers; f; f = f->next)
 
1884
                        {
 
1885
                                troff_ms_escaped_print(f->data, fout);
 
1886
                                fputc('\n', fout);
 
1887
                        }
 
1888
                }
 
1889
 
 
1890
                fputs(".DE\n", fout);
 
1891
        }
 
1892
}
 
1893
 
 
1894
 
 
1895
/********************************/
 
1896
/* Public functions             */
 
1897
/********************************/
 
1898
 
 
1899
 
 
1900
/*
 
1901
 * PageOutput
 
1902
 *
 
1903
 * Tests if pager is needed and returns appropriate FILE pointer.
 
1904
 */
 
1905
FILE *
 
1906
PageOutput(int lines, unsigned short int pager)
 
1907
{
 
1908
        /* check whether we need / can / are supposed to use pager */
 
1909
        if (pager && isatty(fileno(stdin)) && isatty(fileno(stdout)))
 
1910
        {
 
1911
                const char *pagerprog;
 
1912
                FILE       *pagerpipe;
 
1913
 
 
1914
#ifdef TIOCGWINSZ
 
1915
                int                     result;
 
1916
                struct winsize screen_size;
 
1917
 
 
1918
                result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
 
1919
 
 
1920
                /* >= accounts for a one-line prompt */
 
1921
                if (result == -1 || lines >= screen_size.ws_row || pager > 1)
 
1922
                {
 
1923
#endif
 
1924
                        pagerprog = getenv("PAGER");
 
1925
                        if (!pagerprog)
 
1926
                                pagerprog = DEFAULT_PAGER;
 
1927
#ifndef WIN32
 
1928
                        pqsignal(SIGPIPE, SIG_IGN);
 
1929
#endif
 
1930
                        pagerpipe = popen(pagerprog, "w");
 
1931
                        if (pagerpipe)
 
1932
                                return pagerpipe;
 
1933
#ifdef TIOCGWINSZ
 
1934
                }
 
1935
#endif
 
1936
        }
 
1937
 
 
1938
        return stdout;
 
1939
}
 
1940
 
 
1941
/*
 
1942
 * ClosePager
 
1943
 *
 
1944
 * Close previously opened pager pipe, if any
 
1945
 */
 
1946
void
 
1947
ClosePager(FILE *pagerpipe)
 
1948
{
 
1949
        if (pagerpipe && pagerpipe != stdout)
 
1950
        {
 
1951
                /*
 
1952
                 * If printing was canceled midstream, warn about it.
 
1953
                 *
 
1954
                 * Some pagers like less use Ctrl-C as part of their command set. Even
 
1955
                 * so, we abort our processing and warn the user what we did.  If the
 
1956
                 * pager quit as a result of the SIGINT, this message won't go
 
1957
                 * anywhere ...
 
1958
                 */
 
1959
                if (cancel_pressed)
 
1960
                        fprintf(pagerpipe, _("Interrupted\n"));
 
1961
 
 
1962
                pclose(pagerpipe);
 
1963
#ifndef WIN32
 
1964
                pqsignal(SIGPIPE, SIG_DFL);
 
1965
#endif
 
1966
        }
 
1967
}
 
1968
 
 
1969
/*
 
1970
 * Initialise a table contents struct.
 
1971
 *              Must be called before any other printTable method is used.
 
1972
 *
 
1973
 * The title is not duplicated; the caller must ensure that the buffer
 
1974
 * is available for the lifetime of the printTableContent struct.
 
1975
 *
 
1976
 * If you call this, you must call printTableCleanup once you're done with the
 
1977
 * table.
 
1978
 */
 
1979
void
 
1980
printTableInit(printTableContent *const content, const printTableOpt *opt,
 
1981
                           const char *title, const int ncolumns, const int nrows)
 
1982
{
 
1983
        content->opt = opt;
 
1984
        content->title = title;
 
1985
        content->ncolumns = ncolumns;
 
1986
        content->nrows = nrows;
 
1987
 
 
1988
        content->headers = pg_local_calloc(ncolumns + 1,
 
1989
                                                                           sizeof(*content->headers));
 
1990
 
 
1991
        content->cells = pg_local_calloc(ncolumns * nrows + 1,
 
1992
                                                                         sizeof(*content->cells));
 
1993
 
 
1994
        content->footers = NULL;
 
1995
 
 
1996
        content->aligns = pg_local_calloc(ncolumns + 1,
 
1997
                                                                          sizeof(*content->align));
 
1998
 
 
1999
        content->header = content->headers;
 
2000
        content->cell = content->cells;
 
2001
        content->footer = content->footers;
 
2002
        content->align = content->aligns;
 
2003
}
 
2004
 
 
2005
/*
 
2006
 * Add a header to the table.
 
2007
 *
 
2008
 * Headers are not duplicated; you must ensure that the header string is
 
2009
 * available for the lifetime of the printTableContent struct.
 
2010
 *
 
2011
 * If translate is true, the function will pass the header through gettext.
 
2012
 * Otherwise, the header will not be translated.
 
2013
 *
 
2014
 * align is either 'l' or 'r', and specifies the alignment for cells in this
 
2015
 * column.
 
2016
 */
 
2017
void
 
2018
printTableAddHeader(printTableContent *const content, const char *header,
 
2019
                                        const bool translate, const char align)
 
2020
{
 
2021
#ifndef ENABLE_NLS
 
2022
        (void) translate;               /* unused parameter */
 
2023
#endif
 
2024
 
 
2025
        if (content->header >= content->headers + content->ncolumns)
 
2026
        {
 
2027
                fprintf(stderr, _("Cannot add header to table content: "
 
2028
                                                  "column count of %d exceeded.\n"),
 
2029
                                content->ncolumns);
 
2030
                exit(EXIT_FAILURE);
 
2031
        }
 
2032
 
 
2033
        *content->header = (char *) mbvalidate((unsigned char *) header,
 
2034
                                                                                   content->opt->encoding);
 
2035
#ifdef ENABLE_NLS
 
2036
        if (translate)
 
2037
                *content->header = _(*content->header);
 
2038
#endif
 
2039
        content->header++;
 
2040
 
 
2041
        *content->align = align;
 
2042
        content->align++;
 
2043
}
 
2044
 
 
2045
/*
 
2046
 * Add a cell to the table.
 
2047
 *
 
2048
 * Cells are not duplicated; you must ensure that the cell string is available
 
2049
 * for the lifetime of the printTableContent struct.
 
2050
 *
 
2051
 * If translate is true, the function will pass the cell through gettext.
 
2052
 * Otherwise, the cell will not be translated.
 
2053
 */
 
2054
void
 
2055
printTableAddCell(printTableContent *const content, const char *cell,
 
2056
                                  const bool translate)
 
2057
{
 
2058
#ifndef ENABLE_NLS
 
2059
        (void) translate;               /* unused parameter */
 
2060
#endif
 
2061
 
 
2062
        if (content->cell >= content->cells + (content->ncolumns * content->nrows))
 
2063
        {
 
2064
                fprintf(stderr, _("Cannot add cell to table content: "
 
2065
                                                  "total cell count of %d exceeded.\n"),
 
2066
                                content->ncolumns * content->nrows);
 
2067
                exit(EXIT_FAILURE);
 
2068
        }
 
2069
 
 
2070
        *content->cell = (char *) mbvalidate((unsigned char *) cell,
 
2071
                                                                                 content->opt->encoding);
 
2072
 
 
2073
#ifdef ENABLE_NLS
 
2074
        if (translate)
 
2075
                *content->header = _(*content->header);
 
2076
#endif
 
2077
        content->cell++;
 
2078
}
 
2079
 
 
2080
/*
 
2081
 * Add a footer to the table.
 
2082
 *
 
2083
 * Footers are added as elements of a singly-linked list, and the content is
 
2084
 * strdup'd, so there is no need to keep the original footer string around.
 
2085
 *
 
2086
 * Footers are never translated by the function.  If you want the footer
 
2087
 * translated you must do so yourself, before calling printTableAddFooter.  The
 
2088
 * reason this works differently to headers and cells is that footers tend to
 
2089
 * be made of up individually translated components, rather than being
 
2090
 * translated as a whole.
 
2091
 */
 
2092
void
 
2093
printTableAddFooter(printTableContent *const content, const char *footer)
 
2094
{
 
2095
        printTableFooter *f;
 
2096
 
 
2097
        f = pg_local_calloc(1, sizeof(*f));
 
2098
        f->data = pg_strdup(footer);
 
2099
 
 
2100
        if (content->footers == NULL)
 
2101
                content->footers = f;
 
2102
        else
 
2103
                content->footer->next = f;
 
2104
 
 
2105
        content->footer = f;
 
2106
}
 
2107
 
 
2108
/*
 
2109
 * Change the content of the last-added footer.
 
2110
 *
 
2111
 * The current contents of the last-added footer are freed, and replaced by the
 
2112
 * content given in *footer.  If there was no previous footer, add a new one.
 
2113
 *
 
2114
 * The content is strdup'd, so there is no need to keep the original string
 
2115
 * around.
 
2116
 */
 
2117
void
 
2118
printTableSetFooter(printTableContent *const content, const char *footer)
 
2119
{
 
2120
        if (content->footers != NULL)
 
2121
        {
 
2122
                free(content->footer->data);
 
2123
                content->footer->data = pg_strdup(footer);
 
2124
        }
 
2125
        else
 
2126
                printTableAddFooter(content, footer);
 
2127
}
 
2128
 
 
2129
/*
 
2130
 * Free all memory allocated to this struct.
 
2131
 *
 
2132
 * Once this has been called, the struct is unusable unless you pass it to
 
2133
 * printTableInit() again.
 
2134
 */
 
2135
void
 
2136
printTableCleanup(printTableContent *const content)
 
2137
{
 
2138
        free(content->headers);
 
2139
        free(content->cells);
 
2140
        free(content->aligns);
 
2141
 
 
2142
        content->opt = NULL;
 
2143
        content->title = NULL;
 
2144
        content->headers = NULL;
 
2145
        content->cells = NULL;
 
2146
        content->aligns = NULL;
 
2147
        content->header = NULL;
 
2148
        content->cell = NULL;
 
2149
        content->align = NULL;
 
2150
 
 
2151
        if (content->footers)
 
2152
        {
 
2153
                for (content->footer = content->footers; content->footer;)
 
2154
                {
 
2155
                        printTableFooter *f;
 
2156
 
 
2157
                        f = content->footer;
 
2158
                        content->footer = f->next;
 
2159
                        free(f->data);
 
2160
                        free(f);
 
2161
                }
 
2162
        }
 
2163
        content->footers = NULL;
 
2164
        content->footer = NULL;
 
2165
}
 
2166
 
 
2167
/*
 
2168
 * IsPagerNeeded
 
2169
 *
 
2170
 * Setup pager if required
 
2171
 */
 
2172
void
 
2173
IsPagerNeeded(const printTableContent *cont, const int extra_lines, FILE **fout,
 
2174
                          bool *is_pager)
 
2175
{
 
2176
        if (*fout == stdout)
 
2177
        {
 
2178
                int                     lines;
 
2179
 
 
2180
                if (cont->opt->expanded)
 
2181
                        lines = (cont->ncolumns + 1) * cont->nrows;
 
2182
                else
 
2183
                        lines = cont->nrows + 1;
 
2184
 
 
2185
                if (!cont->opt->tuples_only)
 
2186
                {
 
2187
                        printTableFooter *f;
 
2188
 
 
2189
                        /*
 
2190
                         * FIXME -- this is slightly bogus: it counts the number of
 
2191
                         * footers, not the number of lines in them.
 
2192
                         */
 
2193
                        for (f = cont->footers; f; f = f->next)
 
2194
                                lines++;
 
2195
                }
 
2196
 
 
2197
                *fout = PageOutput(lines + extra_lines, cont->opt->pager);
 
2198
                *is_pager = (*fout != stdout);
 
2199
        }
 
2200
        else
 
2201
                *is_pager = false;
 
2202
}
 
2203
 
 
2204
/*
 
2205
 * Use this to print just any table in the supported formats.
 
2206
 */
 
2207
void
 
2208
printTable(const printTableContent *cont, FILE *fout, FILE *flog)
 
2209
{
 
2210
        bool            is_pager = false;
 
2211
        
 
2212
        if (cancel_pressed)
 
2213
                return;
 
2214
 
 
2215
        if (cont->opt->format == PRINT_NOTHING)
 
2216
                return;
 
2217
 
 
2218
        /* print_aligned_text() handles the pager itself */
 
2219
        if ((cont->opt->format != PRINT_ALIGNED &&
 
2220
                 cont->opt->format != PRINT_WRAPPED) ||
 
2221
                 cont->opt->expanded)
 
2222
                IsPagerNeeded(cont, 0, &fout, &is_pager);
 
2223
 
 
2224
        /* print the stuff */
 
2225
 
 
2226
        if (flog)
 
2227
                print_aligned_text(cont, flog);
 
2228
 
 
2229
        switch (cont->opt->format)
 
2230
        {
 
2231
                case PRINT_UNALIGNED:
 
2232
                        if (cont->opt->expanded)
 
2233
                                print_unaligned_vertical(cont, fout);
 
2234
                        else
 
2235
                                print_unaligned_text(cont, fout);
 
2236
                        break;
 
2237
                case PRINT_ALIGNED:
 
2238
                case PRINT_WRAPPED:
 
2239
                        if (cont->opt->expanded)
 
2240
                                print_aligned_vertical(cont, fout);
 
2241
                        else
 
2242
                                print_aligned_text(cont, fout);
 
2243
                        break;
 
2244
                case PRINT_HTML:
 
2245
                        if (cont->opt->expanded)
 
2246
                                print_html_vertical(cont, fout);
 
2247
                        else
 
2248
                                print_html_text(cont, fout);
 
2249
                        break;
 
2250
                case PRINT_LATEX:
 
2251
                        if (cont->opt->expanded)
 
2252
                                print_latex_vertical(cont, fout);
 
2253
                        else
 
2254
                                print_latex_text(cont, fout);
 
2255
                        break;
 
2256
                case PRINT_TROFF_MS:
 
2257
                        if (cont->opt->expanded)
 
2258
                                print_troff_ms_vertical(cont, fout);
 
2259
                        else
 
2260
                                print_troff_ms_text(cont, fout);
 
2261
                        break;
 
2262
                default:
 
2263
                        fprintf(stderr, _("invalid fout format (internal error): %d"),
 
2264
                                        cont->opt->format);
 
2265
                        exit(EXIT_FAILURE);
 
2266
        }
 
2267
 
 
2268
        if (is_pager)
 
2269
                ClosePager(fout);
 
2270
}
 
2271
 
 
2272
/*
 
2273
 * Use this to print query results
 
2274
 *
 
2275
 * It calls printTable with all the things set straight.
 
2276
 */
 
2277
void
 
2278
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
 
2279
{
 
2280
        printTableContent cont;
 
2281
        int                     i,
 
2282
                                r,
 
2283
                                c;
 
2284
 
 
2285
        if (cancel_pressed)
 
2286
                return;
 
2287
 
 
2288
        printTableInit(&cont, &opt->topt, opt->title,
 
2289
                                   PQnfields(result), PQntuples(result));
 
2290
 
 
2291
        for (i = 0; i < cont.ncolumns; i++)
 
2292
        {
 
2293
                char            align;
 
2294
                Oid                     ftype = PQftype(result, i);
 
2295
 
 
2296
                switch (ftype)
 
2297
                {
 
2298
                        case INT2OID:
 
2299
                        case INT4OID:
 
2300
                        case INT8OID:
 
2301
                        case FLOAT4OID:
 
2302
                        case FLOAT8OID:
 
2303
                        case NUMERICOID:
 
2304
                        case OIDOID:
 
2305
                        case XIDOID:
 
2306
                        case CIDOID:
 
2307
                        case CASHOID:
 
2308
                                align = 'r';
 
2309
                                break;
 
2310
                        default:
 
2311
                                align = 'l';
 
2312
                                break;
 
2313
                }
 
2314
 
 
2315
                printTableAddHeader(&cont, PQfname(result, i),
 
2316
                                                        opt->translate_header, align);
 
2317
        }
 
2318
 
 
2319
        /* set cells */
 
2320
        for (r = 0; r < cont.nrows; r++)
 
2321
        {
 
2322
                for (c = 0; c < cont.ncolumns; c++)
 
2323
                {
 
2324
                        char            *cell;
 
2325
                        bool             translate;
 
2326
 
 
2327
                        if (PQgetisnull(result, r, c))
 
2328
                                cell = opt->nullPrint ? opt->nullPrint : "";
 
2329
                        else
 
2330
                                cell = PQgetvalue(result, r, c);
 
2331
 
 
2332
                        translate = (opt->translate_columns && opt->translate_columns[c]);
 
2333
                        printTableAddCell(&cont, cell, translate);
 
2334
                }
 
2335
        }
 
2336
 
 
2337
        /* set footers */
 
2338
        if (opt->footers)
 
2339
        {
 
2340
                char            **footer;
 
2341
 
 
2342
                for (footer = opt->footers; *footer; footer++)
 
2343
                        printTableAddFooter(&cont, *footer);
 
2344
        }
 
2345
        else if (!opt->topt.expanded && opt->default_footer)
 
2346
        {
 
2347
                unsigned long total_records;
 
2348
                char            default_footer[100];
 
2349
 
 
2350
                total_records = opt->topt.prior_records + cont.nrows;
 
2351
                snprintf(default_footer, 100, ngettext("(1 row)", "(%lu rows)", total_records), total_records);
 
2352
 
 
2353
                printTableAddFooter(&cont, default_footer);
 
2354
        }
 
2355
 
 
2356
        printTable(&cont, fout, flog);
 
2357
        printTableCleanup(&cont);
 
2358
}
 
2359
 
 
2360
 
 
2361
void
 
2362
setDecimalLocale(void)
 
2363
{
 
2364
        struct lconv *extlconv;
 
2365
 
 
2366
        extlconv = localeconv();
 
2367
 
 
2368
        if (*extlconv->decimal_point)
 
2369
                decimal_point = pg_strdup(extlconv->decimal_point);
 
2370
        else
 
2371
                decimal_point = ".";    /* SQL output standard */
 
2372
        if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
 
2373
                grouping = pg_strdup(extlconv->grouping);
 
2374
        else
 
2375
                grouping = "3";                 /* most common */
 
2376
 
 
2377
        /* similar code exists in formatting.c */
 
2378
        if (*extlconv->thousands_sep)
 
2379
                thousands_sep = pg_strdup(extlconv->thousands_sep);
 
2380
        /* Make sure thousands separator doesn't match decimal point symbol. */
 
2381
        else if (strcmp(decimal_point, ",") != 0)
 
2382
                thousands_sep = ",";
 
2383
        else
 
2384
                thousands_sep = ".";
 
2385
}
 
2386
 
 
2387
/*
 
2388
 * Compute the byte distance to the end of the string or *target_width
 
2389
 * display character positions, whichever comes first.  Update *target_width
 
2390
 * to be the number of display character positions actually filled.
 
2391
 */
 
2392
static int
 
2393
strlen_max_width(unsigned char *str, int *target_width, int encoding)
 
2394
{
 
2395
        unsigned char *start = str;
 
2396
        unsigned char *end = str + strlen((char *) str);
 
2397
        int curr_width = 0;
 
2398
 
 
2399
        while (str < end)
 
2400
        {
 
2401
                int char_width = PQdsplen((char *) str, encoding);
 
2402
 
 
2403
                /*
 
2404
                 *      If the display width of the new character causes
 
2405
                 *      the string to exceed its target width, skip it
 
2406
                 *      and return.  However, if this is the first character
 
2407
                 *      of the string (curr_width == 0), we have to accept it.
 
2408
                 */
 
2409
                if (*target_width < curr_width + char_width && curr_width != 0)
 
2410
                        break;
 
2411
 
 
2412
                curr_width += char_width;
 
2413
                        
 
2414
                str += PQmblen((char *) str, encoding);
 
2415
        }
 
2416
 
 
2417
        *target_width = curr_width;
 
2418
        
 
2419
        return str - start;
 
2420
}