mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-25 18:57:20 +02:00
Use --enable-nls to turn it on; see installation instructions for details. See developer's guide how to make use of it in programs and how to add translations. psql sources have been almost fully prepared and an incomplete German translation has been provided. In the backend, only elog() calls are currently translatable, and the provided German translation file is more of a placeholder.
1124 lines
22 KiB
C
1124 lines
22 KiB
C
/*
|
|
* psql - the PostgreSQL interactive terminal
|
|
*
|
|
* Copyright 2000 by PostgreSQL Global Development Group
|
|
*
|
|
* $Header: /cvsroot/pgsql/src/bin/psql/print.c,v 1.20 2001/06/02 18:25:18 petere Exp $
|
|
*/
|
|
#include "postgres_fe.h"
|
|
#include "print.h"
|
|
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
#ifndef WIN32
|
|
#include <unistd.h> /* for isatty() */
|
|
#include <sys/ioctl.h> /* for ioctl() */
|
|
#else
|
|
#define popen(x,y) _popen(x,y)
|
|
#define pclose(x) _pclose(x)
|
|
#endif
|
|
|
|
#include "pqsignal.h"
|
|
#include "libpq-fe.h"
|
|
|
|
#include "settings.h"
|
|
|
|
#ifndef __CYGWIN__
|
|
#define DEFAULT_PAGER "more"
|
|
#else
|
|
#define DEFAULT_PAGER "less"
|
|
#endif
|
|
|
|
|
|
|
|
/*************************/
|
|
/* Unaligned text */
|
|
/*************************/
|
|
|
|
|
|
static void
|
|
print_unaligned_text(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
const char *const * ptr;
|
|
bool need_recordsep = false;
|
|
|
|
if (!opt_fieldsep)
|
|
opt_fieldsep = "";
|
|
if (!opt_recordsep)
|
|
opt_recordsep = "";
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
fprintf(fout, "%s%s", title, opt_recordsep);
|
|
|
|
/* print headers and count columns */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
{
|
|
col_count++;
|
|
if (!opt_barebones)
|
|
{
|
|
if (col_count > 1)
|
|
fputs(opt_fieldsep, fout);
|
|
fputs(*ptr, fout);
|
|
}
|
|
}
|
|
if (!opt_barebones)
|
|
need_recordsep = true;
|
|
|
|
/* print cells */
|
|
i = 0;
|
|
for (ptr = cells; *ptr; ptr++)
|
|
{
|
|
if (need_recordsep)
|
|
{
|
|
fputs(opt_recordsep, fout);
|
|
need_recordsep = false;
|
|
}
|
|
fputs(*ptr, fout);
|
|
if ((i + 1) % col_count)
|
|
fputs(opt_fieldsep, fout);
|
|
else
|
|
need_recordsep = true;
|
|
i++;
|
|
}
|
|
|
|
/* print footers */
|
|
|
|
if (!opt_barebones && footers)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
if (need_recordsep)
|
|
{
|
|
fputs(opt_recordsep, fout);
|
|
need_recordsep = false;
|
|
}
|
|
fputs(*ptr, fout);
|
|
need_recordsep = true;
|
|
}
|
|
|
|
/* the last record needs to be concluded with a newline */
|
|
if (need_recordsep)
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_unaligned_vertical(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_fieldsep, const char *opt_recordsep, bool opt_barebones,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
const char *const * ptr;
|
|
|
|
if (!opt_fieldsep)
|
|
opt_fieldsep = "";
|
|
if (!opt_recordsep)
|
|
opt_recordsep = "";
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
fputs(title, fout);
|
|
|
|
/* count columns */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
col_count++;
|
|
|
|
/* print records */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
if (i != 0 || (!opt_barebones && title))
|
|
{
|
|
fputs(opt_recordsep, fout);
|
|
if (i % col_count == 0)
|
|
fputs(opt_recordsep, fout); /* another one */
|
|
}
|
|
|
|
fputs(headers[i % col_count], fout);
|
|
fputs(opt_fieldsep, fout);
|
|
fputs(*ptr, fout);
|
|
}
|
|
|
|
/* print footers */
|
|
if (!opt_barebones && footers && *footers)
|
|
{
|
|
fputs(opt_recordsep, fout);
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
fputs(opt_recordsep, fout);
|
|
fputs(*ptr, fout);
|
|
}
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
/********************/
|
|
/* Aligned text */
|
|
/********************/
|
|
|
|
|
|
/* draw "line" */
|
|
static void
|
|
_print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout)
|
|
{
|
|
unsigned int i,
|
|
j;
|
|
|
|
if (border == 1)
|
|
fputc('-', fout);
|
|
else if (border == 2)
|
|
fputs("+-", fout);
|
|
|
|
for (i = 0; i < col_count; i++)
|
|
{
|
|
for (j = 0; j < widths[i]; j++)
|
|
fputc('-', fout);
|
|
|
|
if (i < col_count - 1)
|
|
{
|
|
if (border == 0)
|
|
fputc(' ', fout);
|
|
else
|
|
fputs("-+-", fout);
|
|
}
|
|
}
|
|
|
|
if (border == 2)
|
|
fputs("-+", fout);
|
|
else if (border == 1)
|
|
fputc('-', fout);
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_aligned_text(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i,
|
|
tmp;
|
|
unsigned int *widths,
|
|
total_w;
|
|
const char *const * ptr;
|
|
|
|
/* count columns */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
col_count++;
|
|
|
|
widths = calloc(col_count, sizeof(*widths));
|
|
if (!widths)
|
|
{
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* calc column widths */
|
|
for (i = 0; i < col_count; i++)
|
|
if ((tmp = strlen(headers[i])) > widths[i])
|
|
widths[i] = tmp; /* don't wanna call strlen twice */
|
|
|
|
for (i = 0, ptr = cells; *ptr; ptr++, i++)
|
|
if ((tmp = strlen(*ptr)) > widths[i % col_count])
|
|
widths[i % col_count] = tmp;
|
|
|
|
if (opt_border == 0)
|
|
total_w = col_count - 1;
|
|
else if (opt_border == 1)
|
|
total_w = col_count * 3 - 1;
|
|
else
|
|
total_w = col_count * 3 + 1;
|
|
|
|
for (i = 0; i < col_count; i++)
|
|
total_w += widths[i];
|
|
|
|
/* print title */
|
|
if (title && !opt_barebones)
|
|
{
|
|
if (strlen(title) >= total_w)
|
|
fprintf(fout, "%s\n", title);
|
|
else
|
|
fprintf(fout, "%-*s%s\n", (int) (total_w - strlen(title)) / 2, "", title);
|
|
}
|
|
|
|
/* print headers */
|
|
if (!opt_barebones)
|
|
{
|
|
if (opt_border == 2)
|
|
_print_horizontal_line(col_count, widths, opt_border, fout);
|
|
|
|
if (opt_border == 2)
|
|
fputs("| ", fout);
|
|
else if (opt_border == 1)
|
|
fputc(' ', fout);
|
|
|
|
for (i = 0; i < col_count; i++)
|
|
{
|
|
/* centered */
|
|
fprintf(fout, "%-*s%s%-*s",
|
|
(int) floor((widths[i] - strlen(headers[i])) / 2.0), "",
|
|
headers[i], (int) ceil((widths[i] - strlen(headers[i])) / 2.0), "");
|
|
|
|
if (i < col_count - 1)
|
|
{
|
|
if (opt_border == 0)
|
|
fputc(' ', fout);
|
|
else
|
|
fputs(" | ", fout);
|
|
}
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
fputs(" |", fout);
|
|
else if (opt_border == 1)
|
|
fputc(' ', fout);;
|
|
fputc('\n', fout);
|
|
|
|
_print_horizontal_line(col_count, widths, opt_border, fout);
|
|
}
|
|
|
|
/* print cells */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
/* beginning of line */
|
|
if (i % col_count == 0)
|
|
{
|
|
if (opt_border == 2)
|
|
fputs("| ", fout);
|
|
else if (opt_border == 1)
|
|
fputc(' ', fout);
|
|
}
|
|
|
|
/* content */
|
|
if (opt_align[(i) % col_count] == 'r')
|
|
fprintf(fout, "%*s", widths[i % col_count], cells[i]);
|
|
else
|
|
{
|
|
if ((i + 1) % col_count == 0 && opt_border != 2)
|
|
fputs(cells[i], fout);
|
|
else
|
|
fprintf(fout, "%-*s", widths[i % col_count], cells[i]);
|
|
}
|
|
|
|
/* divider */
|
|
if ((i + 1) % col_count)
|
|
{
|
|
if (opt_border == 0)
|
|
fputc(' ', fout);
|
|
else
|
|
fputs(" | ", fout);
|
|
}
|
|
/* end of line */
|
|
else
|
|
{
|
|
if (opt_border == 2)
|
|
fputs(" |", fout);
|
|
fputc('\n', fout);
|
|
}
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
_print_horizontal_line(col_count, widths, opt_border, fout);
|
|
|
|
/* print footers */
|
|
if (footers && !opt_barebones)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
fprintf(fout, "%s\n", *ptr);
|
|
|
|
fputc('\n', fout);
|
|
|
|
/* clean up */
|
|
free(widths);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_aligned_vertical(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
bool opt_barebones, unsigned short int opt_border,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int record = 1;
|
|
const char *const * ptr;
|
|
unsigned int i,
|
|
tmp,
|
|
hwidth = 0,
|
|
dwidth = 0;
|
|
char *divider;
|
|
|
|
if (cells[0] == NULL)
|
|
{
|
|
puts(gettext("(No rows)\n"));
|
|
return;
|
|
}
|
|
|
|
/* count columns and find longest header */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
{
|
|
col_count++;
|
|
if ((tmp = strlen(*ptr)) > hwidth)
|
|
hwidth = tmp; /* don't wanna call strlen twice */
|
|
}
|
|
|
|
/* find longest data cell */
|
|
for (ptr = cells; *ptr; ptr++)
|
|
if ((tmp = strlen(*ptr)) > dwidth)
|
|
dwidth = tmp;
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
fprintf(fout, "%s\n", title);
|
|
|
|
/* make horizontal border */
|
|
divider = malloc(hwidth + dwidth + 10);
|
|
if (!divider)
|
|
{
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
divider[0] = '\0';
|
|
if (opt_border == 2)
|
|
strcat(divider, "+-");
|
|
for (i = 0; i < hwidth; i++)
|
|
strcat(divider, opt_border > 0 ? "-" : " ");
|
|
if (opt_border > 0)
|
|
strcat(divider, "-+-");
|
|
else
|
|
strcat(divider, " ");
|
|
for (i = 0; i < dwidth; i++)
|
|
strcat(divider, opt_border > 0 ? "-" : " ");
|
|
if (opt_border == 2)
|
|
strcat(divider, "-+");
|
|
|
|
|
|
/* print records */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
if (i % col_count == 0)
|
|
{
|
|
if (!opt_barebones)
|
|
{
|
|
char *record_str = malloc(32);
|
|
size_t record_str_len;
|
|
|
|
if (!record_str)
|
|
{
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (opt_border == 0)
|
|
sprintf(record_str, "* Record %d", record++);
|
|
else
|
|
sprintf(record_str, "[ RECORD %d ]", record++);
|
|
record_str_len = strlen(record_str);
|
|
|
|
if (record_str_len + opt_border > strlen(divider))
|
|
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
|
|
else
|
|
{
|
|
char *div_copy = strdup(divider);
|
|
|
|
if (!div_copy)
|
|
{
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strncpy(div_copy + opt_border, record_str, record_str_len);
|
|
fprintf(fout, "%s\n", div_copy);
|
|
free(div_copy);
|
|
}
|
|
free(record_str);
|
|
}
|
|
else if (i != 0 || opt_border == 2)
|
|
fprintf(fout, "%s\n", divider);
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
fputs("| ", fout);
|
|
fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
|
|
|
|
if (opt_border > 0)
|
|
fputs(" | ", fout);
|
|
else
|
|
fputs(" ", fout);
|
|
|
|
if (opt_border < 2)
|
|
fprintf(fout, "%s\n", *ptr);
|
|
else
|
|
fprintf(fout, "%-*s |\n", dwidth, *ptr);
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
fprintf(fout, "%s\n", divider);
|
|
|
|
|
|
/* print footers */
|
|
|
|
if (!opt_barebones && footers && *footers)
|
|
{
|
|
if (opt_border < 2)
|
|
fputc('\n', fout);
|
|
for (ptr = footers; *ptr; ptr++)
|
|
fprintf(fout, "%s\n", *ptr);
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
free(divider);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**********************/
|
|
/* HTML printing ******/
|
|
/**********************/
|
|
|
|
|
|
static void
|
|
html_escaped_print(const char *in, FILE *fout)
|
|
{
|
|
const char *p;
|
|
|
|
for (p = in; *p; p++)
|
|
switch (*p)
|
|
{
|
|
case '&':
|
|
fputs("&", fout);
|
|
break;
|
|
case '<':
|
|
fputs("<", fout);
|
|
break;
|
|
case '>':
|
|
fputs(">", fout);
|
|
break;
|
|
case '\n':
|
|
fputs("<br>", fout);
|
|
break;
|
|
default:
|
|
fputc(*p, fout);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_html_text(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
|
const char *opt_table_attr,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
const char *const * ptr;
|
|
|
|
fprintf(fout, "<table border=%d", opt_border);
|
|
if (opt_table_attr)
|
|
fprintf(fout, " %s", opt_table_attr);
|
|
fputs(">\n", fout);
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
{
|
|
fputs(" <caption>", fout);
|
|
html_escaped_print(title, fout);
|
|
fputs("</caption>\n", fout);
|
|
}
|
|
|
|
/* print headers and count columns */
|
|
if (!opt_barebones)
|
|
fputs(" <tr>\n", fout);
|
|
for (i = 0, ptr = headers; *ptr; i++, ptr++)
|
|
{
|
|
col_count++;
|
|
if (!opt_barebones)
|
|
{
|
|
fputs(" <th align=center>", fout);
|
|
html_escaped_print(*ptr, fout);
|
|
fputs("</th>\n", fout);
|
|
}
|
|
}
|
|
if (!opt_barebones)
|
|
fputs(" </tr>\n", fout);
|
|
|
|
/* print cells */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
if (i % col_count == 0)
|
|
fputs(" <tr valign=top>\n", fout);
|
|
|
|
fprintf(fout, " <td align=%s>", opt_align[(i) % col_count] == 'r' ? "right" : "left");
|
|
if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only
|
|
* whitespace? */
|
|
fputs(" ", fout);
|
|
else
|
|
html_escaped_print(*ptr, fout);
|
|
fputs("</td>\n", fout);
|
|
|
|
if ((i + 1) % col_count == 0)
|
|
fputs(" </tr>\n", fout);
|
|
}
|
|
|
|
fputs("</table>\n", fout);
|
|
|
|
/* print footers */
|
|
|
|
if (footers && !opt_barebones)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
html_escaped_print(*ptr, fout);
|
|
fputs("<br>\n", fout);
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_html_vertical(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
|
const char *opt_table_attr,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
unsigned int record = 1;
|
|
const char *const * ptr;
|
|
|
|
fprintf(fout, "<table border=%d", opt_border);
|
|
if (opt_table_attr)
|
|
fprintf(fout, " %s", opt_table_attr);
|
|
fputs(">\n", fout);
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
{
|
|
fputs(" <caption>", fout);
|
|
html_escaped_print(title, fout);
|
|
fputs("</caption>\n", fout);
|
|
}
|
|
|
|
/* count columns */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
col_count++;
|
|
|
|
/* print records */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
if (i % col_count == 0)
|
|
{
|
|
if (!opt_barebones)
|
|
fprintf(fout, "\n <tr><td colspan=2 align=center>Record %d</td></tr>\n", record++);
|
|
else
|
|
fputs("\n <tr><td colspan=2> </td></tr>\n", fout);
|
|
}
|
|
fputs(" <tr valign=top>\n"
|
|
" <th>", fout);
|
|
html_escaped_print(headers[i % col_count], fout);
|
|
fputs("</th>\n", fout);
|
|
|
|
fprintf(fout, " <td align=%s>", opt_align[i % col_count] == 'r' ? "right" : "left");
|
|
if ((*ptr)[strspn(*ptr, " \t")] == '\0') /* is string only
|
|
* whitespace? */
|
|
fputs(" ", fout);
|
|
else
|
|
html_escaped_print(*ptr, fout);
|
|
fputs("</td>\n </tr>\n", fout);
|
|
}
|
|
|
|
fputs("</table>\n", fout);
|
|
|
|
/* print footers */
|
|
if (footers && !opt_barebones)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
html_escaped_print(*ptr, fout);
|
|
fputs("<br>\n", fout);
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
/*************************/
|
|
/* LaTeX */
|
|
/*************************/
|
|
|
|
|
|
static void
|
|
latex_escaped_print(const char *in, FILE *fout)
|
|
{
|
|
const char *p;
|
|
|
|
for (p = in; *p; p++)
|
|
switch (*p)
|
|
{
|
|
case '&':
|
|
fputs("\\&", fout);
|
|
break;
|
|
case '%':
|
|
fputs("\\%", fout);
|
|
break;
|
|
case '$':
|
|
fputs("\\$", fout);
|
|
break;
|
|
case '{':
|
|
fputs("\\{", fout);
|
|
break;
|
|
case '}':
|
|
fputs("\\}", fout);
|
|
break;
|
|
case '\\':
|
|
fputs("\\backslash", fout);
|
|
break;
|
|
case '\n':
|
|
fputs("\\\\", fout);
|
|
break;
|
|
default:
|
|
fputc(*p, fout);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_latex_text(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
const char *cp;
|
|
const char *const * ptr;
|
|
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
{
|
|
fputs("\\begin{center}\n", fout);
|
|
latex_escaped_print(title, fout);
|
|
fputs("\n\\end{center}\n\n", fout);
|
|
}
|
|
|
|
/* begin environment and set alignments and borders */
|
|
fputs("\\begin{tabular}{", fout);
|
|
if (opt_border == 0)
|
|
fputs(opt_align, fout);
|
|
else if (opt_border == 1)
|
|
{
|
|
for (cp = opt_align; *cp; cp++)
|
|
{
|
|
if (cp != opt_align)
|
|
fputc('|', fout);
|
|
fputc(*cp, fout);
|
|
}
|
|
}
|
|
else if (opt_border == 2)
|
|
{
|
|
for (cp = opt_align; *cp; cp++)
|
|
{
|
|
fputc('|', fout);
|
|
fputc(*cp, fout);
|
|
}
|
|
fputc('|', fout);
|
|
}
|
|
fputs("}\n", fout);
|
|
|
|
if (!opt_barebones && opt_border == 2)
|
|
fputs("\\hline\n", fout);
|
|
|
|
/* print headers and count columns */
|
|
for (i = 0, ptr = headers; *ptr; i++, ptr++)
|
|
{
|
|
col_count++;
|
|
if (!opt_barebones)
|
|
{
|
|
if (i != 0)
|
|
fputs(" & ", fout);
|
|
latex_escaped_print(*ptr, fout);
|
|
}
|
|
}
|
|
|
|
if (!opt_barebones)
|
|
{
|
|
fputs(" \\\\\n", fout);
|
|
fputs("\\hline\n", fout);
|
|
}
|
|
|
|
/* print cells */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
latex_escaped_print(*ptr, fout);
|
|
|
|
if ((i + 1) % col_count == 0)
|
|
fputs(" \\\\\n", fout);
|
|
else
|
|
fputs(" & ", fout);
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
fputs("\\hline\n", fout);
|
|
|
|
fputs("\\end{tabular}\n\n", fout);
|
|
|
|
|
|
/* print footers */
|
|
|
|
if (footers && !opt_barebones)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
latex_escaped_print(*ptr, fout);
|
|
fputs(" \\\\\n", fout);
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
print_latex_vertical(const char *title, const char *const * headers,
|
|
const char *const * cells, const char *const * footers,
|
|
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
|
FILE *fout)
|
|
{
|
|
unsigned int col_count = 0;
|
|
unsigned int i;
|
|
const char *const * ptr;
|
|
unsigned int record = 1;
|
|
|
|
(void) opt_align; /* currently unused parameter */
|
|
|
|
/* print title */
|
|
if (!opt_barebones && title)
|
|
{
|
|
fputs("\\begin{center}\n", fout);
|
|
latex_escaped_print(title, fout);
|
|
fputs("\n\\end{center}\n\n", fout);
|
|
}
|
|
|
|
/* begin environment and set alignments and borders */
|
|
fputs("\\begin{tabular}{", fout);
|
|
if (opt_border == 0)
|
|
fputs("cl", fout);
|
|
else if (opt_border == 1)
|
|
fputs("c|l", fout);
|
|
else if (opt_border == 2)
|
|
fputs("|c|l|", fout);
|
|
fputs("}\n", fout);
|
|
|
|
|
|
/* count columns */
|
|
for (ptr = headers; *ptr; ptr++)
|
|
col_count++;
|
|
|
|
|
|
/* print records */
|
|
for (i = 0, ptr = cells; *ptr; i++, ptr++)
|
|
{
|
|
/* new record */
|
|
if (i % col_count == 0)
|
|
{
|
|
if (!opt_barebones)
|
|
{
|
|
if (opt_border == 2)
|
|
fputs("\\hline\n", fout);
|
|
fprintf(fout, "\\multicolumn{2}{c}{Record %d} \\\\\n", record++);
|
|
}
|
|
if (opt_border >= 1)
|
|
fputs("\\hline\n", fout);
|
|
}
|
|
|
|
latex_escaped_print(headers[i % col_count], fout);
|
|
fputs(" & ", fout);
|
|
latex_escaped_print(*ptr, fout);
|
|
fputs(" \\\\\n", fout);
|
|
}
|
|
|
|
if (opt_border == 2)
|
|
fputs("\\hline\n", fout);
|
|
|
|
fputs("\\end{tabular}\n\n", fout);
|
|
|
|
|
|
/* print footers */
|
|
|
|
if (footers && !opt_barebones)
|
|
for (ptr = footers; *ptr; ptr++)
|
|
{
|
|
latex_escaped_print(*ptr, fout);
|
|
fputs(" \\\\\n", fout);
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/********************************/
|
|
/* Public functions */
|
|
/********************************/
|
|
|
|
|
|
void
|
|
printTable(const char *title,
|
|
const char *const * headers,
|
|
const char *const * cells,
|
|
const char *const * footers,
|
|
const char *align,
|
|
const printTableOpt *opt, FILE *fout)
|
|
{
|
|
const char *default_footer[] = {NULL};
|
|
unsigned short int border = opt->border;
|
|
FILE *pager = NULL,
|
|
*output;
|
|
|
|
|
|
if (opt->format == PRINT_NOTHING)
|
|
return;
|
|
|
|
if (!footers)
|
|
footers = default_footer;
|
|
|
|
if (opt->format != PRINT_HTML && border > 2)
|
|
border = 2;
|
|
|
|
|
|
/* check whether we need / can / are supposed to use pager */
|
|
if (fout == stdout && opt->pager
|
|
#ifndef WIN32
|
|
&&
|
|
isatty(fileno(stdin)) &&
|
|
isatty(fileno(stdout))
|
|
#endif
|
|
)
|
|
{
|
|
const char *pagerprog;
|
|
|
|
#ifdef TIOCGWINSZ
|
|
unsigned int col_count = 0,
|
|
row_count = 0,
|
|
lines;
|
|
const char *const * ptr;
|
|
int result;
|
|
struct winsize screen_size;
|
|
|
|
/* rough estimate of columns and rows */
|
|
if (headers)
|
|
for (ptr = headers; *ptr; ptr++)
|
|
col_count++;
|
|
if (cells)
|
|
for (ptr = cells; *ptr; ptr++)
|
|
row_count++;
|
|
row_count /= col_count;
|
|
|
|
if (opt->expanded)
|
|
lines = (col_count + 1) * row_count;
|
|
else
|
|
lines = row_count + 1;
|
|
if (!opt->tuples_only)
|
|
lines += 5;
|
|
|
|
result = ioctl(fileno(stdout), TIOCGWINSZ, &screen_size);
|
|
if (result == -1 || lines > screen_size.ws_row)
|
|
{
|
|
#endif
|
|
pagerprog = getenv("PAGER");
|
|
if (!pagerprog)
|
|
pagerprog = DEFAULT_PAGER;
|
|
pager = popen(pagerprog, "w");
|
|
#ifdef TIOCGWINSZ
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pager)
|
|
{
|
|
output = pager;
|
|
#ifndef WIN32
|
|
pqsignal(SIGPIPE, SIG_IGN);
|
|
#endif
|
|
}
|
|
else
|
|
output = fout;
|
|
|
|
|
|
/* print the stuff */
|
|
|
|
switch (opt->format)
|
|
{
|
|
case PRINT_UNALIGNED:
|
|
if (opt->expanded)
|
|
print_unaligned_vertical(title, headers, cells, footers, opt->fieldSep, opt->recordSep, opt->tuples_only, output);
|
|
else
|
|
print_unaligned_text(title, headers, cells, footers, opt->fieldSep, opt->recordSep, opt->tuples_only, output);
|
|
break;
|
|
case PRINT_ALIGNED:
|
|
if (opt->expanded)
|
|
print_aligned_vertical(title, headers, cells, footers, opt->tuples_only, border, output);
|
|
else
|
|
print_aligned_text(title, headers, cells, footers, align, opt->tuples_only, border, output);
|
|
break;
|
|
case PRINT_HTML:
|
|
if (opt->expanded)
|
|
print_html_vertical(title, headers, cells, footers, align, opt->tuples_only, border, opt->tableAttr, output);
|
|
else
|
|
print_html_text(title, headers, cells, footers, align, opt->tuples_only, border, opt->tableAttr, output);
|
|
break;
|
|
case PRINT_LATEX:
|
|
if (opt->expanded)
|
|
print_latex_vertical(title, headers, cells, footers, align, opt->tuples_only, border, output);
|
|
else
|
|
print_latex_text(title, headers, cells, footers, align, opt->tuples_only, border, output);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "+ Oops, you shouldn't see this!\n");
|
|
}
|
|
|
|
if (pager)
|
|
{
|
|
pclose(pager);
|
|
#ifndef WIN32
|
|
pqsignal(SIGPIPE, SIG_DFL);
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout)
|
|
{
|
|
int nfields;
|
|
const char **headers;
|
|
const char **cells;
|
|
char **footers;
|
|
char *align;
|
|
int i;
|
|
|
|
/* extract headers */
|
|
|
|
nfields = PQnfields(result);
|
|
|
|
headers = calloc(nfields + 1, sizeof(*headers));
|
|
if (!headers)
|
|
{
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < nfields; i++)
|
|
headers[i] = PQfname(result, i);
|
|
|
|
/* set cells */
|
|
|
|
cells = calloc(nfields * PQntuples(result) + 1, sizeof(*cells));
|
|
if (!cells)
|
|
{
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < nfields * PQntuples(result); i++)
|
|
{
|
|
if (PQgetisnull(result, i / nfields, i % nfields))
|
|
cells[i] = opt->nullPrint ? opt->nullPrint : "";
|
|
else
|
|
cells[i] = PQgetvalue(result, i / nfields, i % nfields);
|
|
}
|
|
|
|
/* set footers */
|
|
|
|
if (opt->footers)
|
|
footers = opt->footers;
|
|
else if (!opt->topt.expanded && opt->default_footer)
|
|
{
|
|
footers = calloc(2, sizeof(*footers));
|
|
if (!footers)
|
|
{
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
footers[0] = malloc(100);
|
|
if (PQntuples(result) == 1)
|
|
snprintf(footers[0], 100, gettext("(1 row)"));
|
|
else
|
|
snprintf(footers[0], 100, gettext("(%d rows)"), PQntuples(result));
|
|
}
|
|
else
|
|
footers = NULL;
|
|
|
|
/* set alignment */
|
|
|
|
align = calloc(nfields + 1, sizeof(*align));
|
|
if (!align)
|
|
{
|
|
perror("calloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < nfields; i++)
|
|
{
|
|
Oid ftype = PQftype(result, i);
|
|
|
|
if (ftype == 20 || /* int8 */
|
|
ftype == 21 || /* int2 */
|
|
ftype == 23 || /* int4 */
|
|
(ftype >= 26 && ftype <= 30) || /* ?id */
|
|
ftype == 700 || /* float4 */
|
|
ftype == 701 || /* float8 */
|
|
ftype == 790 || /* money */
|
|
ftype == 1700 /* numeric */
|
|
)
|
|
align[i] = 'r';
|
|
else
|
|
align[i] = 'l';
|
|
}
|
|
|
|
/* call table printer */
|
|
|
|
printTable(opt->title, headers, cells,
|
|
footers ? (const char *const *) footers : (const char *const *) (opt->footers),
|
|
align, &opt->topt, fout);
|
|
|
|
free(headers);
|
|
free(cells);
|
|
if (footers)
|
|
{
|
|
free(footers[0]);
|
|
free(footers);
|
|
}
|
|
free(align);
|
|
}
|
|
|
|
|
|
/* the end */
|