OSDN Git Service

abf6864943b667666544dedfb5ef17779d1efd75
[pg-rex/syncrep.git] / src / bin / psql / print.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
5  *
6  * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.54 2005/01/01 05:43:08 momjian Exp $
7  */
8 #include "postgres_fe.h"
9 #include "common.h"
10 #include "print.h"
11
12 #include <math.h>
13 #include <signal.h>
14
15 #ifndef WIN32_CLIENT_ONLY
16 #include <unistd.h>
17 #endif
18
19 #ifndef WIN32
20 #include <sys/ioctl.h>                  /* for ioctl() */
21 #endif
22
23 #ifdef HAVE_TERMIOS_H
24 #include <termios.h>
25 #endif
26
27 #include "pqsignal.h"
28 #include "libpq-fe.h"
29
30 #include "mbprint.h"
31
32 /*************************/
33 /* Unaligned text                */
34 /*************************/
35
36
37 static void
38 print_unaligned_text(const char *title, const char *const * headers,
39                                   const char *const * cells, const char *const * footers,
40  const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones,
41                                          FILE *fout)
42 {
43         unsigned int col_count = 0;
44         unsigned int i;
45         const char *const * ptr;
46         bool            need_recordsep = false;
47
48         if (!opt_fieldsep)
49                 opt_fieldsep = "";
50         if (!opt_recordsep)
51                 opt_recordsep = "";
52
53         /* print title */
54         if (!opt_barebones && title)
55                 fprintf(fout, "%s%s", title, opt_recordsep);
56
57         /* print headers and count columns */
58         for (ptr = headers; *ptr; ptr++)
59         {
60                 col_count++;
61                 if (!opt_barebones)
62                 {
63                         if (col_count > 1)
64                                 fputs(opt_fieldsep, fout);
65                         fputs(*ptr, fout);
66                 }
67         }
68         if (!opt_barebones)
69                 need_recordsep = true;
70
71         /* print cells */
72         i = 0;
73         for (ptr = cells; *ptr; ptr++)
74         {
75                 if (need_recordsep)
76                 {
77                         fputs(opt_recordsep, fout);
78                         need_recordsep = false;
79                 }
80                 fputs(*ptr, fout);
81                 if ((i + 1) % col_count)
82                         fputs(opt_fieldsep, fout);
83                 else
84                         need_recordsep = true;
85                 i++;
86         }
87
88         /* print footers */
89
90         if (!opt_barebones && footers)
91                 for (ptr = footers; *ptr; ptr++)
92                 {
93                         if (need_recordsep)
94                         {
95                                 fputs(opt_recordsep, fout);
96                                 need_recordsep = false;
97                         }
98                         fputs(*ptr, fout);
99                         need_recordsep = true;
100                 }
101
102         /* the last record needs to be concluded with a newline */
103         if (need_recordsep)
104                 fputc('\n', fout);
105 }
106
107
108
109 static void
110 print_unaligned_vertical(const char *title, const char *const * headers,
111                                   const char *const * cells, const char *const * footers,
112  const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones,
113                                                  FILE *fout)
114 {
115         unsigned int col_count = 0;
116         unsigned int i;
117         const char *const * ptr;
118
119         if (!opt_fieldsep)
120                 opt_fieldsep = "";
121         if (!opt_recordsep)
122                 opt_recordsep = "";
123
124         /* print title */
125         if (!opt_barebones && title)
126                 fputs(title, fout);
127
128         /* count columns */
129         for (ptr = headers; *ptr; ptr++)
130                 col_count++;
131
132         /* print records */
133         for (i = 0, ptr = cells; *ptr; i++, ptr++)
134         {
135                 if (i != 0 || (!opt_barebones && title))
136                 {
137                         fputs(opt_recordsep, fout);
138                         if (i % col_count == 0)
139                                 fputs(opt_recordsep, fout);             /* another one */
140                 }
141
142                 fputs(headers[i % col_count], fout);
143                 fputs(opt_fieldsep, fout);
144                 fputs(*ptr, fout);
145         }
146
147         /* print footers */
148         if (!opt_barebones && footers && *footers)
149         {
150                 fputs(opt_recordsep, fout);
151                 for (ptr = footers; *ptr; ptr++)
152                 {
153                         fputs(opt_recordsep, fout);
154                         fputs(*ptr, fout);
155                 }
156         }
157
158         fputc('\n', fout);
159 }
160
161
162
163 /********************/
164 /* Aligned text         */
165 /********************/
166
167
168 /* draw "line" */
169 static void
170 _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout)
171 {
172         unsigned int i,
173                                 j;
174
175         if (border == 1)
176                 fputc('-', fout);
177         else if (border == 2)
178                 fputs("+-", fout);
179
180         for (i = 0; i < col_count; i++)
181         {
182                 for (j = 0; j < widths[i]; j++)
183                         fputc('-', fout);
184
185                 if (i < col_count - 1)
186                 {
187                         if (border == 0)
188                                 fputc(' ', fout);
189                         else
190                                 fputs("-+-", fout);
191                 }
192         }
193
194         if (border == 2)
195                 fputs("-+", fout);
196         else if (border == 1)
197                 fputc('-', fout);
198
199         fputc('\n', fout);
200 }
201
202
203
204 static void
205 print_aligned_text(const char *title, const char *const * headers,
206                                    const char *const * cells, const char *const * footers,
207                                    const char *opt_align, bool opt_barebones,
208                                    unsigned short int opt_border, int encoding,
209                                    FILE *fout)
210 {
211         unsigned int col_count = 0;
212         unsigned int cell_count = 0;
213         unsigned int *head_w,
214                            *cell_w;
215         unsigned int i,
216                                 tmp;
217         unsigned int *widths,
218                                 total_w;
219         const char *const * ptr;
220
221         /* count columns */
222         for (ptr = headers; *ptr; ptr++)
223                 col_count++;
224
225         if (col_count > 0)
226         {
227                 widths = calloc(col_count, sizeof(*widths));
228                 if (!widths)
229                 {
230                         fprintf(stderr, gettext("out of memory\n"));
231                         exit(EXIT_FAILURE);
232                 }
233
234                 head_w = calloc(col_count, sizeof(*head_w));
235                 if (!head_w)
236                 {
237                         fprintf(stderr, gettext("out of memory\n"));
238                         exit(EXIT_FAILURE);
239                 }
240         }
241         else
242         {
243                 widths = NULL;
244                 head_w = NULL;
245         }
246
247         /* count cells (rows * cols) */
248         for (ptr = cells; *ptr; ptr++)
249                 cell_count++;
250
251         if (cell_count > 0)
252         {
253                 cell_w = calloc(cell_count, sizeof(*cell_w));
254                 if (!cell_w)
255                 {
256                         fprintf(stderr, gettext("out of memory\n"));
257                         exit(EXIT_FAILURE);
258                 }
259         }
260         else
261                 cell_w = NULL;
262
263         /* calc column widths */
264         for (i = 0; i < col_count; i++)
265         {
266                 tmp = pg_wcswidth((unsigned char *) headers[i], strlen(headers[i]), encoding);
267                 if (tmp > widths[i])
268                         widths[i] = tmp;
269                 head_w[i] = tmp;
270         }
271
272         for (i = 0, ptr = cells; *ptr; ptr++, i++)
273         {
274                 tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding);
275                 if (tmp > widths[i % col_count])
276                         widths[i % col_count] = tmp;
277                 cell_w[i] = tmp;
278         }
279
280         if (opt_border == 0)
281                 total_w = col_count - 1;
282         else if (opt_border == 1)
283                 total_w = col_count * 3 - 1;
284         else
285                 total_w = col_count * 3 + 1;
286
287         for (i = 0; i < col_count; i++)
288                 total_w += widths[i];
289
290         /* print title */
291         if (title && !opt_barebones)
292         {
293                 tmp = pg_wcswidth((unsigned char *) title, strlen(title), encoding);
294                 if (tmp >= total_w)
295                         fprintf(fout, "%s\n", title);
296                 else
297                         fprintf(fout, "%-*s%s\n", (total_w - tmp) / 2, "", title);
298         }
299
300         /* print headers */
301         if (!opt_barebones)
302         {
303                 if (opt_border == 2)
304                         _print_horizontal_line(col_count, widths, opt_border, fout);
305
306                 if (opt_border == 2)
307                         fputs("| ", fout);
308                 else if (opt_border == 1)
309                         fputc(' ', fout);
310
311                 for (i = 0; i < col_count; i++)
312                 {
313                         unsigned int nbspace;
314
315                         nbspace = widths[i] - head_w[i];
316
317                         /* centered */
318                         fprintf(fout, "%-*s%s%-*s",
319                                         nbspace / 2, "", headers[i], (nbspace + 1) / 2, "");
320
321                         if (i < col_count - 1)
322                         {
323                                 if (opt_border == 0)
324                                         fputc(' ', fout);
325                                 else
326                                         fputs(" | ", fout);
327                         }
328                 }
329
330                 if (opt_border == 2)
331                         fputs(" |", fout);
332                 else if (opt_border == 1)
333                         fputc(' ', fout);;
334                 fputc('\n', fout);
335
336                 _print_horizontal_line(col_count, widths, opt_border, fout);
337         }
338
339         /* print cells */
340         for (i = 0, ptr = cells; *ptr; i++, ptr++)
341         {
342                 /* beginning of line */
343                 if (i % col_count == 0)
344                 {
345                         if (opt_border == 2)
346                                 fputs("| ", fout);
347                         else if (opt_border == 1)
348                                 fputc(' ', fout);
349                 }
350
351                 /* content */
352                 if (opt_align[i % col_count] == 'r')
353                 {
354                         fprintf(fout, "%*s%s",
355                                         widths[i % col_count] - cell_w[i], "", cells[i]);
356                 }
357                 else
358                 {
359                         if ((i + 1) % col_count == 0 && opt_border != 2)
360                                 fputs(cells[i], fout);
361                         else
362                                 fprintf(fout, "%-s%*s", cells[i],
363                                                 widths[i % col_count] - cell_w[i], "");
364                 }
365
366                 /* divider */
367                 if ((i + 1) % col_count)
368                 {
369                         if (opt_border == 0)
370                                 fputc(' ', fout);
371                         else
372                                 fputs(" | ", fout);
373                 }
374                 /* end of line */
375                 else
376                 {
377                         if (opt_border == 2)
378                                 fputs(" |", fout);
379                         fputc('\n', fout);
380                 }
381         }
382
383         if (opt_border == 2)
384                 _print_horizontal_line(col_count, widths, opt_border, fout);
385
386         /* print footers */
387         if (footers && !opt_barebones)
388                 for (ptr = footers; *ptr; ptr++)
389                         fprintf(fout, "%s\n", *ptr);
390
391 #ifndef __MINGW32__
392
393         /*
394          * for some reason MinGW outputs an extra newline, so this supresses
395          * it
396          */
397         fputc('\n', fout);
398 #endif
399
400         /* clean up */
401         free(cell_w);
402         free(head_w);
403         free(widths);
404 }
405
406
407
408 static void
409 print_aligned_vertical(const char *title, const char *const * headers,
410                                   const char *const * cells, const char *const * footers,
411                                            bool opt_barebones, unsigned short int opt_border,
412                                            int encoding, FILE *fout)
413 {
414         unsigned int col_count = 0;
415         unsigned int record = 1;
416         const char *const * ptr;
417         unsigned int i,
418                                 tmp = 0,
419                                 hwidth = 0,
420                                 dwidth = 0;
421         char       *divider;
422         unsigned int cell_count = 0;
423         unsigned int *cell_w,
424                            *head_w;
425
426         if (cells[0] == NULL)
427         {
428                 puts(gettext("(No rows)\n"));
429                 return;
430         }
431
432         /* count headers and find longest one */
433         for (ptr = headers; *ptr; ptr++)
434                 col_count++;
435         if (col_count > 0)
436         {
437                 head_w = calloc(col_count, sizeof(*head_w));
438                 if (!head_w)
439                 {
440                         fprintf(stderr, gettext("out of memory\n"));
441                         exit(EXIT_FAILURE);
442                 }
443         }
444         else
445                 head_w = NULL;
446
447         for (i = 0; i < col_count; i++)
448         {
449                 tmp = pg_wcswidth((unsigned char *) headers[i], strlen(headers[i]), encoding);
450                 if (tmp > hwidth)
451                         hwidth = tmp;
452                 head_w[i] = tmp;
453         }
454
455         /* Count cells, find their lengths */
456         for (ptr = cells; *ptr; ptr++)
457                 cell_count++;
458
459         if (cell_count > 0)
460         {
461                 cell_w = calloc(cell_count, sizeof(*cell_w));
462                 if (!cell_w)
463                 {
464                         fprintf(stderr, gettext("out of memory\n"));
465                         exit(EXIT_FAILURE);
466                 }
467         }
468         else
469                 cell_w = NULL;
470
471         /* find longest data cell */
472         for (i = 0, ptr = cells; *ptr; ptr++, i++)
473         {
474                 tmp = pg_wcswidth((unsigned char *) *ptr, strlen(*ptr), encoding);
475                 if (tmp > dwidth)
476                         dwidth = tmp;
477                 cell_w[i] = tmp;
478         }
479
480         /* print title */
481         if (!opt_barebones && title)
482                 fprintf(fout, "%s\n", title);
483
484         /* make horizontal border */
485         divider = malloc(hwidth + dwidth + 10);
486         if (!divider)
487         {
488                 fprintf(stderr, gettext("out of memory\n"));
489                 exit(EXIT_FAILURE);
490         }
491         divider[0] = '\0';
492         if (opt_border == 2)
493                 strcat(divider, "+-");
494         for (i = 0; i < hwidth; i++)
495                 strcat(divider, opt_border > 0 ? "-" : " ");
496         if (opt_border > 0)
497                 strcat(divider, "-+-");
498         else
499                 strcat(divider, " ");
500         for (i = 0; i < dwidth; i++)
501                 strcat(divider, opt_border > 0 ? "-" : " ");
502         if (opt_border == 2)
503                 strcat(divider, "-+");
504
505         /* print records */
506         for (i = 0, ptr = cells; *ptr; i++, ptr++)
507         {
508                 if (i % col_count == 0)
509                 {
510                         if (!opt_barebones)
511                         {
512                                 char       *record_str = malloc(32);
513                                 size_t          record_str_len;
514
515                                 if (!record_str)
516                                 {
517                                         fprintf(stderr, gettext("out of memory\n"));
518                                         exit(EXIT_FAILURE);
519                                 }
520
521                                 if (opt_border == 0)
522                                         snprintf(record_str, 32, "* Record %d", record++);
523                                 else
524                                         snprintf(record_str, 32, "[ RECORD %d ]", record++);
525                                 record_str_len = strlen(record_str);
526
527                                 if (record_str_len + opt_border > strlen(divider))
528                                         fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
529                                 else
530                                 {
531                                         char       *div_copy = strdup(divider);
532
533                                         if (!div_copy)
534                                         {
535                                                 fprintf(stderr, gettext("out of memory\n"));
536                                                 exit(EXIT_FAILURE);
537                                         }
538
539                                         strncpy(div_copy + opt_border, record_str, record_str_len);
540                                         fprintf(fout, "%s\n", div_copy);
541                                         free(div_copy);
542                                 }
543                                 free(record_str);
544                         }
545                         else if (i != 0 || opt_border == 2)
546                                 fprintf(fout, "%s\n", divider);
547                 }
548
549                 if (opt_border == 2)
550                         fputs("| ", fout);
551                 fprintf(fout, "%-s%*s", headers[i % col_count],
552                                 hwidth - head_w[i % col_count], "");
553
554                 if (opt_border > 0)
555                         fputs(" | ", fout);
556                 else
557                         fputs(" ", fout);
558
559                 if (opt_border < 2)
560                         fprintf(fout, "%s\n", *ptr);
561                 else
562                         fprintf(fout, "%-s%*s |\n", *ptr, dwidth - cell_w[i], "");
563         }
564
565         if (opt_border == 2)
566                 fprintf(fout, "%s\n", divider);
567
568         /* print footers */
569
570         if (!opt_barebones && footers && *footers)
571         {
572                 if (opt_border < 2)
573                         fputc('\n', fout);
574                 for (ptr = footers; *ptr; ptr++)
575                         fprintf(fout, "%s\n", *ptr);
576         }
577
578         fputc('\n', fout);
579         free(divider);
580
581         free(cell_w);
582         free(head_w);
583 }
584
585
586
587
588
589 /**********************/
590 /* HTML printing ******/
591 /**********************/
592
593
594 void
595 html_escaped_print(const char *in, FILE *fout)
596 {
597         const char *p;
598
599         for (p = in; *p; p++)
600                 switch (*p)
601                 {
602                         case '&':
603                                 fputs("&amp;", fout);
604                                 break;
605                         case '<':
606                                 fputs("&lt;", fout);
607                                 break;
608                         case '>':
609                                 fputs("&gt;", fout);
610                                 break;
611                         case '\n':
612                                 fputs("<br />\n", fout);
613                                 break;
614                         case '"':
615                                 fputs("&quot;", fout);
616                                 break;
617                         case '\'':
618                                 fputs("&apos;", fout);
619                                 break;
620                         default:
621                                 fputc(*p, fout);
622                 }
623 }
624
625
626
627 static void
628 print_html_text(const char *title, const char *const * headers,
629                                 const char *const * cells, const char *const * footers,
630 const char *opt_align, bool opt_barebones, unsigned short int opt_border,
631                                 const char *opt_table_attr,
632                                 FILE *fout)
633 {
634         unsigned int col_count = 0;
635         unsigned int i;
636         const char *const * ptr;
637
638         fprintf(fout, "<table border=\"%d\"", opt_border);
639         if (opt_table_attr)
640                 fprintf(fout, " %s", opt_table_attr);
641         fputs(">\n", fout);
642
643         /* print title */
644         if (!opt_barebones && title)
645         {
646                 fputs("  <caption>", fout);
647                 html_escaped_print(title, fout);
648                 fputs("</caption>\n", fout);
649         }
650
651         /* print headers and count columns */
652         if (!opt_barebones)
653                 fputs("  <tr>\n", fout);
654         for (i = 0, ptr = headers; *ptr; i++, ptr++)
655         {
656                 col_count++;
657                 if (!opt_barebones)
658                 {
659                         fputs("    <th align=\"center\">", fout);
660                         html_escaped_print(*ptr, fout);
661                         fputs("</th>\n", fout);
662                 }
663         }
664         if (!opt_barebones)
665                 fputs("  </tr>\n", fout);
666
667         /* print cells */
668         for (i = 0, ptr = cells; *ptr; i++, ptr++)
669         {
670                 if (i % col_count == 0)
671                         fputs("  <tr valign=\"top\">\n", fout);
672
673                 fprintf(fout, "    <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left");
674                 if ((*ptr)[strspn(*ptr, " \t")] == '\0')                /* is string only
675                                                                                                                  * whitespace? */
676                         fputs("&nbsp; ", fout);
677                 else
678                         html_escaped_print(*ptr, fout);
679                 fputs("</td>\n", fout);
680
681                 if ((i + 1) % col_count == 0)
682                         fputs("  </tr>\n", fout);
683         }
684
685         fputs("</table>\n", fout);
686
687         /* print footers */
688
689         if (!opt_barebones && footers && *footers)
690         {
691                 fputs("<p>", fout);
692                 for (ptr = footers; *ptr; ptr++)
693                 {
694                         html_escaped_print(*ptr, fout);
695                         fputs("<br />\n", fout);
696                 }
697                 fputs("</p>", fout);
698         }
699         fputc('\n', fout);
700 }
701
702
703
704 static void
705 print_html_vertical(const char *title, const char *const * headers,
706                                   const char *const * cells, const char *const * footers,
707 const char *opt_align, bool opt_barebones, unsigned short int opt_border,
708                                         const char *opt_table_attr,
709                                         FILE *fout)
710 {
711         unsigned int col_count = 0;
712         unsigned int i;
713         unsigned int record = 1;
714         const char *const * ptr;
715
716         fprintf(fout, "<table border=\"%d\"", opt_border);
717         if (opt_table_attr)
718                 fprintf(fout, " %s", opt_table_attr);
719         fputs(">\n", fout);
720
721         /* print title */
722         if (!opt_barebones && title)
723         {
724                 fputs("  <caption>", fout);
725                 html_escaped_print(title, fout);
726                 fputs("</caption>\n", fout);
727         }
728
729         /* count columns */
730         for (ptr = headers; *ptr; ptr++)
731                 col_count++;
732
733         /* print records */
734         for (i = 0, ptr = cells; *ptr; i++, ptr++)
735         {
736                 if (i % col_count == 0)
737                 {
738                         if (!opt_barebones)
739                                 fprintf(fout, "\n  <tr><td colspan=\"2\" align=\"center\">Record %d</td></tr>\n", record++);
740                         else
741                                 fputs("\n  <tr><td colspan=\"2\">&nbsp;</td></tr>\n", fout);
742                 }
743                 fputs("  <tr valign=\"top\">\n"
744                           "    <th>", fout);
745                 html_escaped_print(headers[i % col_count], fout);
746                 fputs("</th>\n", fout);
747
748                 fprintf(fout, "    <td align=\"%s\">", opt_align[i % col_count] == 'r' ? "right" : "left");
749                 if ((*ptr)[strspn(*ptr, " \t")] == '\0')                /* is string only
750                                                                                                                  * whitespace? */
751                         fputs("&nbsp; ", fout);
752                 else
753                         html_escaped_print(*ptr, fout);
754                 fputs("</td>\n  </tr>\n", fout);
755         }
756
757         fputs("</table>\n", fout);
758
759         /* print footers */
760         if (!opt_barebones && footers && *footers)
761         {
762                 fputs("<p>", fout);
763                 for (ptr = footers; *ptr; ptr++)
764                 {
765                         html_escaped_print(*ptr, fout);
766                         fputs("<br />\n", fout);
767                 }
768                 fputs("</p>", fout);
769         }
770         fputc('\n', fout);
771 }
772
773
774
775 /*************************/
776 /* LaTeX                 */
777 /*************************/
778
779
780 static void
781 latex_escaped_print(const char *in, FILE *fout)
782 {
783         const char *p;
784
785         for (p = in; *p; p++)
786                 switch (*p)
787                 {
788                         case '&':
789                                 fputs("\\&", fout);
790                                 break;
791                         case '%':
792                                 fputs("\\%", fout);
793                                 break;
794                         case '$':
795                                 fputs("\\$", fout);
796                                 break;
797                         case '_':
798                                 fputs("\\_", fout);
799                                 break;
800                         case '{':
801                                 fputs("\\{", fout);
802                                 break;
803                         case '}':
804                                 fputs("\\}", fout);
805                                 break;
806                         case '\\':
807                                 fputs("\\backslash", fout);
808                                 break;
809                         case '\n':
810                                 fputs("\\\\", fout);
811                                 break;
812                         default:
813                                 fputc(*p, fout);
814                 }
815 }
816
817
818
819 static void
820 print_latex_text(const char *title, const char *const * headers,
821                                  const char *const * cells, const char *const * footers,
822 const char *opt_align, bool opt_barebones, unsigned short int opt_border,
823                                  FILE *fout)
824 {
825         unsigned int col_count = 0;
826         unsigned int i;
827         const char *const * ptr;
828
829
830         /* print title */
831         if (!opt_barebones && title)
832         {
833                 fputs("\\begin{center}\n", fout);
834                 latex_escaped_print(title, fout);
835                 fputs("\n\\end{center}\n\n", fout);
836         }
837
838         /* count columns */
839         for (ptr = headers; *ptr; ptr++)
840                 col_count++;
841
842         /* begin environment and set alignments and borders */
843         fputs("\\begin{tabular}{", fout);
844
845         if (opt_border == 2)
846                 fputs("| ", fout);
847         for (i = 0; i < col_count; i++)
848         {
849                 fputc(*(opt_align + i), fout);
850                 if (opt_border != 0 && i < col_count - 1)
851                         fputs(" | ", fout);
852         }
853         if (opt_border == 2)
854                 fputs(" |", fout);
855
856         fputs("}\n", fout);
857
858         if (!opt_barebones && opt_border == 2)
859                 fputs("\\hline\n", fout);
860
861         /* print headers and count columns */
862         for (i = 0, ptr = headers; i < col_count; i++, ptr++)
863         {
864                 if (!opt_barebones)
865                 {
866                         if (i != 0)
867                                 fputs(" & ", fout);
868                         fputs("\\textit{", fout);
869                         latex_escaped_print(*ptr, fout);
870                         fputc('}', fout);
871                 }
872         }
873
874         if (!opt_barebones)
875         {
876                 fputs(" \\\\\n", fout);
877                 fputs("\\hline\n", fout);
878         }
879
880         /* print cells */
881         for (i = 0, ptr = cells; *ptr; i++, ptr++)
882         {
883                 latex_escaped_print(*ptr, fout);
884
885                 if ((i + 1) % col_count == 0)
886                         fputs(" \\\\\n", fout);
887                 else
888                         fputs(" & ", fout);
889         }
890
891         if (opt_border == 2)
892                 fputs("\\hline\n", fout);
893
894         fputs("\\end{tabular}\n\n\\noindent ", fout);
895
896
897         /* print footers */
898
899         if (footers && !opt_barebones)
900                 for (ptr = footers; *ptr; ptr++)
901                 {
902                         latex_escaped_print(*ptr, fout);
903                         fputs(" \\\\\n", fout);
904                 }
905
906         fputc('\n', fout);
907 }
908
909
910
911 static void
912 print_latex_vertical(const char *title, const char *const * headers,
913                                   const char *const * cells, const char *const * footers,
914 const char *opt_align, bool opt_barebones, unsigned short int opt_border,
915                                          FILE *fout)
916 {
917         unsigned int col_count = 0;
918         unsigned int i;
919         const char *const * ptr;
920         unsigned int record = 1;
921
922         (void) opt_align;                       /* currently unused parameter */
923
924         /* print title */
925         if (!opt_barebones && title)
926         {
927                 fputs("\\begin{center}\n", fout);
928                 latex_escaped_print(title, fout);
929                 fputs("\n\\end{center}\n\n", fout);
930         }
931
932         /* begin environment and set alignments and borders */
933         fputs("\\begin{tabular}{", fout);
934         if (opt_border == 0)
935                 fputs("cl", fout);
936         else if (opt_border == 1)
937                 fputs("c|l", fout);
938         else if (opt_border == 2)
939                 fputs("|c|l|", fout);
940         fputs("}\n", fout);
941
942
943         /* count columns */
944         for (ptr = headers; *ptr; ptr++)
945                 col_count++;
946
947
948         /* print records */
949         for (i = 0, ptr = cells; *ptr; i++, ptr++)
950         {
951                 /* new record */
952                 if (i % col_count == 0)
953                 {
954                         if (!opt_barebones)
955                         {
956                                 if (opt_border == 2)
957                                 {
958                                         fputs("\\hline\n", fout);
959                                         fprintf(fout, "\\multicolumn{2}{|c|}{\\textit{Record %d}} \\\\\n", record++);
960                                 }
961                                 else
962                                         fprintf(fout, "\\multicolumn{2}{c}{\\textit{Record %d}} \\\\\n", record++);
963                         }
964                         if (opt_border >= 1)
965                                 fputs("\\hline\n", fout);
966                 }
967
968                 latex_escaped_print(headers[i % col_count], fout);
969                 fputs(" & ", fout);
970                 latex_escaped_print(*ptr, fout);
971                 fputs(" \\\\\n", fout);
972         }
973
974         if (opt_border == 2)
975                 fputs("\\hline\n", fout);
976
977         fputs("\\end{tabular}\n\n\\noindent ", fout);
978
979
980         /* print footers */
981
982         if (footers && !opt_barebones)
983                 for (ptr = footers; *ptr; ptr++)
984                 {
985                         latex_escaped_print(*ptr, fout);
986                         fputs(" \\\\\n", fout);
987                 }
988
989         fputc('\n', fout);
990 }
991
992
993
994 /********************************/
995 /* Public functions             */
996 /********************************/
997
998
999 /*
1000  * PageOutput
1001  *
1002  * Tests if pager is needed and returns appropriate FILE pointer.
1003  */
1004 FILE *
1005 PageOutput(int lines, unsigned short int pager)
1006 {
1007         /* check whether we need / can / are supposed to use pager */
1008         if (pager
1009 #ifndef WIN32
1010                 &&
1011                 isatty(fileno(stdin)) &&
1012                 isatty(fileno(stdout))
1013 #endif
1014                 )
1015         {
1016                 const char *pagerprog;
1017
1018 #ifdef TIOCGWINSZ
1019                 int                     result;
1020                 struct winsize screen_size;
1021
1022                 result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
1023
1024                 /* >= accounts for a one-line prompt */
1025                 if (result == -1 || lines >= screen_size.ws_row || pager > 1)
1026                 {
1027 #endif
1028                         pagerprog = getenv("PAGER");
1029                         if (!pagerprog)
1030                                 pagerprog = DEFAULT_PAGER;
1031 #ifndef WIN32
1032                         pqsignal(SIGPIPE, SIG_IGN);
1033 #endif
1034                         return popen(pagerprog, "w");
1035 #ifdef TIOCGWINSZ
1036                 }
1037 #endif
1038         }
1039
1040         return stdout;
1041 }
1042
1043
1044
1045 void
1046 printTable(const char *title,
1047                    const char *const * headers,
1048                    const char *const * cells,
1049                    const char *const * footers,
1050                    const char *align,
1051                    const printTableOpt *opt, FILE *fout)
1052 {
1053         const char *default_footer[] = {NULL};
1054         unsigned short int border = opt->border;
1055         FILE       *output;
1056
1057         if (opt->format == PRINT_NOTHING)
1058                 return;
1059
1060         if (!footers)
1061                 footers = default_footer;
1062
1063         if (opt->format != PRINT_HTML && border > 2)
1064                 border = 2;
1065
1066         if (fout == stdout)
1067         {
1068                 int                     col_count = 0,
1069                                         row_count = 0,
1070                                         lines;
1071                 const char *const * ptr;
1072
1073                 /* rough estimate of columns and rows */
1074                 if (headers)
1075                         for (ptr = headers; *ptr; ptr++)
1076                                 col_count++;
1077                 if (cells)
1078                         for (ptr = cells; *ptr; ptr++)
1079                                 row_count++;
1080                 if (col_count > 0)
1081                         row_count /= col_count;
1082
1083                 if (opt->expanded)
1084                         lines = (col_count + 1) * row_count;
1085                 else
1086                         lines = row_count + 1;
1087                 if (footers && !opt->tuples_only)
1088                         for (ptr = footers; *ptr; ptr++)
1089                                 lines++;
1090                 output = PageOutput(lines, opt->pager);
1091         }
1092         else
1093                 output = fout;
1094
1095         /* print the stuff */
1096
1097         switch (opt->format)
1098         {
1099                 case PRINT_UNALIGNED:
1100                         if (opt->expanded)
1101                                 print_unaligned_vertical(title, headers, cells, footers, opt->fieldSep, opt->recordSep, opt->tuples_only, output);
1102                         else
1103                                 print_unaligned_text(title, headers, cells, footers, opt->fieldSep, opt->recordSep, opt->tuples_only, output);
1104                         break;
1105                 case PRINT_ALIGNED:
1106                         if (opt->expanded)
1107                                 print_aligned_vertical(title, headers, cells, footers, opt->tuples_only, border, opt->encoding, output);
1108                         else
1109                                 print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, border, opt->encoding, output);
1110                         break;
1111                 case PRINT_HTML:
1112                         if (opt->expanded)
1113                                 print_html_vertical(title, headers, cells, footers, align, opt->tuples_only, border, opt->tableAttr, output);
1114                         else
1115                                 print_html_text(title, headers, cells, footers, align, opt->tuples_only, border, opt->tableAttr, output);
1116                         break;
1117                 case PRINT_LATEX:
1118                         if (opt->expanded)
1119                                 print_latex_vertical(title, headers, cells, footers, align, opt->tuples_only, border, output);
1120                         else
1121                                 print_latex_text(title, headers, cells, footers, align, opt->tuples_only, border, output);
1122                         break;
1123                 default:
1124                         fprintf(stderr, "+ Oops, you shouldn't see this!\n");
1125         }
1126
1127         /* Only close if we used the pager */
1128         if (fout == stdout && output != stdout)
1129         {
1130                 pclose(output);
1131 #ifndef WIN32
1132                 pqsignal(SIGPIPE, SIG_DFL);
1133 #endif
1134         }
1135 }
1136
1137
1138
1139 void
1140 printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout)
1141 {
1142         int                     nfields;
1143         int                     ncells;
1144         const char **headers;
1145         const char **cells;
1146         char      **footers;
1147         char       *align;
1148         int                     i;
1149
1150         /* extract headers */
1151         nfields = PQnfields(result);
1152
1153         headers = calloc(nfields + 1, sizeof(*headers));
1154         if (!headers)
1155         {
1156                 fprintf(stderr, gettext("out of memory\n"));
1157                 exit(EXIT_FAILURE);
1158         }
1159
1160         for (i = 0; i < nfields; i++)
1161                 headers[i] = mbvalidate(PQfname(result, i), opt->topt.encoding);
1162
1163         /* set cells */
1164         ncells = PQntuples(result) * nfields;
1165         cells = calloc(ncells + 1, sizeof(*cells));
1166         if (!cells)
1167         {
1168                 fprintf(stderr, gettext("out of memory\n"));
1169                 exit(EXIT_FAILURE);
1170         }
1171
1172         for (i = 0; i < ncells; i++)
1173         {
1174                 if (PQgetisnull(result, i / nfields, i % nfields))
1175                         cells[i] = opt->nullPrint ? opt->nullPrint : "";
1176                 else
1177                         cells[i] = mbvalidate(PQgetvalue(result, i / nfields, i % nfields), opt->topt.encoding);
1178         }
1179
1180         /* set footers */
1181
1182         if (opt->footers)
1183                 footers = opt->footers;
1184         else if (!opt->topt.expanded && opt->default_footer)
1185         {
1186                 footers = calloc(2, sizeof(*footers));
1187                 if (!footers)
1188                 {
1189                         fprintf(stderr, gettext("out of memory\n"));
1190                         exit(EXIT_FAILURE);
1191                 }
1192
1193                 footers[0] = malloc(100);
1194                 if (!footers[0])
1195                 {
1196                         fprintf(stderr, gettext("out of memory\n"));
1197                         exit(EXIT_FAILURE);
1198                 }
1199                 if (PQntuples(result) == 1)
1200                         snprintf(footers[0], 100, gettext("(1 row)"));
1201                 else
1202                         snprintf(footers[0], 100, gettext("(%d rows)"), PQntuples(result));
1203         }
1204         else
1205                 footers = NULL;
1206
1207         /* set alignment */
1208         align = calloc(nfields + 1, sizeof(*align));
1209         if (!align)
1210         {
1211                 fprintf(stderr, gettext("out of memory\n"));
1212                 exit(EXIT_FAILURE);
1213         }
1214
1215         for (i = 0; i < nfields; i++)
1216         {
1217                 Oid                     ftype = PQftype(result, i);
1218
1219                 if (ftype == 20 ||              /* int8 */
1220                         ftype == 21 ||          /* int2 */
1221                         ftype == 23 ||          /* int4 */
1222                         (ftype >= 26 && ftype <= 30) ||         /* ?id */
1223                         ftype == 700 ||         /* float4 */
1224                         ftype == 701 ||         /* float8 */
1225                         ftype == 790 ||         /* money */
1226                         ftype == 1700           /* numeric */
1227                         )
1228                         align[i] = 'r';
1229                 else
1230                         align[i] = 'l';
1231         }
1232
1233         /* call table printer */
1234         printTable(opt->title, headers, cells,
1235                            (const char *const *) footers,
1236                            align, &opt->topt, fout);
1237
1238         free(headers);
1239         free(cells);
1240         if (footers)
1241         {
1242                 free(footers[0]);
1243                 free(footers);
1244         }
1245         free(align);
1246 }
1247
1248
1249 /* the end */