~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-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: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

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