Commit 1e9199e8 authored by Alvaro Herrera's avatar Alvaro Herrera

Improve psql's internal print.c code by introducing an actual print API.

Provides for better code readability, but mainly this is infrastructure changes
to allow further changes such as arbitrary footers on printed tables.  Also,
the translation status of each element in the table is more easily customized.

Brendan Jurd, with some editorialization by me.
parent 9340c637
...@@ -3,36 +3,27 @@ ...@@ -3,36 +3,27 @@
* *
* Copyright (c) 2000-2008, PostgreSQL Global Development Group * Copyright (c) 2000-2008, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.170 2008/05/05 01:21:03 adunstan Exp $ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.171 2008/05/12 22:59:58 alvherre Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "describe.h"
#include "dumputils.h" #include <ctype.h>
#include "common.h" #include "common.h"
#include "settings.h" #include "describe.h"
#include "dumputils.h"
#include "mbprint.h"
#include "print.h" #include "print.h"
#include "settings.h"
#include "variables.h" #include "variables.h"
#include <ctype.h>
#ifdef WIN32
/*
* mbvalidate() is used in function describeOneTableDetails() to make sure
* all characters of the cells will be printed to the DOS console in a
* correct way
*/
#include "mbprint.h"
#endif
static bool describeOneTableDetails(const char *schemaname, static bool describeOneTableDetails(const char *schemaname,
const char *relationname, const char *relationname,
const char *oid, const char *oid,
bool verbose); bool verbose);
static bool add_tablespace_footer(char relkind, Oid tablespace, char **footers, static void add_tablespace_footer(printTableContent *const cont, char relkind,
int *count, PQExpBufferData buf, bool newline); Oid tablespace, const bool newline);
static bool listTSParsersVerbose(const char *pattern); static bool listTSParsersVerbose(const char *pattern);
static bool describeOneTSParser(const char *oid, const char *nspname, static bool describeOneTSParser(const char *oid, const char *nspname,
const char *prsname); const char *prsname);
...@@ -789,11 +780,11 @@ describeOneTableDetails(const char *schemaname, ...@@ -789,11 +780,11 @@ describeOneTableDetails(const char *schemaname,
PQExpBufferData buf; PQExpBufferData buf;
PGresult *res = NULL; PGresult *res = NULL;
printTableOpt myopt = pset.popt.topt; printTableOpt myopt = pset.popt.topt;
printTableContent cont;
int i; int i;
char *view_def = NULL; char *view_def = NULL;
const char *headers[5]; char *headers[4];
char **cells = NULL; char **modifiers = NULL;
char **footers = NULL;
char **ptr; char **ptr;
PQExpBufferData title; PQExpBufferData title;
PQExpBufferData tmpbuf; PQExpBufferData tmpbuf;
...@@ -852,25 +843,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -852,25 +843,6 @@ describeOneTableDetails(const char *schemaname,
atooid(PQgetvalue(res, 0, 6)) : 0; atooid(PQgetvalue(res, 0, 6)) : 0;
PQclear(res); PQclear(res);
headers[0] = _("Column");
headers[1] = _("Type");
cols = 2;
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
{
show_modifiers = true;
cols++;
headers[cols - 1] = _("Modifiers");
}
if (verbose)
{
cols++;
headers[cols - 1] = _("Description");
}
headers[cols] = NULL;
/* Get column info (index requires additional checks) */ /* Get column info (index requires additional checks) */
printfPQExpBuffer(&buf, "SELECT a.attname,"); printfPQExpBuffer(&buf, "SELECT a.attname,");
appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod)," appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
...@@ -893,6 +865,26 @@ describeOneTableDetails(const char *schemaname, ...@@ -893,6 +865,26 @@ describeOneTableDetails(const char *schemaname,
goto error_return; goto error_return;
numrows = PQntuples(res); numrows = PQntuples(res);
/* Set the number of columns, and their names */
cols = 2;
headers[0] = "Column";
headers[1] = "Type";
if (tableinfo.relkind == 'r' || tableinfo.relkind == 'v')
{
show_modifiers = true;
headers[cols++] = "Modifiers";
modifiers = pg_malloc_zero((numrows + 1) * sizeof(*modifiers));
}
if (verbose)
headers[cols++] = "Description";
printTableInit(&cont, &myopt, title.data, cols, numrows);
for (i = 0; i < cols; i++)
printTableAddHeader(&cont, headers[i], true, 'l');
/* Check if table is a view */ /* Check if table is a view */
if (tableinfo.relkind == 'v') if (tableinfo.relkind == 'v')
{ {
...@@ -910,33 +902,20 @@ describeOneTableDetails(const char *schemaname, ...@@ -910,33 +902,20 @@ describeOneTableDetails(const char *schemaname,
} }
/* Generate table cells to be printed */ /* Generate table cells to be printed */
/* note: initialize all cells[] to NULL in case of error exit */
cells = pg_malloc_zero((numrows * cols + 1) * sizeof(*cells));
for (i = 0; i < numrows; i++) for (i = 0; i < numrows; i++)
{ {
/* Name */ /* Column */
#ifdef WIN32 printTableAddCell(&cont, PQgetvalue(res, i, 0), false);
cells[i * cols + 0] = mbvalidate(PQgetvalue(res, i, 0), myopt.encoding);
#else
cells[i * cols + 0] = PQgetvalue(res, i, 0); /* don't free this
* afterwards */
#endif
/* Type */ /* Type */
#ifdef WIN32 printTableAddCell(&cont, PQgetvalue(res, i, 1), false);
cells[i * cols + 1] = mbvalidate(PQgetvalue(res, i, 1), myopt.encoding);
#else
cells[i * cols + 1] = PQgetvalue(res, i, 1); /* don't free this
* either */
#endif
/* Extra: not null and default */ /* Extra: not null and default */
if (show_modifiers) if (show_modifiers)
{ {
resetPQExpBuffer(&tmpbuf); resetPQExpBuffer(&tmpbuf);
if (strcmp(PQgetvalue(res, i, 3), "t") == 0) if (strcmp(PQgetvalue(res, i, 3), "t") == 0)
appendPQExpBufferStr(&tmpbuf, "not null"); appendPQExpBufferStr(&tmpbuf, _("not null"));
/* handle "default" here */ /* handle "default" here */
/* (note: above we cut off the 'default' string at 128) */ /* (note: above we cut off the 'default' string at 128) */
...@@ -944,24 +923,18 @@ describeOneTableDetails(const char *schemaname, ...@@ -944,24 +923,18 @@ describeOneTableDetails(const char *schemaname,
{ {
if (tmpbuf.len > 0) if (tmpbuf.len > 0)
appendPQExpBufferStr(&tmpbuf, " "); appendPQExpBufferStr(&tmpbuf, " ");
appendPQExpBuffer(&tmpbuf, "default %s", /* translator: default values of column definitions */
appendPQExpBuffer(&tmpbuf, _("default %s"),
PQgetvalue(res, i, 2)); PQgetvalue(res, i, 2));
} }
#ifdef WIN32 modifiers[i] = pg_strdup(tmpbuf.data);
cells[i * cols + 2] = pg_strdup(mbvalidate(tmpbuf.data, myopt.encoding)); printTableAddCell(&cont, modifiers[i], false);
#else
cells[i * cols + 2] = pg_strdup(tmpbuf.data);
#endif
} }
/* Description */ /* Description */
if (verbose) if (verbose)
#ifdef WIN32 printTableAddCell(&cont, PQgetvalue(res, i, 5), false);
cells[i * cols + cols - 1] = mbvalidate(PQgetvalue(res, i, 5), myopt.encoding);
#else
cells[i * cols + cols - 1] = PQgetvalue(res, i, 5);
#endif
} }
/* Make title */ /* Make title */
...@@ -997,7 +970,8 @@ describeOneTableDetails(const char *schemaname, ...@@ -997,7 +970,8 @@ describeOneTableDetails(const char *schemaname,
schemaname, relationname); schemaname, relationname);
break; break;
default: default:
printfPQExpBuffer(&title, _("?%c? \"%s.%s\""), /* untranslated unknown relkind */
printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
tableinfo.relkind, schemaname, relationname); tableinfo.relkind, schemaname, relationname);
break; break;
} }
...@@ -1033,7 +1007,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -1033,7 +1007,6 @@ describeOneTableDetails(const char *schemaname,
char *indamname = PQgetvalue(result, 0, 4); char *indamname = PQgetvalue(result, 0, 4);
char *indtable = PQgetvalue(result, 0, 5); char *indtable = PQgetvalue(result, 0, 5);
char *indpred = PQgetvalue(result, 0, 6); char *indpred = PQgetvalue(result, 0, 6);
int count_footers = 0;
if (strcmp(indisprimary, "t") == 0) if (strcmp(indisprimary, "t") == 0)
printfPQExpBuffer(&tmpbuf, _("primary key, ")); printfPQExpBuffer(&tmpbuf, _("primary key, "));
...@@ -1056,11 +1029,9 @@ describeOneTableDetails(const char *schemaname, ...@@ -1056,11 +1029,9 @@ describeOneTableDetails(const char *schemaname,
if (strcmp(indisvalid, "t") != 0) if (strcmp(indisvalid, "t") != 0)
appendPQExpBuffer(&tmpbuf, _(", invalid")); appendPQExpBuffer(&tmpbuf, _(", invalid"));
footers = pg_malloc_zero(4 * sizeof(*footers)); printTableAddFooter(&cont, tmpbuf.data);
footers[count_footers++] = pg_strdup(tmpbuf.data); add_tablespace_footer(&cont, tableinfo.relkind,
add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace, tableinfo.tablespace, true);
footers, &count_footers, tmpbuf, true);
footers[count_footers] = NULL;
} }
...@@ -1069,10 +1040,12 @@ describeOneTableDetails(const char *schemaname, ...@@ -1069,10 +1040,12 @@ describeOneTableDetails(const char *schemaname,
else if (view_def) else if (view_def)
{ {
PGresult *result = NULL; PGresult *result = NULL;
int rule_count = 0;
int count_footers = 0;
/* count rules other than the view rule */ /* Footer information about a view */
printTableAddFooter(&cont, _("View definition:"));
printTableAddFooter(&cont, view_def);
/* print rules */
if (tableinfo.hasrules) if (tableinfo.hasrules)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
...@@ -1083,60 +1056,32 @@ describeOneTableDetails(const char *schemaname, ...@@ -1083,60 +1056,32 @@ describeOneTableDetails(const char *schemaname,
result = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result) if (!result)
goto error_return; goto error_return;
else
rule_count = PQntuples(result);
}
/* Footer information about a view */ if (PQntuples(result) > 0)
footers = pg_malloc_zero((rule_count + 3) * sizeof(*footers));
footers[count_footers] = pg_malloc(64 + strlen(view_def));
snprintf(footers[count_footers], 64 + strlen(view_def),
_("View definition:\n%s"), view_def);
count_footers++;
/* print rules */
if (rule_count > 0)
{
printfPQExpBuffer(&buf, _("Rules:"));
footers[count_footers++] = pg_strdup(buf.data);
for (i = 0; i < rule_count; i++)
{ {
const char *ruledef; printTableAddFooter(&cont, _("Rules:"));
for (i = 0; i < PQntuples(result); i++)
/* Everything after "CREATE RULE" is echoed verbatim */ {
ruledef = PQgetvalue(result, i, 1); const char *ruledef;
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef); /* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
footers[count_footers++] = pg_strdup(buf.data); printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
} }
PQclear(result); PQclear(result);
} }
footers[count_footers] = NULL;
} }
else if (tableinfo.relkind == 'r') else if (tableinfo.relkind == 'r')
{ {
/* Footer information about a table */ /* Footer information about a table */
PGresult *result1 = NULL, PGresult *result = NULL;
*result2 = NULL, int tuples = 0;
*result3 = NULL,
*result4 = NULL, /* print indexes */
*result5 = NULL,
*result6 = NULL,
*result7 = NULL;
int check_count = 0,
index_count = 0,
foreignkey_count = 0,
rule_count = 0,
trigger_count = 0,
referencedby_count = 0,
inherits_count = 0;
int count_footers = 0;
/* count indexes */
if (tableinfo.hasindex) if (tableinfo.hasindex)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
...@@ -1146,14 +1091,57 @@ describeOneTableDetails(const char *schemaname, ...@@ -1146,14 +1091,57 @@ describeOneTableDetails(const char *schemaname,
"WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n" "WHERE c.oid = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
"ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname", "ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname",
oid); oid);
result1 = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result1) if (!result)
goto error_return; goto error_return;
else else
index_count = PQntuples(result1); tuples = PQntuples(result);
if (tuples > 0)
{
printTableAddFooter(&cont, _("Indexes:"));
for (i = 0; i < tuples; i++)
{
const char *indexdef;
const char *usingpos;
/* untranslated index name */
printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0));
/* Label as primary key or unique (but not both) */
appendPQExpBuffer(&buf,
strcmp(PQgetvalue(result, i, 1), "t") == 0
? " PRIMARY KEY," :
(strcmp(PQgetvalue(result, i, 2), "t") == 0
? " UNIQUE,"
: ""));
/* Everything after "USING" is echoed verbatim */
indexdef = PQgetvalue(result, i, 5);
usingpos = strstr(indexdef, " USING ");
if (usingpos)
indexdef = usingpos + 7;
appendPQExpBuffer(&buf, " %s", indexdef);
if (strcmp(PQgetvalue(result, i, 3), "t") == 0)
appendPQExpBuffer(&buf, " CLUSTER");
if (strcmp(PQgetvalue(result, i, 4), "t") != 0)
appendPQExpBuffer(&buf, " INVALID");
printTableAddFooter(&cont, buf.data);
/* Print tablespace of the index on the same line */
add_tablespace_footer(&cont, 'i',
atooid(PQgetvalue(result, i, 6)),
false);
}
}
PQclear(result);
} }
/* count table (and column) check constraints */ /* print table (and column) check constraints */
if (tableinfo.checks) if (tableinfo.checks)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
...@@ -1162,72 +1150,29 @@ describeOneTableDetails(const char *schemaname, ...@@ -1162,72 +1150,29 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_constraint r\n" "FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'c' ORDER BY 1", "WHERE r.conrelid = '%s' AND r.contype = 'c' ORDER BY 1",
oid); oid);
result2 = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result2) if (!result)
{
PQclear(result1);
goto error_return; goto error_return;
}
else else
check_count = PQntuples(result2); tuples = PQntuples(result);
}
/* count rules */ if (tuples > 0)
if (tableinfo.hasrules)
{
if (pset.sversion < 80300)
{ {
printfPQExpBuffer(&buf, printTableAddFooter(&cont, _("Check constraints:"));
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), " for (i = 0; i < tuples; i++)
"'O'::char AS ev_enabled\n" {
"FROM pg_catalog.pg_rewrite r\n" /* untranslated contraint name and def */
"WHERE r.ev_class = '%s' ORDER BY 1", printfPQExpBuffer(&buf, " \"%s\" %s",
oid); PQgetvalue(result, i, 0),
} PQgetvalue(result, i, 1));
else
{
printfPQExpBuffer(&buf,
"SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
"ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' ORDER BY 1",
oid);
}
result3 = PSQLexec(buf.data, false);
if (!result3)
{
PQclear(result1);
PQclear(result2);
goto error_return;
}
else
rule_count = PQntuples(result3);
}
/* count triggers (but ignore foreign-key triggers) */ printTableAddFooter(&cont, buf.data);
if (tableinfo.triggers) }
{
printfPQExpBuffer(&buf,
"SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid), "
"t.tgenabled\n"
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' "
"AND t.tgconstraint = 0\n"
"ORDER BY 1",
oid);
result4 = PSQLexec(buf.data, false);
if (!result4)
{
PQclear(result1);
PQclear(result2);
PQclear(result3);
goto error_return;
} }
else PQclear(result);
trigger_count = PQntuples(result4);
} }
/* count foreign-key constraints (there are none if no triggers) */ /* print foreign-key constraints (there are none if no triggers) */
if (tableinfo.triggers) if (tableinfo.triggers)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
...@@ -1236,20 +1181,29 @@ describeOneTableDetails(const char *schemaname, ...@@ -1236,20 +1181,29 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_constraint r\n" "FROM pg_catalog.pg_constraint r\n"
"WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1", "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1",
oid); oid);
result5 = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result5) if (!result)
{
PQclear(result1);
PQclear(result2);
PQclear(result3);
PQclear(result4);
goto error_return; goto error_return;
}
else else
foreignkey_count = PQntuples(result5); tuples = PQntuples(result);
if (tuples > 0)
{
printTableAddFooter(&cont, _("Foreign-key constraints:"));
for (i = 0; i < tuples; i++)
{
/* untranslated constraint name and def */
printfPQExpBuffer(&buf, " \"%s\" %s",
PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1));
printTableAddFooter(&cont, buf.data);
}
}
PQclear(result);
} }
/* count incoming foreign-key references (none if no triggers) */ /* print incoming foreign-key references (none if no triggers) */
if (tableinfo.triggers) if (tableinfo.triggers)
{ {
printfPQExpBuffer(&buf, printfPQExpBuffer(&buf,
...@@ -1258,303 +1212,247 @@ describeOneTableDetails(const char *schemaname, ...@@ -1258,303 +1212,247 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_constraint c\n" "FROM pg_catalog.pg_constraint c\n"
"WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1", "WHERE c.confrelid = '%s' AND c.contype = 'f' ORDER BY 1",
oid); oid);
result6 = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result6) if (!result)
{
PQclear(result1);
PQclear(result2);
PQclear(result3);
PQclear(result4);
PQclear(result5);
goto error_return; goto error_return;
}
else else
referencedby_count = PQntuples(result6); tuples = PQntuples(result);
}
/* count inherited tables */ if (tuples > 0)
printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
result7 = PSQLexec(buf.data, false);
if (!result7)
{
PQclear(result1);
PQclear(result2);
PQclear(result3);
PQclear(result4);
PQclear(result5);
PQclear(result6);
goto error_return;
}
else
inherits_count = PQntuples(result7);
footers = pg_malloc_zero((index_count + check_count + rule_count + trigger_count + foreignkey_count + referencedby_count + inherits_count + 8 + 1) * sizeof(*footers));
/* print indexes */
if (index_count > 0)
{
printfPQExpBuffer(&buf, _("Indexes:"));
footers[count_footers++] = pg_strdup(buf.data);
for (i = 0; i < index_count; i++)
{ {
const char *indexdef; printTableAddFooter(&cont, _("Referenced by:"));
const char *usingpos; for (i = 0; i < tuples; i++)
PQExpBufferData tmpbuf;
/* Output index name */
printfPQExpBuffer(&buf, _(" \"%s\""),
PQgetvalue(result1, i, 0));
/* Label as primary key or unique (but not both) */
appendPQExpBuffer(&buf,
strcmp(PQgetvalue(result1, i, 1), "t") == 0
? " PRIMARY KEY," :
(strcmp(PQgetvalue(result1, i, 2), "t") == 0
? " UNIQUE,"
: ""));
/* Everything after "USING" is echoed verbatim */
indexdef = PQgetvalue(result1, i, 5);
usingpos = strstr(indexdef, " USING ");
if (usingpos)
indexdef = usingpos + 7;
appendPQExpBuffer(&buf, " %s", indexdef);
if (strcmp(PQgetvalue(result1, i, 3), "t") == 0)
appendPQExpBuffer(&buf, " CLUSTER");
if (strcmp(PQgetvalue(result1, i, 4), "t") != 0)
appendPQExpBuffer(&buf, " INVALID");
/* Print tablespace of the index on the same line */
count_footers += 1;
initPQExpBuffer(&tmpbuf);
if (add_tablespace_footer('i',
atooid(PQgetvalue(result1, i, 6)),
footers, &count_footers, tmpbuf, false))
{ {
appendPQExpBuffer(&buf, ", "); /* translator: the first %s is a FK name, the following are
appendPQExpBuffer(&buf, tmpbuf.data); * a table name and the FK definition */
printfPQExpBuffer(&buf, _(" \"%s\" IN %s %s"),
count_footers -= 2; PQgetvalue(result, i, 0),
PQgetvalue(result, i, 1),
PQgetvalue(result, i, 2));
printTableAddFooter(&cont, buf.data);
} }
else
count_footers -= 1;
termPQExpBuffer(&tmpbuf);
footers[count_footers++] = pg_strdup(buf.data);
}
}
/* print check constraints */
if (check_count > 0)
{
printfPQExpBuffer(&buf, _("Check constraints:"));
footers[count_footers++] = pg_strdup(buf.data);
for (i = 0; i < check_count; i++)
{
printfPQExpBuffer(&buf, _(" \"%s\" %s"),
PQgetvalue(result2, i, 0),
PQgetvalue(result2, i, 1));
footers[count_footers++] = pg_strdup(buf.data);
} }
PQclear(result);
} }
/* print foreign key constraints */ /* print rules */
if (foreignkey_count > 0) if (tableinfo.hasrules)
{ {
printfPQExpBuffer(&buf, _("Foreign-key constraints:")); if (pset.sversion < 80300)
footers[count_footers++] = pg_strdup(buf.data);
for (i = 0; i < foreignkey_count; i++)
{ {
printfPQExpBuffer(&buf, _(" \"%s\" %s"), printfPQExpBuffer(&buf,
PQgetvalue(result5, i, 0), "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
PQgetvalue(result5, i, 1)); "'O'::char AS ev_enabled\n"
"FROM pg_catalog.pg_rewrite r\n"
footers[count_footers++] = pg_strdup(buf.data); "WHERE r.ev_class = '%s' ORDER BY 1",
oid);
} }
} else
/* print incoming foreign-key constraints */
if (referencedby_count > 0)
{
printfPQExpBuffer(&buf, _("Referenced by:"));
footers[count_footers++] = pg_strdup(buf.data);
for (i = 0; i < referencedby_count; i++)
{ {
printfPQExpBuffer(&buf, _(" \"%s\" IN %s %s"), printfPQExpBuffer(&buf,
PQgetvalue(result6, i, 0), "SELECT r.rulename, trim(trailing ';' from pg_catalog.pg_get_ruledef(r.oid, true)), "
PQgetvalue(result6, i, 1), "ev_enabled\n"
PQgetvalue(result6, i, 2)); "FROM pg_catalog.pg_rewrite r\n"
"WHERE r.ev_class = '%s' ORDER BY 1",
footers[count_footers++] = pg_strdup(buf.data); oid);
} }
} result = PSQLexec(buf.data, false);
if (!result)
/* print rules */ goto error_return;
if (rule_count > 0) else
{ tuples = PQntuples(result);
bool have_heading;
int category;
for (category = 0; category < 4; category++) if (tuples > 0)
{ {
have_heading = false; bool have_heading;
int category;
for (i = 0; i < rule_count; i++) for (category = 0; category < 4; category++)
{ {
const char *ruledef; have_heading = false;
bool list_rule = false;
switch (category) for (i = 0; i < tuples; i++)
{ {
case 0: const char *ruledef;
if (*PQgetvalue(result3, i, 2) == 'O') bool list_rule = false;
list_rule = true;
break;
case 1:
if (*PQgetvalue(result3, i, 2) == 'D')
list_rule = true;
break;
case 2:
if (*PQgetvalue(result3, i, 2) == 'A')
list_rule = true;
break;
case 3:
if (*PQgetvalue(result3, i, 2) == 'R')
list_rule = true;
break;
}
if (!list_rule)
continue;
if (!have_heading)
{
switch (category) switch (category)
{ {
case 0: case 0:
printfPQExpBuffer(&buf, _("Rules:")); if (*PQgetvalue(result, i, 2) == 'O')
list_rule = true;
break; break;
case 1: case 1:
printfPQExpBuffer(&buf, _("Disabled rules:")); if (*PQgetvalue(result, i, 2) == 'D')
list_rule = true;
break; break;
case 2: case 2:
printfPQExpBuffer(&buf, _("Rules firing always:")); if (*PQgetvalue(result, i, 2) == 'A')
list_rule = true;
break; break;
case 3: case 3:
printfPQExpBuffer(&buf, _("Rules firing on replica only:")); if (*PQgetvalue(result, i, 2) == 'R')
list_rule = true;
break; break;
} }
footers[count_footers++] = pg_strdup(buf.data); if (!list_rule)
have_heading = true; continue;
}
/* Everything after "CREATE RULE" is echoed verbatim */ if (!have_heading)
ruledef = PQgetvalue(result3, i, 1); {
ruledef += 12; switch (category)
printfPQExpBuffer(&buf, " %s", ruledef); {
footers[count_footers++] = pg_strdup(buf.data); case 0:
printfPQExpBuffer(&buf, _("Rules:"));
break;
case 1:
printfPQExpBuffer(&buf, _("Disabled rules:"));
break;
case 2:
printfPQExpBuffer(&buf, _("Rules firing always:"));
break;
case 3:
printfPQExpBuffer(&buf, _("Rules firing on replica only:"));
break;
}
printTableAddFooter(&cont, buf.data);
have_heading = true;
}
/* Everything after "CREATE RULE" is echoed verbatim */
ruledef = PQgetvalue(result, i, 1);
ruledef += 12;
printfPQExpBuffer(&buf, " %s", ruledef);
printTableAddFooter(&cont, buf.data);
}
} }
} }
PQclear(result);
} }
/* print triggers */ /* print triggers (but ignore foreign-key triggers) */
if (trigger_count > 0) if (tableinfo.triggers)
{ {
bool have_heading; printfPQExpBuffer(&buf,
int category; "SELECT t.tgname, pg_catalog.pg_get_triggerdef(t.oid), "
"t.tgenabled\n"
/* "FROM pg_catalog.pg_trigger t\n"
* split the output into 4 different categories. Enabled triggers, "WHERE t.tgrelid = '%s' "
* disabled triggers and the two special ALWAYS and REPLICA "AND t.tgconstraint = 0\n"
* configurations. "ORDER BY 1",
*/ oid);
for (category = 0; category < 4; category++) result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
if (tuples > 0)
{ {
have_heading = false; bool have_heading;
for (i = 0; i < trigger_count; i++) int category;
/*
* split the output into 4 different categories. Enabled triggers,
* disabled triggers and the two special ALWAYS and REPLICA
* configurations.
*/
for (category = 0; category < 4; category++)
{ {
bool list_trigger; have_heading = false;
const char *tgdef; for (i = 0; i < tuples; i++)
const char *usingpos;
const char *tgenabled;
/* Check if this trigger falls into the current category */
tgenabled = PQgetvalue(result4, i, 2);
list_trigger = false;
switch (category)
{
case 0:
if (*tgenabled == 'O' || *tgenabled == 't')
list_trigger = true;
break;
case 1:
if (*tgenabled == 'D' || *tgenabled == 'f')
list_trigger = true;
break;
case 2:
if (*tgenabled == 'A')
list_trigger = true;
break;
case 3:
if (*tgenabled == 'R')
list_trigger = true;
break;
}
if (list_trigger == false)
continue;
/* Print the category heading once */
if (have_heading == false)
{ {
bool list_trigger;
const char *tgdef;
const char *usingpos;
const char *tgenabled;
/* Check if this trigger falls into the current category */
tgenabled = PQgetvalue(result, i, 2);
list_trigger = false;
switch (category) switch (category)
{ {
case 0: case 0:
printfPQExpBuffer(&buf, _("Triggers:")); if (*tgenabled == 'O' || *tgenabled == 't')
list_trigger = true;
break; break;
case 1: case 1:
printfPQExpBuffer(&buf, _("Disabled triggers:")); if (*tgenabled == 'D' || *tgenabled == 'f')
list_trigger = true;
break; break;
case 2: case 2:
printfPQExpBuffer(&buf, _("Triggers firing always:")); if (*tgenabled == 'A')
list_trigger = true;
break; break;
case 3: case 3:
printfPQExpBuffer(&buf, _("Triggers firing on replica only:")); if (*tgenabled == 'R')
list_trigger = true;
break; break;
}
if (list_trigger == false)
continue;
/* Print the category heading once */
if (have_heading == false)
{
switch (category)
{
case 0:
printfPQExpBuffer(&buf, _("Triggers:"));
break;
case 1:
printfPQExpBuffer(&buf, _("Disabled triggers:"));
break;
case 2:
printfPQExpBuffer(&buf, _("Triggers firing always:"));
break;
case 3:
printfPQExpBuffer(&buf, _("Triggers firing on replica only:"));
break;
}
printTableAddFooter(&cont, buf.data);
have_heading = true;
} }
footers[count_footers++] = pg_strdup(buf.data);
have_heading = true;
}
/* Everything after "TRIGGER" is echoed verbatim */ /* Everything after "TRIGGER" is echoed verbatim */
tgdef = PQgetvalue(result4, i, 1); tgdef = PQgetvalue(result, i, 1);
usingpos = strstr(tgdef, " TRIGGER "); usingpos = strstr(tgdef, " TRIGGER ");
if (usingpos) if (usingpos)
tgdef = usingpos + 9; tgdef = usingpos + 9;
printfPQExpBuffer(&buf, " %s", tgdef); printfPQExpBuffer(&buf, " %s", tgdef);
footers[count_footers++] = pg_strdup(buf.data); printTableAddFooter(&cont, buf.data);
}
} }
} }
PQclear(result);
} }
/* print inherits */ /* print inherited tables */
for (i = 0; i < inherits_count; i++) printfPQExpBuffer(&buf, "SELECT c.oid::pg_catalog.regclass FROM pg_catalog.pg_class c, pg_catalog.pg_inherits i WHERE c.oid=i.inhparent AND i.inhrelid = '%s' ORDER BY inhseqno", oid);
result = PSQLexec(buf.data, false);
if (!result)
goto error_return;
else
tuples = PQntuples(result);
for (i = 0; i < tuples; i++)
{ {
const char *s = _("Inherits"); const char *s = _("Inherits");
if (i == 0) if (i == 0)
printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result7, i, 0)); printfPQExpBuffer(&buf, "%s: %s", s, PQgetvalue(result, i, 0));
else else
printfPQExpBuffer(&buf, "%*s %s", (int) strlen(s), "", PQgetvalue(result7, i, 0)); printfPQExpBuffer(&buf, "%*s %s", (int) strlen(s), "", PQgetvalue(result, i, 0));
if (i < inherits_count - 1) if (i < tuples - 1)
appendPQExpBuffer(&buf, ","); appendPQExpBuffer(&buf, ",");
footers[count_footers++] = pg_strdup(buf.data); printTableAddFooter(&cont, buf.data);
} }
PQclear(result);
if (verbose) if (verbose)
{ {
...@@ -1562,51 +1460,31 @@ describeOneTableDetails(const char *schemaname, ...@@ -1562,51 +1460,31 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf, "%s: %s", s, printfPQExpBuffer(&buf, "%s: %s", s,
(tableinfo.hasoids ? _("yes") : _("no"))); (tableinfo.hasoids ? _("yes") : _("no")));
footers[count_footers++] = pg_strdup(buf.data); printTableAddFooter(&cont, buf.data);
} }
add_tablespace_footer(tableinfo.relkind, tableinfo.tablespace, add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
footers, &count_footers, buf, true); true);
/* end of list marker */
footers[count_footers] = NULL;
PQclear(result1);
PQclear(result2);
PQclear(result3);
PQclear(result4);
PQclear(result5);
PQclear(result6);
PQclear(result7);
} }
printTable(title.data, headers, printTable(&cont, pset.queryFout, pset.logfile);
(const char **) cells, (const char **) footers, printTableCleanup(&cont);
"llll", &myopt, pset.queryFout, pset.logfile);
retval = true; retval = true;
error_return: error_return:
/* clean up */ /* clean up */
printTableCleanup(&cont);
termPQExpBuffer(&buf); termPQExpBuffer(&buf);
termPQExpBuffer(&title); termPQExpBuffer(&title);
termPQExpBuffer(&tmpbuf); termPQExpBuffer(&tmpbuf);
if (cells) if (show_modifiers)
{
for (i = 0; i < numrows; i++)
{
if (show_modifiers)
free(cells[i * cols + 2]);
}
free(cells);
}
if (footers)
{ {
for (ptr = footers; *ptr; ptr++) for (ptr = modifiers; *ptr; ptr++)
free(*ptr); free(*ptr);
free(footers); free(modifiers);
} }
if (view_def) if (view_def)
...@@ -1618,14 +1496,14 @@ error_return: ...@@ -1618,14 +1496,14 @@ error_return:
return retval; return retval;
} }
/* /*
* Return true if the relation uses non default tablespace; * Add a tablespace description to a footer. If 'newline' is true, it is added
* otherwise return false * in a new line; otherwise it's appended to the current value of the last
* footer.
*/ */
static bool static void
add_tablespace_footer(char relkind, Oid tablespace, char **footers, add_tablespace_footer(printTableContent *const cont, char relkind,
int *count, PQExpBufferData buf, bool newline) Oid tablespace, const bool newline)
{ {
/* relkinds for which we support tablespaces */ /* relkinds for which we support tablespaces */
if (relkind == 'r' || relkind == 'i') if (relkind == 'r' || relkind == 'i')
...@@ -1636,29 +1514,40 @@ add_tablespace_footer(char relkind, Oid tablespace, char **footers, ...@@ -1636,29 +1514,40 @@ add_tablespace_footer(char relkind, Oid tablespace, char **footers,
*/ */
if (tablespace != 0) if (tablespace != 0)
{ {
PGresult *result1 = NULL; PGresult *result = NULL;
PQExpBufferData buf;
initPQExpBuffer(&buf);
printfPQExpBuffer(&buf, "SELECT spcname FROM pg_tablespace \n" printfPQExpBuffer(&buf, "SELECT spcname FROM pg_tablespace \n"
"WHERE oid = '%u';", tablespace); "WHERE oid = '%u';", tablespace);
result1 = PSQLexec(buf.data, false); result = PSQLexec(buf.data, false);
if (!result1) if (!result)
return false; return;
/* Should always be the case, but.... */ /* Should always be the case, but.... */
if (PQntuples(result1) > 0) if (PQntuples(result) > 0)
{ {
printfPQExpBuffer(&buf, if (newline)
newline ? _("Tablespace: \"%s\"") : _("tablespace \"%s\""), {
PQgetvalue(result1, 0, 0)); /* Add the tablespace as a new footer */
printfPQExpBuffer(&buf, _("Tablespace: \"%s\""),
footers[(*count)++] = pg_strdup(buf.data); PQgetvalue(result, 0, 0));
printTableAddFooter(cont, buf.data);
}
else
{
/* Append the tablespace to the latest footer */
printfPQExpBuffer(&buf, "%s", cont->footer->data);
/* translator: before this string there's an index
* description like '"foo_pkey" PRIMARY KEY, btree (a)' */
appendPQExpBuffer(&buf, _(", tablespace \"%s\""),
PQgetvalue(result, 0, 0));
printTableSetFooter(cont, buf.data);
}
} }
PQclear(result1); PQclear(result);
termPQExpBuffer(&buf);
return true;
} }
} }
return false;
} }
/* /*
......
...@@ -3,13 +3,10 @@ ...@@ -3,13 +3,10 @@
* *
* Copyright (c) 2000-2008, PostgreSQL Global Development Group * Copyright (c) 2000-2008, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.99 2008/05/10 03:31:58 tgl Exp $ * $PostgreSQL: pgsql/src/bin/psql/print.c,v 1.100 2008/05/12 22:59:58 alvherre Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "print.h"
#include "catalog/pg_type.h"
#include <math.h> #include <math.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
...@@ -24,9 +21,12 @@ ...@@ -24,9 +21,12 @@
#include <locale.h> #include <locale.h>
#include "catalog/pg_type.h"
#include "pqsignal.h" #include "pqsignal.h"
#include "common.h"
#include "mbprint.h" #include "mbprint.h"
#include "print.h"
/* /*
* We define the cancel_pressed flag in this file, rather than common.c where * We define the cancel_pressed flag in this file, rather than common.c where
...@@ -175,16 +175,12 @@ format_numeric_locale(const char *my_str) ...@@ -175,16 +175,12 @@ format_numeric_locale(const char *my_str)
static void static void
print_unaligned_text(const char *title, const char *const * headers, print_unaligned_text(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
const char *opt_fieldsep = opt->fieldSep; const char *opt_fieldsep = cont->opt->fieldSep;
const char *opt_recordsep = opt->recordSep; const char *opt_recordsep = cont->opt->recordSep;
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
bool need_recordsep = false; bool need_recordsep = false;
...@@ -197,22 +193,18 @@ print_unaligned_text(const char *title, const char *const * headers, ...@@ -197,22 +193,18 @@ print_unaligned_text(const char *title, const char *const * headers,
if (!opt_recordsep) if (!opt_recordsep)
opt_recordsep = ""; opt_recordsep = "";
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
fprintf(fout, "%s%s", title, opt_recordsep); fprintf(fout, "%s%s", cont->title, opt_recordsep);
/* print headers */ /* print headers */
if (!opt_tuples_only) if (!opt_tuples_only)
{ {
for (ptr = headers; *ptr; ptr++) for (ptr = cont->headers; *ptr; ptr++)
{ {
if (ptr != headers) if (ptr != cont->headers)
fputs(opt_fieldsep, fout); fputs(opt_fieldsep, fout);
fputs(*ptr, fout); fputs(*ptr, fout);
} }
...@@ -224,7 +216,7 @@ print_unaligned_text(const char *title, const char *const * headers, ...@@ -224,7 +216,7 @@ print_unaligned_text(const char *title, const char *const * headers,
need_recordsep = true; need_recordsep = true;
/* print cells */ /* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (need_recordsep) if (need_recordsep)
{ {
...@@ -233,7 +225,7 @@ print_unaligned_text(const char *title, const char *const * headers, ...@@ -233,7 +225,7 @@ print_unaligned_text(const char *title, const char *const * headers,
if (cancel_pressed) if (cancel_pressed)
break; break;
} }
if (opt_align[i % col_count] == 'r' && opt_numeric_locale) if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale(*ptr); char *my_cell = format_numeric_locale(*ptr);
...@@ -243,27 +235,30 @@ print_unaligned_text(const char *title, const char *const * headers, ...@@ -243,27 +235,30 @@ print_unaligned_text(const char *title, const char *const * headers,
else else
fputs(*ptr, fout); fputs(*ptr, fout);
if ((i + 1) % col_count) if ((i + 1) % cont->ncolumns)
fputs(opt_fieldsep, fout); fputs(opt_fieldsep, fout);
else else
need_recordsep = true; need_recordsep = true;
} }
/* print footers */ /* print footers */
if (opt->stop_table) if (cont->opt->stop_table)
{ {
if (!opt_tuples_only && footers && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
for (ptr = footers; *ptr; ptr++) {
printTableFooter *f;
for (f = cont->footers; f; f = f->next)
{ {
if (need_recordsep) if (need_recordsep)
{ {
fputs(opt_recordsep, fout); fputs(opt_recordsep, fout);
need_recordsep = false; need_recordsep = false;
} }
fputs(*ptr, fout); fputs(f->data, fout);
need_recordsep = true; need_recordsep = true;
} }
}
/* the last record needs to be concluded with a newline */ /* the last record needs to be concluded with a newline */
if (need_recordsep) if (need_recordsep)
fputc('\n', fout); fputc('\n', fout);
...@@ -272,16 +267,12 @@ print_unaligned_text(const char *title, const char *const * headers, ...@@ -272,16 +267,12 @@ print_unaligned_text(const char *title, const char *const * headers,
static void static void
print_unaligned_vertical(const char *title, const char *const * headers, print_unaligned_vertical(const printTableContent *cont, FILE *fout)
const char *const * cells,
const char *const * footers, const char *opt_align,
const printTableOpt *opt, FILE *fout)
{ {
const char *opt_fieldsep = opt->fieldSep; const char *opt_fieldsep = cont->opt->fieldSep;
const char *opt_recordsep = opt->recordSep; const char *opt_recordsep = cont->opt->recordSep;
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
bool need_recordsep = false; bool need_recordsep = false;
...@@ -294,16 +285,12 @@ print_unaligned_vertical(const char *title, const char *const * headers, ...@@ -294,16 +285,12 @@ print_unaligned_vertical(const char *title, const char *const * headers,
if (!opt_recordsep) if (!opt_recordsep)
opt_recordsep = ""; opt_recordsep = "";
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs(title, fout); fputs(cont->title, fout);
need_recordsep = true; need_recordsep = true;
} }
} }
...@@ -312,7 +299,7 @@ print_unaligned_vertical(const char *title, const char *const * headers, ...@@ -312,7 +299,7 @@ print_unaligned_vertical(const char *title, const char *const * headers,
need_recordsep = true; need_recordsep = true;
/* print records */ /* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (need_recordsep) if (need_recordsep)
{ {
...@@ -324,9 +311,9 @@ print_unaligned_vertical(const char *title, const char *const * headers, ...@@ -324,9 +311,9 @@ print_unaligned_vertical(const char *title, const char *const * headers,
break; break;
} }
fputs(headers[i % col_count], fout); fputs(cont->headers[i % cont->ncolumns], fout);
fputs(opt_fieldsep, fout); fputs(opt_fieldsep, fout);
if (opt_align[i % col_count] == 'r' && opt_numeric_locale) if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale(*ptr); char *my_cell = format_numeric_locale(*ptr);
...@@ -336,22 +323,24 @@ print_unaligned_vertical(const char *title, const char *const * headers, ...@@ -336,22 +323,24 @@ print_unaligned_vertical(const char *title, const char *const * headers,
else else
fputs(*ptr, fout); fputs(*ptr, fout);
if ((i + 1) % col_count) if ((i + 1) % cont->ncolumns)
fputs(opt_recordsep, fout); fputs(opt_recordsep, fout);
else else
need_recordsep = true; need_recordsep = true;
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
/* print footers */ /* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
{ {
printTableFooter *f;
fputs(opt_recordsep, fout); fputs(opt_recordsep, fout);
for (ptr = footers; *ptr; ptr++) for (f = cont->footers; f; f = f->next)
{ {
fputs(opt_recordsep, fout); fputs(opt_recordsep, fout);
fputs(*ptr, fout); fputs(f->data, fout);
} }
} }
...@@ -367,7 +356,8 @@ print_unaligned_vertical(const char *title, const char *const * headers, ...@@ -367,7 +356,8 @@ print_unaligned_vertical(const char *title, const char *const * headers,
/* draw "line" */ /* draw "line" */
static void static void
_print_horizontal_line(const unsigned int col_count, const unsigned int *widths, unsigned short border, FILE *fout) _print_horizontal_line(const unsigned int ncolumns, const unsigned int *widths,
unsigned short border, FILE *fout)
{ {
unsigned int i, unsigned int i,
j; j;
...@@ -377,12 +367,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, ...@@ -377,12 +367,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths,
else if (border == 2) else if (border == 2)
fputs("+-", fout); fputs("+-", fout);
for (i = 0; i < col_count; i++) for (i = 0; i < ncolumns; i++)
{ {
for (j = 0; j < widths[i]; j++) for (j = 0; j < widths[i]; j++)
fputc('-', fout); fputc('-', fout);
if (i < col_count - 1) if (i < ncolumns - 1)
{ {
if (border == 0) if (border == 0)
fputc(' ', fout); fputc(' ', fout);
...@@ -404,15 +394,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths, ...@@ -404,15 +394,12 @@ _print_horizontal_line(const unsigned int col_count, const unsigned int *widths,
* Print pretty boxes around cells. * Print pretty boxes around cells.
*/ */
static void static void
print_aligned_text(const char *title, const char *const * headers, print_aligned_text(const printTableContent *cont, bool is_pager, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
bool is_pager, FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
int encoding = opt->encoding; int encoding = cont->opt->encoding;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
unsigned int col_count = 0, cell_count = 0; unsigned int col_count = 0, cell_count = 0;
...@@ -444,12 +431,9 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -444,12 +431,9 @@ print_aligned_text(const char *title, const char *const * headers,
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
/* count columns */ if (cont->ncolumns > 0)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (col_count > 0)
{ {
col_count = cont->ncolumns;
width_header = pg_local_calloc(col_count, sizeof(*width_header)); width_header = pg_local_calloc(col_count, sizeof(*width_header));
width_average = pg_local_calloc(col_count, sizeof(*width_average)); width_average = pg_local_calloc(col_count, sizeof(*width_average));
max_width = pg_local_calloc(col_count, sizeof(*max_width)); max_width = pg_local_calloc(col_count, sizeof(*max_width));
...@@ -484,8 +468,8 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -484,8 +468,8 @@ print_aligned_text(const char *title, const char *const * headers,
nl_lines, nl_lines,
bytes_required; bytes_required;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
&width, &nl_lines, &bytes_required); encoding, &width, &nl_lines, &bytes_required);
if (width > max_width[i]) if (width > max_width[i])
max_width[i] = width; max_width[i] = width;
if (nl_lines > max_nl_lines[i]) if (nl_lines > max_nl_lines[i])
...@@ -497,7 +481,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -497,7 +481,7 @@ print_aligned_text(const char *title, const char *const * headers,
} }
/* scan all cells, find maximum width, compute cell_count */ /* scan all cells, find maximum width, compute cell_count */
for (i = 0, ptr = cells; *ptr; ptr++, i++, cell_count++) for (i = 0, ptr = cont->cells; *ptr; ptr++, i++, cell_count++)
{ {
int width, int width,
nl_lines, nl_lines,
...@@ -506,7 +490,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -506,7 +490,7 @@ print_aligned_text(const char *title, const char *const * headers,
/* Get width, ignore nl_lines */ /* Get width, ignore nl_lines */
pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding, pg_wcssize((unsigned char *) *ptr, strlen(*ptr), encoding,
&width, &nl_lines, &bytes_required); &width, &nl_lines, &bytes_required);
if (opt_numeric_locale && opt_align[i % col_count] == 'r') if (opt_numeric_locale && cont->aligns[i % col_count] == 'r')
{ {
width += additional_numeric_locale_len(*ptr); width += additional_numeric_locale_len(*ptr);
bytes_required += additional_numeric_locale_len(*ptr); bytes_required += additional_numeric_locale_len(*ptr);
...@@ -568,17 +552,17 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -568,17 +552,17 @@ print_aligned_text(const char *title, const char *const * headers,
for (i = 0; i < col_count; i++) for (i = 0; i < col_count; i++)
width_wrap[i] = max_width[i]; width_wrap[i] = max_width[i];
if (opt->format == PRINT_WRAPPED) if (cont->opt->format == PRINT_WRAPPED)
{ {
/* /*
* Choose target output width: \pset columns, or $COLUMNS, or ioctl * Choose target output width: \pset columns, or $COLUMNS, or ioctl
*/ */
if (opt->columns > 0) if (cont->opt->columns > 0)
output_columns = opt->columns; output_columns = cont->opt->columns;
else if ((fout == stdout && isatty(fileno(stdout))) || is_pager) else if ((fout == stdout && isatty(fileno(stdout))) || is_pager)
{ {
if (opt->env_columns > 0) if (cont->opt->env_columns > 0)
output_columns = opt->env_columns; output_columns = cont->opt->env_columns;
#ifdef TIOCGWINSZ #ifdef TIOCGWINSZ
else else
{ {
...@@ -640,19 +624,22 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -640,19 +624,22 @@ print_aligned_text(const char *title, const char *const * headers,
} }
/* time to output */ /* time to output */
if (opt->start_table) if (cont->opt->start_table)
{ {
/* print title */ /* print title */
if (title && !opt_tuples_only) if (cont->title && !opt_tuples_only)
{ {
int width, height; int width, height;
pg_wcssize((unsigned char *) title, strlen(title), encoding, pg_wcssize((unsigned char *) cont->title, strlen(cont->title),
&width, &height, NULL); encoding, &width, &height, NULL);
if (width >= width_total) if (width >= width_total)
fprintf(fout, "%s\n", title); /* Aligned */ /* Aligned */
fprintf(fout, "%s\n", cont->title);
else else
fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "", title); /* Centered */ /* Centered */
fprintf(fout, "%-*s%s\n", (width_total - width) / 2, "",
cont->title);
} }
/* print headers */ /* print headers */
...@@ -665,8 +652,9 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -665,8 +652,9 @@ print_aligned_text(const char *title, const char *const * headers,
_print_horizontal_line(col_count, width_wrap, opt_border, fout); _print_horizontal_line(col_count, width_wrap, opt_border, fout);
for (i = 0; i < col_count; i++) for (i = 0; i < col_count; i++)
pg_wcsformat((unsigned char *) headers[i], strlen(headers[i]), pg_wcsformat((unsigned char *) cont->headers[i],
encoding, col_lineptrs[i], max_nl_lines[i]); strlen(cont->headers[i]), encoding,
col_lineptrs[i], max_nl_lines[i]);
more_col_wrapping = col_count; more_col_wrapping = col_count;
curr_nl_line = 0; curr_nl_line = 0;
...@@ -678,7 +666,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -678,7 +666,7 @@ print_aligned_text(const char *title, const char *const * headers,
else if (opt_border == 1) else if (opt_border == 1)
fputc(curr_nl_line ? '+' : ' ', fout); fputc(curr_nl_line ? '+' : ' ', fout);
for (i = 0; i < col_count; i++) for (i = 0; i < cont->ncolumns; i++)
{ {
unsigned int nbspace; unsigned int nbspace;
...@@ -722,7 +710,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -722,7 +710,7 @@ print_aligned_text(const char *title, const char *const * headers,
} }
/* print cells, one loop per row */ /* print cells, one loop per row */
for (i = 0, ptr = cells; *ptr; i += col_count, ptr += col_count) for (i = 0, ptr = cont->cells; *ptr; i += col_count, ptr += col_count)
{ {
bool more_lines; bool more_lines;
...@@ -739,7 +727,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -739,7 +727,7 @@ print_aligned_text(const char *title, const char *const * headers,
col_lineptrs[j], max_nl_lines[j]); col_lineptrs[j], max_nl_lines[j]);
curr_nl_line[j] = 0; curr_nl_line[j] = 0;
if (opt_numeric_locale && opt_align[j % col_count] == 'r') if (opt_numeric_locale && cont->aligns[j % col_count] == 'r')
{ {
char *my_cell; char *my_cell;
...@@ -798,7 +786,7 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -798,7 +786,7 @@ print_aligned_text(const char *title, const char *const * headers,
if (chars_to_output > width_wrap[j]) if (chars_to_output > width_wrap[j])
chars_to_output = width_wrap[j]; chars_to_output = width_wrap[j];
if (opt_align[j] == 'r') /* Right aligned cell */ if (cont->aligns[j] == 'r') /* Right aligned cell */
{ {
/* spaces first */ /* spaces first */
fprintf(fout, "%*s", width_wrap[j] - chars_to_output, ""); fprintf(fout, "%*s", width_wrap[j] - chars_to_output, "");
...@@ -857,15 +845,19 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -857,15 +845,19 @@ print_aligned_text(const char *title, const char *const * headers,
} while (more_lines); } while (more_lines);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
if (opt_border == 2 && !cancel_pressed) if (opt_border == 2 && !cancel_pressed)
_print_horizontal_line(col_count, width_wrap, opt_border, fout); _print_horizontal_line(col_count, width_wrap, opt_border, fout);
/* print footers */ /* print footers */
if (footers && !opt_tuples_only && !cancel_pressed) if (cont->footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++) {
fprintf(fout, "%s\n", *ptr); printTableFooter *f;
for (f = cont->footers; f; f = f->next)
fprintf(fout, "%s\n", f->data);
}
/* /*
* for some reason MinGW (and MSVC) outputs an extra newline, so this * for some reason MinGW (and MSVC) outputs an extra newline, so this
...@@ -894,17 +886,13 @@ print_aligned_text(const char *title, const char *const * headers, ...@@ -894,17 +886,13 @@ print_aligned_text(const char *title, const char *const * headers,
static void static void
print_aligned_vertical(const char *title, const char *const * headers, print_aligned_vertical(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
int encoding = opt->encoding; int encoding = cont->opt->encoding;
unsigned int col_count = 0; unsigned long record = cont->opt->prior_records + 1;
unsigned long record = opt->prior_records + 1;
const char *const * ptr; const char *const * ptr;
unsigned int i, unsigned int i,
hwidth = 0, hwidth = 0,
...@@ -914,7 +902,6 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -914,7 +902,6 @@ print_aligned_vertical(const char *title, const char *const * headers,
hformatsize = 0, hformatsize = 0,
dformatsize = 0; dformatsize = 0;
char *divider; char *divider;
unsigned int cell_count = 0;
struct lineptr *hlineptr, struct lineptr *hlineptr,
*dlineptr; *dlineptr;
...@@ -924,25 +911,22 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -924,25 +911,22 @@ print_aligned_vertical(const char *title, const char *const * headers,
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
if (cells[0] == NULL && opt->start_table && opt->stop_table) if (cont->cells[0] == NULL && cont->opt->start_table &&
cont->opt->stop_table)
{ {
fprintf(fout, _("(No rows)\n")); fprintf(fout, _("(No rows)\n"));
return; return;
} }
/* count columns */
for (ptr = headers; *ptr; ptr++)
col_count++;
/* Find the maximum dimensions for the headers */ /* Find the maximum dimensions for the headers */
for (i = 0; i < col_count; i++) for (i = 0; i < cont->ncolumns; i++)
{ {
int width, int width,
height, height,
fs; fs;
pg_wcssize((unsigned char *) headers[i], strlen(headers[i]), encoding, pg_wcssize((unsigned char *) cont->headers[i], strlen(cont->headers[i]),
&width, &height, &fs); encoding, &width, &height, &fs);
if (width > hwidth) if (width > hwidth)
hwidth = width; hwidth = width;
if (height > hheight) if (height > hheight)
...@@ -951,19 +935,15 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -951,19 +935,15 @@ print_aligned_vertical(const char *title, const char *const * headers,
hformatsize = fs; hformatsize = fs;
} }
/* Count cells, find their lengths */
for (ptr = cells; *ptr; ptr++)
cell_count++;
/* find longest data cell */ /* find longest data cell */
for (i = 0, ptr = cells; *ptr; ptr++, i++) for (i = 0, ptr = cont->cells; *ptr; ptr++, i++)
{ {
int numeric_locale_len; int numeric_locale_len;
int width, int width,
height, height,
fs; fs;
if (opt_align[i % col_count] == 'r' && opt_numeric_locale) if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
numeric_locale_len = additional_numeric_locale_len(*ptr); numeric_locale_len = additional_numeric_locale_len(*ptr);
else else
numeric_locale_len = 0; numeric_locale_len = 0;
...@@ -1005,21 +985,21 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -1005,21 +985,21 @@ print_aligned_vertical(const char *title, const char *const * headers,
if (opt_border == 2) if (opt_border == 2)
strcat(divider, "-+"); strcat(divider, "-+");
if (opt->start_table) if (cont->opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
fprintf(fout, "%s\n", title); fprintf(fout, "%s\n", cont->title);
} }
/* print records */ /* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
int line_count, int line_count,
dcomplete, dcomplete,
hcomplete; hcomplete;
if (i % col_count == 0) if (i % cont->ncolumns == 0)
{ {
if (cancel_pressed) if (cancel_pressed)
break; break;
...@@ -1038,26 +1018,20 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -1038,26 +1018,20 @@ print_aligned_vertical(const char *title, const char *const * headers,
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str); fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
else else
{ {
char *div_copy = strdup(divider); char *div_copy = pg_strdup(divider);
if (!div_copy)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
strncpy(div_copy + opt_border, record_str, record_str_len); strncpy(div_copy + opt_border, record_str, record_str_len);
fprintf(fout, "%s\n", div_copy); fprintf(fout, "%s\n", div_copy);
free(div_copy); free(div_copy);
} }
} }
else if (i != 0 || !opt->start_table || opt_border == 2) else if (i != 0 || !cont->opt->start_table || opt_border == 2)
fprintf(fout, "%s\n", divider); fprintf(fout, "%s\n", divider);
} }
/* Format the header */ /* Format the header */
pg_wcsformat((unsigned char *) headers[i % col_count], pg_wcsformat((unsigned char *) cont->headers[i % cont->ncolumns],
strlen(headers[i % col_count]), strlen(cont->headers[i % cont->ncolumns]),
encoding, hlineptr, hheight); encoding, hlineptr, hheight);
/* Format the data */ /* Format the data */
pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding, pg_wcsformat((unsigned char *) *ptr, strlen(*ptr), encoding,
...@@ -1087,7 +1061,7 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -1087,7 +1061,7 @@ print_aligned_vertical(const char *title, const char *const * headers,
if (!dcomplete) if (!dcomplete)
{ {
if (opt_align[i % col_count] == 'r' && opt_numeric_locale) if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale((char *) dlineptr[line_count].ptr); char *my_cell = format_numeric_locale((char *) dlineptr[line_count].ptr);
...@@ -1121,18 +1095,20 @@ print_aligned_vertical(const char *title, const char *const * headers, ...@@ -1121,18 +1095,20 @@ print_aligned_vertical(const char *title, const char *const * headers,
} }
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
if (opt_border == 2 && !cancel_pressed) if (opt_border == 2 && !cancel_pressed)
fprintf(fout, "%s\n", divider); fprintf(fout, "%s\n", divider);
/* print footers */ /* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
{ {
printTableFooter *f;
if (opt_border < 2) if (opt_border < 2)
fputc('\n', fout); fputc('\n', fout);
for (ptr = footers; *ptr; ptr++) for (f = cont->footers; f; f = f->next)
fprintf(fout, "%s\n", *ptr); fprintf(fout, "%s\n", f->data);
} }
fputc('\n', fout); fputc('\n', fout);
...@@ -1193,27 +1169,19 @@ html_escaped_print(const char *in, FILE *fout) ...@@ -1193,27 +1169,19 @@ html_escaped_print(const char *in, FILE *fout)
static void static void
print_html_text(const char *title, const char *const * headers, print_html_text(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
const char *opt_table_attr = opt->tableAttr; const char *opt_table_attr = cont->opt->tableAttr;
unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
if (cancel_pressed) if (cancel_pressed)
return; return;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
fprintf(fout, "<table border=\"%d\"", opt_border); fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr) if (opt_table_attr)
...@@ -1221,10 +1189,10 @@ print_html_text(const char *title, const char *const * headers, ...@@ -1221,10 +1189,10 @@ print_html_text(const char *title, const char *const * headers,
fputs(">\n", fout); fputs(">\n", fout);
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs(" <caption>", fout); fputs(" <caption>", fout);
html_escaped_print(title, fout); html_escaped_print(cont->title, fout);
fputs("</caption>\n", fout); fputs("</caption>\n", fout);
} }
...@@ -1232,7 +1200,7 @@ print_html_text(const char *title, const char *const * headers, ...@@ -1232,7 +1200,7 @@ print_html_text(const char *title, const char *const * headers,
if (!opt_tuples_only) if (!opt_tuples_only)
{ {
fputs(" <tr>\n", fout); fputs(" <tr>\n", fout);
for (ptr = headers; *ptr; ptr++) for (ptr = cont->headers; *ptr; ptr++)
{ {
fputs(" <th align=\"center\">", fout); fputs(" <th align=\"center\">", fout);
html_escaped_print(*ptr, fout); html_escaped_print(*ptr, fout);
...@@ -1243,20 +1211,20 @@ print_html_text(const char *title, const char *const * headers, ...@@ -1243,20 +1211,20 @@ print_html_text(const char *title, const char *const * headers,
} }
/* print cells */ /* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (i % col_count == 0) if (i % cont->ncolumns == 0)
{ {
if (cancel_pressed) if (cancel_pressed)
break; break;
fputs(" <tr valign=\"top\">\n", fout); fputs(" <tr valign=\"top\">\n", fout);
} }
fprintf(fout, " <td align=\"%s\">", opt_align[(i) % col_count] == 'r' ? "right" : "left"); fprintf(fout, " <td align=\"%s\">", cont->aligns[(i) % cont->ncolumns] == 'r' ? "right" : "left");
/* is string only whitespace? */ /* is string only whitespace? */
if ((*ptr)[strspn(*ptr, " \t")] == '\0') if ((*ptr)[strspn(*ptr, " \t")] == '\0')
fputs("&nbsp; ", fout); fputs("&nbsp; ", fout);
else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale(*ptr); char *my_cell = format_numeric_locale(*ptr);
...@@ -1268,21 +1236,23 @@ print_html_text(const char *title, const char *const * headers, ...@@ -1268,21 +1236,23 @@ print_html_text(const char *title, const char *const * headers,
fputs("</td>\n", fout); fputs("</td>\n", fout);
if ((i + 1) % col_count == 0) if ((i + 1) % cont->ncolumns == 0)
fputs(" </tr>\n", fout); fputs(" </tr>\n", fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
fputs("</table>\n", fout); fputs("</table>\n", fout);
/* print footers */ /* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
{ {
printTableFooter *f;
fputs("<p>", fout); fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++) for (f = cont->footers; f; f = f->next)
{ {
html_escaped_print(*ptr, fout); html_escaped_print(f->data, fout);
fputs("<br />\n", fout); fputs("<br />\n", fout);
} }
fputs("</p>", fout); fputs("</p>", fout);
...@@ -1294,28 +1264,20 @@ print_html_text(const char *title, const char *const * headers, ...@@ -1294,28 +1264,20 @@ print_html_text(const char *title, const char *const * headers,
static void static void
print_html_vertical(const char *title, const char *const * headers, print_html_vertical(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
const char *opt_table_attr = opt->tableAttr; const char *opt_table_attr = cont->opt->tableAttr;
unsigned int col_count = 0; unsigned long record = cont->opt->prior_records + 1;
unsigned long record = opt->prior_records + 1;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
if (cancel_pressed) if (cancel_pressed)
return; return;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
fprintf(fout, "<table border=\"%d\"", opt_border); fprintf(fout, "<table border=\"%d\"", opt_border);
if (opt_table_attr) if (opt_table_attr)
...@@ -1323,18 +1285,18 @@ print_html_vertical(const char *title, const char *const * headers, ...@@ -1323,18 +1285,18 @@ print_html_vertical(const char *title, const char *const * headers,
fputs(">\n", fout); fputs(">\n", fout);
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs(" <caption>", fout); fputs(" <caption>", fout);
html_escaped_print(title, fout); html_escaped_print(cont->title, fout);
fputs("</caption>\n", fout); fputs("</caption>\n", fout);
} }
} }
/* print records */ /* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (i % col_count == 0) if (i % cont->ncolumns == 0)
{ {
if (cancel_pressed) if (cancel_pressed)
break; break;
...@@ -1347,14 +1309,14 @@ print_html_vertical(const char *title, const char *const * headers, ...@@ -1347,14 +1309,14 @@ print_html_vertical(const char *title, const char *const * headers,
} }
fputs(" <tr valign=\"top\">\n" fputs(" <tr valign=\"top\">\n"
" <th>", fout); " <th>", fout);
html_escaped_print(headers[i % col_count], fout); html_escaped_print(cont->headers[i % cont->ncolumns], fout);
fputs("</th>\n", fout); fputs("</th>\n", fout);
fprintf(fout, " <td align=\"%s\">", opt_align[i % col_count] == 'r' ? "right" : "left"); fprintf(fout, " <td align=\"%s\">", cont->aligns[i % cont->ncolumns] == 'r' ? "right" : "left");
/* is string only whitespace? */ /* is string only whitespace? */
if ((*ptr)[strspn(*ptr, " \t")] == '\0') if ((*ptr)[strspn(*ptr, " \t")] == '\0')
fputs("&nbsp; ", fout); fputs("&nbsp; ", fout);
else if (opt_align[i % col_count] == 'r' && opt_numeric_locale) else if (cont->aligns[i % cont->ncolumns] == 'r' && opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale(*ptr); char *my_cell = format_numeric_locale(*ptr);
...@@ -1367,17 +1329,19 @@ print_html_vertical(const char *title, const char *const * headers, ...@@ -1367,17 +1329,19 @@ print_html_vertical(const char *title, const char *const * headers,
fputs("</td>\n </tr>\n", fout); fputs("</td>\n </tr>\n", fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
fputs("</table>\n", fout); fputs("</table>\n", fout);
/* print footers */ /* print footers */
if (!opt_tuples_only && footers && *footers && !cancel_pressed) if (!opt_tuples_only && cont->footers != NULL && !cancel_pressed)
{ {
printTableFooter *f;
fputs("<p>", fout); fputs("<p>", fout);
for (ptr = footers; *ptr; ptr++) for (f = cont->footers; f; f = f->next)
{ {
html_escaped_print(*ptr, fout); html_escaped_print(f->data, fout);
fputs("<br />\n", fout); fputs("<br />\n", fout);
} }
fputs("</p>", fout); fputs("</p>", fout);
...@@ -1432,15 +1396,11 @@ latex_escaped_print(const char *in, FILE *fout) ...@@ -1432,15 +1396,11 @@ latex_escaped_print(const char *in, FILE *fout)
static void static void
print_latex_text(const char *title, const char *const * headers, print_latex_text(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
...@@ -1450,17 +1410,13 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1450,17 +1410,13 @@ print_latex_text(const char *title, const char *const * headers,
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs("\\begin{center}\n", fout); fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout); latex_escaped_print(cont->title, fout);
fputs("\n\\end{center}\n\n", fout); fputs("\n\\end{center}\n\n", fout);
} }
...@@ -1469,10 +1425,10 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1469,10 +1425,10 @@ print_latex_text(const char *title, const char *const * headers,
if (opt_border == 2) if (opt_border == 2)
fputs("| ", fout); fputs("| ", fout);
for (i = 0; i < col_count; i++) for (i = 0; i < cont->ncolumns; i++)
{ {
fputc(*(opt_align + i), fout); fputc(*(cont->aligns + i), fout);
if (opt_border != 0 && i < col_count - 1) if (opt_border != 0 && i < cont->ncolumns - 1)
fputs(" | ", fout); fputs(" | ", fout);
} }
if (opt_border == 2) if (opt_border == 2)
...@@ -1486,7 +1442,7 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1486,7 +1442,7 @@ print_latex_text(const char *title, const char *const * headers,
/* print headers */ /* print headers */
if (!opt_tuples_only) if (!opt_tuples_only)
{ {
for (i = 0, ptr = headers; i < col_count; i++, ptr++) for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
{ {
if (i != 0) if (i != 0)
fputs(" & ", fout); fputs(" & ", fout);
...@@ -1500,7 +1456,7 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1500,7 +1456,7 @@ print_latex_text(const char *title, const char *const * headers,
} }
/* print cells */ /* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (opt_numeric_locale) if (opt_numeric_locale)
{ {
...@@ -1512,7 +1468,7 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1512,7 +1468,7 @@ print_latex_text(const char *title, const char *const * headers,
else else
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
if ((i + 1) % col_count == 0) if ((i + 1) % cont->ncolumns == 0)
{ {
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
if (cancel_pressed) if (cancel_pressed)
...@@ -1522,7 +1478,7 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1522,7 +1478,7 @@ print_latex_text(const char *title, const char *const * headers,
fputs(" & ", fout); fputs(" & ", fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
if (opt_border == 2) if (opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
...@@ -1530,11 +1486,13 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1530,11 +1486,13 @@ print_latex_text(const char *title, const char *const * headers,
fputs("\\end{tabular}\n\n\\noindent ", fout); fputs("\\end{tabular}\n\n\\noindent ", fout);
/* print footers */ /* print footers */
if (footers && !opt_tuples_only && !cancel_pressed) if (cont->footers && !opt_tuples_only && !cancel_pressed)
{ {
for (ptr = footers; *ptr; ptr++) printTableFooter *f;
for (f = cont->footers; f; f = f->next)
{ {
latex_escaped_print(*ptr, fout); latex_escaped_print(f->data, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
} }
...@@ -1545,38 +1503,28 @@ print_latex_text(const char *title, const char *const * headers, ...@@ -1545,38 +1503,28 @@ print_latex_text(const char *title, const char *const * headers,
static void static void
print_latex_vertical(const char *title, const char *const * headers, print_latex_vertical(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
unsigned int col_count = 0; unsigned long record = cont->opt->prior_records + 1;
unsigned long record = opt->prior_records + 1;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
(void) opt_align; /* currently unused parameter */
if (cancel_pressed) if (cancel_pressed)
return; return;
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs("\\begin{center}\n", fout); fputs("\\begin{center}\n", fout);
latex_escaped_print(title, fout); latex_escaped_print(cont->title, fout);
fputs("\n\\end{center}\n\n", fout); fputs("\n\\end{center}\n\n", fout);
} }
...@@ -1592,10 +1540,10 @@ print_latex_vertical(const char *title, const char *const * headers, ...@@ -1592,10 +1540,10 @@ print_latex_vertical(const char *title, const char *const * headers,
} }
/* print records */ /* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
/* new record */ /* new record */
if (i % col_count == 0) if (i % cont->ncolumns == 0)
{ {
if (cancel_pressed) if (cancel_pressed)
break; break;
...@@ -1613,13 +1561,13 @@ print_latex_vertical(const char *title, const char *const * headers, ...@@ -1613,13 +1561,13 @@ print_latex_vertical(const char *title, const char *const * headers,
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
} }
latex_escaped_print(headers[i % col_count], fout); latex_escaped_print(cont->headers[i % cont->ncolumns], fout);
fputs(" & ", fout); fputs(" & ", fout);
latex_escaped_print(*ptr, fout); latex_escaped_print(*ptr, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
if (opt_border == 2) if (opt_border == 2)
fputs("\\hline\n", fout); fputs("\\hline\n", fout);
...@@ -1627,19 +1575,21 @@ print_latex_vertical(const char *title, const char *const * headers, ...@@ -1627,19 +1575,21 @@ print_latex_vertical(const char *title, const char *const * headers,
fputs("\\end{tabular}\n\n\\noindent ", fout); fputs("\\end{tabular}\n\n\\noindent ", fout);
/* print footers */ /* print footers */
if (footers && !opt_tuples_only && !cancel_pressed) if (cont->footers && !opt_tuples_only && !cancel_pressed)
{ {
for (ptr = footers; *ptr; ptr++) printTableFooter *f;
for (f = cont->footers; f; f = f->next)
{ {
if (opt_numeric_locale) if (opt_numeric_locale)
{ {
char *my_cell = format_numeric_locale(*ptr); char *my_cell = format_numeric_locale(f->data);
latex_escaped_print(my_cell, fout); latex_escaped_print(my_cell, fout);
free(my_cell); free(my_cell);
} }
else else
latex_escaped_print(*ptr, fout); latex_escaped_print(f->data, fout);
fputs(" \\\\\n", fout); fputs(" \\\\\n", fout);
} }
} }
...@@ -1672,15 +1622,11 @@ troff_ms_escaped_print(const char *in, FILE *fout) ...@@ -1672,15 +1622,11 @@ troff_ms_escaped_print(const char *in, FILE *fout)
static void static void
print_troff_ms_text(const char *title, const char *const * headers, print_troff_ms_text(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
unsigned int col_count = 0;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
...@@ -1690,17 +1636,13 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1690,17 +1636,13 @@ print_troff_ms_text(const char *title, const char *const * headers,
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs(".LP\n.DS C\n", fout); fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout); troff_ms_escaped_print(cont->title, fout);
fputs("\n.DE\n", fout); fputs("\n.DE\n", fout);
} }
...@@ -1711,10 +1653,10 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1711,10 +1653,10 @@ print_troff_ms_text(const char *title, const char *const * headers,
else else
fputs("center;\n", fout); fputs("center;\n", fout);
for (i = 0; i < col_count; i++) for (i = 0; i < cont->ncolumns; i++)
{ {
fputc(*(opt_align + i), fout); fputc(*(cont->aligns + i), fout);
if (opt_border > 0 && i < col_count - 1) if (opt_border > 0 && i < cont->ncolumns - 1)
fputs(" | ", fout); fputs(" | ", fout);
} }
fputs(".\n", fout); fputs(".\n", fout);
...@@ -1722,7 +1664,7 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1722,7 +1664,7 @@ print_troff_ms_text(const char *title, const char *const * headers,
/* print headers */ /* print headers */
if (!opt_tuples_only) if (!opt_tuples_only)
{ {
for (i = 0, ptr = headers; i < col_count; i++, ptr++) for (i = 0, ptr = cont->headers; i < cont->ncolumns; i++, ptr++)
{ {
if (i != 0) if (i != 0)
fputc('\t', fout); fputc('\t', fout);
...@@ -1735,7 +1677,7 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1735,7 +1677,7 @@ print_troff_ms_text(const char *title, const char *const * headers,
} }
/* print cells */ /* print cells */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
if (opt_numeric_locale) if (opt_numeric_locale)
{ {
...@@ -1747,7 +1689,7 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1747,7 +1689,7 @@ print_troff_ms_text(const char *title, const char *const * headers,
else else
troff_ms_escaped_print(*ptr, fout); troff_ms_escaped_print(*ptr, fout);
if ((i + 1) % col_count == 0) if ((i + 1) % cont->ncolumns == 0)
{ {
fputc('\n', fout); fputc('\n', fout);
if (cancel_pressed) if (cancel_pressed)
...@@ -1757,17 +1699,21 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1757,17 +1699,21 @@ print_troff_ms_text(const char *title, const char *const * headers,
fputc('\t', fout); fputc('\t', fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
fputs(".TE\n.DS L\n", fout); fputs(".TE\n.DS L\n", fout);
/* print footers */ /* print footers */
if (footers && !opt_tuples_only && !cancel_pressed) if (cont->footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++) {
printTableFooter *f;
for (f = cont->footers; f; f = f->next)
{ {
troff_ms_escaped_print(*ptr, fout); troff_ms_escaped_print(f->data, fout);
fputc('\n', fout); fputc('\n', fout);
} }
}
fputs(".DE\n", fout); fputs(".DE\n", fout);
} }
...@@ -1775,39 +1721,29 @@ print_troff_ms_text(const char *title, const char *const * headers, ...@@ -1775,39 +1721,29 @@ print_troff_ms_text(const char *title, const char *const * headers,
static void static void
print_troff_ms_vertical(const char *title, const char *const * headers, print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
const char *const * cells, const char *const * footers,
const char *opt_align, const printTableOpt *opt,
FILE *fout)
{ {
bool opt_tuples_only = opt->tuples_only; bool opt_tuples_only = cont->opt->tuples_only;
bool opt_numeric_locale = opt->numericLocale; bool opt_numeric_locale = cont->opt->numericLocale;
unsigned short opt_border = opt->border; unsigned short opt_border = cont->opt->border;
unsigned int col_count = 0; unsigned long record = cont->opt->prior_records + 1;
unsigned long record = opt->prior_records + 1;
unsigned int i; unsigned int i;
const char *const * ptr; const char *const * ptr;
unsigned short current_format = 0; /* 0=none, 1=header, 2=body */ unsigned short current_format = 0; /* 0=none, 1=header, 2=body */
(void) opt_align; /* currently unused parameter */
if (cancel_pressed) if (cancel_pressed)
return; return;
if (opt_border > 2) if (opt_border > 2)
opt_border = 2; opt_border = 2;
/* count columns */ if (cont->opt->start_table)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (opt->start_table)
{ {
/* print title */ /* print title */
if (!opt_tuples_only && title) if (!opt_tuples_only && cont->title)
{ {
fputs(".LP\n.DS C\n", fout); fputs(".LP\n.DS C\n", fout);
troff_ms_escaped_print(title, fout); troff_ms_escaped_print(cont->title, fout);
fputs("\n.DE\n", fout); fputs("\n.DE\n", fout);
} }
...@@ -1826,10 +1762,10 @@ print_troff_ms_vertical(const char *title, const char *const * headers, ...@@ -1826,10 +1762,10 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
current_format = 2; /* assume tuples printed already */ current_format = 2; /* assume tuples printed already */
/* print records */ /* print records */
for (i = 0, ptr = cells; *ptr; i++, ptr++) for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
{ {
/* new record */ /* new record */
if (i % col_count == 0) if (i % cont->ncolumns == 0)
{ {
if (cancel_pressed) if (cancel_pressed)
break; break;
...@@ -1864,7 +1800,7 @@ print_troff_ms_vertical(const char *title, const char *const * headers, ...@@ -1864,7 +1800,7 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
} }
} }
troff_ms_escaped_print(headers[i % col_count], fout); troff_ms_escaped_print(cont->headers[i % cont->ncolumns], fout);
fputc('\t', fout); fputc('\t', fout);
if (opt_numeric_locale) if (opt_numeric_locale)
{ {
...@@ -1879,17 +1815,21 @@ print_troff_ms_vertical(const char *title, const char *const * headers, ...@@ -1879,17 +1815,21 @@ print_troff_ms_vertical(const char *title, const char *const * headers,
fputc('\n', fout); fputc('\n', fout);
} }
if (opt->stop_table) if (cont->opt->stop_table)
{ {
fputs(".TE\n.DS L\n", fout); fputs(".TE\n.DS L\n", fout);
/* print footers */ /* print footers */
if (footers && !opt_tuples_only && !cancel_pressed) if (cont->footers && !opt_tuples_only && !cancel_pressed)
for (ptr = footers; *ptr; ptr++) {
printTableFooter *f;
for (f = cont->footers; f; f = f->next)
{ {
troff_ms_escaped_print(*ptr, fout); troff_ms_escaped_print(f->data, fout);
fputc('\n', fout); fputc('\n', fout);
} }
}
fputs(".DE\n", fout); fputs(".DE\n", fout);
} }
...@@ -1976,53 +1916,239 @@ ClosePager(FILE *pagerpipe) ...@@ -1976,53 +1916,239 @@ ClosePager(FILE *pagerpipe)
} }
} }
/*
* Initialise a table contents struct.
*
* Must be called before any other printTable method is used.
*
* If you call this, you must call printTableCleanup once you're done with the
* table.
*/
void
printTableInit(printTableContent *const content, const printTableOpt *opt,
const char *title, int ncolumns, int nrows)
{
content->opt = opt;
content->title = title;
content->ncolumns = ncolumns;
content->nrows = nrows;
content->headers = pg_local_calloc(ncolumns + 1,
sizeof(*content->headers));
content->cells = pg_local_calloc(ncolumns * nrows + 1,
sizeof(*content->cells));
content->footers = NULL;
content->aligns = pg_local_calloc(ncolumns + 1,
sizeof(*content->align));
content->header = content->headers;
content->cell = content->cells;
content->footer = content->footers;
content->align = content->aligns;
}
/*
* Add a header to the table.
*
* Headers are not duplicated; you must ensure that the header string is
* available for the lifetime of the printTableContent struct.
*
* If translate is true, the function will pass the header through gettext.
* Otherwise, the header will not be translated.
*
* align is either 'l' or 'r', and specifies the alignment for cells in this
* column.
*/
void void
printTable(const char *title, printTableAddHeader(printTableContent *const content, const char *header,
const char *const * headers, bool translate, char align)
const char *const * cells, {
const char *const * footers, #ifndef ENABLE_NLS
const char *align, (void) translate; /* unused parameter */
const printTableOpt *opt, FILE *fout, FILE *flog) #endif
if (content->header >= content->headers + content->ncolumns)
{
fprintf(stderr, _("Cannot add header to table content: "
"column count of %d exceeded.\n"),
content->ncolumns);
exit(EXIT_FAILURE);
}
*content->header = (char *) mbvalidate((unsigned char *) header,
content->opt->encoding);
#ifdef ENABLE_NLS
if (translate)
*content->header = _(*content->header);
#endif
content->header++;
*content->align = align;
content->align++;
}
/*
* Add a cell to the table.
*
* Cells are not duplicated; you must ensure that the cell string is available
* for the lifetime of the printTableContent struct.
*
* If translate is true, the function will pass the cell through gettext.
* Otherwise, the cell will not be translated.
*/
void
printTableAddCell(printTableContent *const content, const char *cell,
bool translate)
{
#ifndef ENABLE_NLS
(void) translate; /* unused parameter */
#endif
if (content->cell >= content->cells + (content->ncolumns * content->nrows))
{
fprintf(stderr, _("Cannot add cell to table content: "
"total cell count of %d exceeded.\n"),
content->ncolumns * content->nrows);
exit(EXIT_FAILURE);
}
*content->cell = (char *) mbvalidate((unsigned char *) cell,
content->opt->encoding);
#ifdef ENABLE_NLS
if (translate)
*content->header = _(*content->header);
#endif
content->cell++;
}
/*
* Add a footer to the table.
*
* Footers are added as elements of a singly-linked list, and the content is
* strdup'd, so there is no need to keep the original footer string around.
*
* Footers are never translated by the function. If you want the footer
* translated you must do so yourself, before calling printTableAddFooter. The
* reason this works differently to headers and cells is that footers tend to
* be made of up individually translated components, rather than being
* translated as a whole.
*/
void
printTableAddFooter(printTableContent *const content, const char *footer)
{
printTableFooter *f;
f = pg_local_calloc(1, sizeof(*f));
f->data = pg_strdup(footer);
if (content->footers == NULL)
content->footers = f;
else
content->footer->next = f;
content->footer = f;
}
/*
* Change the content of the last-added footer.
*
* The current contents of the last-added footer are freed, and replaced by the
* content given in *footer. If there was no previous footer, add a new one.
*
* The content is strdup'd, so there is no need to keep the original string
* around.
*/
void
printTableSetFooter(printTableContent *const content, const char *footer)
{
if (content->footers != NULL)
{
free(content->footer->data);
content->footer->data = pg_strdup(footer);
}
else
printTableAddFooter(content, footer);
}
/*
* Free all memory allocated to this struct.
*
* Once this has been called, the struct is unusable unless you pass it to
* printTableInit() again.
*/
void
printTableCleanup(printTableContent *content)
{
free(content->headers);
free(content->cells);
free(content->aligns);
content->opt = NULL;
content->title = NULL;
content->headers = NULL;
content->cells = NULL;
content->aligns = NULL;
content->header = NULL;
content->cell = NULL;
content->align = NULL;
if (content->footers)
{
for (content->footer = content->footers; content->footer;)
{
printTableFooter *f;
f = content->footer;
content->footer = f->next;
free(f->data);
free(f);
}
}
content->footers = NULL;
content->footer = NULL;
}
/*
* Use this to print just any table in the supported formats.
*/
void
printTable(const printTableContent *cont, FILE *fout, FILE *flog)
{ {
static const char *default_footer[] = {NULL};
FILE *output; FILE *output;
bool is_pager = false; bool is_pager = false;
if (cancel_pressed) if (cancel_pressed)
return; return;
if (opt->format == PRINT_NOTHING) if (cont->opt->format == PRINT_NOTHING)
return; return;
if (!footers)
footers = default_footer;
if (fout == stdout) if (fout == stdout)
{ {
int col_count = 0, int lines;
row_count = 0,
lines; if (cont->opt->expanded)
const char *const * ptr; lines = (cont->ncolumns + 1) * cont->nrows;
/* rough estimate of columns and rows */
if (headers)
for (ptr = headers; *ptr; ptr++)
col_count++;
if (cells)
for (ptr = cells; *ptr; ptr++)
row_count++;
if (col_count > 0)
row_count /= col_count;
if (opt->expanded)
lines = (col_count + 1) * row_count;
else else
lines = row_count + 1; lines = cont->nrows + 1;
if (footers && !opt->tuples_only)
for (ptr = footers; *ptr; ptr++) if (!cont->opt->tuples_only)
{
printTableFooter *f;
/*
* FIXME -- this is slightly bogus: it counts the number of
* footers, not the number of lines in them.
*/
for (f = cont->footers; f; f = f->next)
lines++; lines++;
output = PageOutput(lines, opt->pager); }
output = PageOutput(lines, cont->opt->pager);
is_pager = (output != fout); is_pager = (output != fout);
} }
else else
...@@ -2031,54 +2157,44 @@ printTable(const char *title, ...@@ -2031,54 +2157,44 @@ printTable(const char *title,
/* print the stuff */ /* print the stuff */
if (flog) if (flog)
print_aligned_text(title, headers, cells, footers, align, print_aligned_text(cont, is_pager, flog);
opt, is_pager, flog);
switch (opt->format) switch (cont->opt->format)
{ {
case PRINT_UNALIGNED: case PRINT_UNALIGNED:
if (opt->expanded) if (cont->opt->expanded)
print_unaligned_vertical(title, headers, cells, footers, align, print_unaligned_vertical(cont, output);
opt, output);
else else
print_unaligned_text(title, headers, cells, footers, align, print_unaligned_text(cont, output);
opt, output);
break; break;
case PRINT_ALIGNED: case PRINT_ALIGNED:
case PRINT_WRAPPED: case PRINT_WRAPPED:
if (opt->expanded) if (cont->opt->expanded)
print_aligned_vertical(title, headers, cells, footers, align, print_aligned_vertical(cont, output);
opt, output);
else else
print_aligned_text(title, headers, cells, footers, align, print_aligned_text(cont, is_pager, output);
opt, is_pager, output);
break; break;
case PRINT_HTML: case PRINT_HTML:
if (opt->expanded) if (cont->opt->expanded)
print_html_vertical(title, headers, cells, footers, align, print_html_vertical(cont, output);
opt, output);
else else
print_html_text(title, headers, cells, footers, align, print_html_text(cont, output);
opt, output);
break; break;
case PRINT_LATEX: case PRINT_LATEX:
if (opt->expanded) if (cont->opt->expanded)
print_latex_vertical(title, headers, cells, footers, align, print_latex_vertical(cont, output);
opt, output);
else else
print_latex_text(title, headers, cells, footers, align, print_latex_text(cont, output);
opt, output);
break; break;
case PRINT_TROFF_MS: case PRINT_TROFF_MS:
if (opt->expanded) if (cont->opt->expanded)
print_troff_ms_vertical(title, headers, cells, footers, align, print_troff_ms_vertical(cont, output);
opt, output);
else else
print_troff_ms_text(title, headers, cells, footers, align, print_troff_ms_text(cont, output);
opt, output);
break; break;
default: default:
fprintf(stderr, _("invalid output format (internal error): %d"), opt->format); fprintf(stderr, _("invalid output format (internal error): %d"),
cont->opt->format);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -2086,17 +2202,15 @@ printTable(const char *title, ...@@ -2086,17 +2202,15 @@ printTable(const char *title,
ClosePager(output); ClosePager(output);
} }
/*
* Use this to print query results
*
* It calls printTable with all the things set straight.
*/
void void
printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog) printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *flog)
{ {
int ntuples; printTableContent cont;
int nfields;
int ncells;
const char **headers;
const char **cells;
char **footers;
char *align;
int i, int i,
r, r,
c; c;
...@@ -2104,71 +2218,12 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f ...@@ -2104,71 +2218,12 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
if (cancel_pressed) if (cancel_pressed)
return; return;
/* extract headers */ printTableInit(&cont, &opt->topt, opt->title,
ntuples = PQntuples(result); PQnfields(result), PQntuples(result));
nfields = PQnfields(result);
headers = pg_local_calloc(nfields + 1, sizeof(*headers));
for (i = 0; i < nfields; i++)
{
headers[i] = (char *) mbvalidate((unsigned char *) PQfname(result, i),
opt->topt.encoding);
#ifdef ENABLE_NLS
if (opt->trans_headers)
headers[i] = _(headers[i]);
#endif
}
/* set cells */
ncells = ntuples * nfields;
cells = pg_local_calloc(ncells + 1, sizeof(*cells));
i = 0;
for (r = 0; r < ntuples; r++)
{
for (c = 0; c < nfields; c++)
{
if (PQgetisnull(result, r, c))
cells[i] = opt->nullPrint ? opt->nullPrint : "";
else
{
cells[i] = (char *)
mbvalidate((unsigned char *) PQgetvalue(result, r, c),
opt->topt.encoding);
#ifdef ENABLE_NLS
if (opt->trans_columns && opt->trans_columns[c])
cells[i] = _(cells[i]);
#endif
}
i++;
}
}
/* set footers */
if (opt->footers) for (i = 0; i < cont.ncolumns; i++)
footers = opt->footers;
else if (!opt->topt.expanded && opt->default_footer)
{
unsigned long total_records;
footers = pg_local_calloc(2, sizeof(*footers));
footers[0] = pg_local_malloc(100);
total_records = opt->topt.prior_records + ntuples;
if (total_records == 1)
snprintf(footers[0], 100, _("(1 row)"));
else
snprintf(footers[0], 100, _("(%lu rows)"), total_records);
}
else
footers = NULL;
/* set alignment */
align = pg_local_calloc(nfields + 1, sizeof(*align));
for (i = 0; i < nfields; i++)
{ {
char align;
Oid ftype = PQftype(result, i); Oid ftype = PQftype(result, i);
switch (ftype) switch (ftype)
...@@ -2183,27 +2238,59 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f ...@@ -2183,27 +2238,59 @@ printQuery(const PGresult *result, const printQueryOpt *opt, FILE *fout, FILE *f
case XIDOID: case XIDOID:
case CIDOID: case CIDOID:
case CASHOID: case CASHOID:
align[i] = 'r'; align = 'r';
break; break;
default: default:
align[i] = 'l'; align = 'l';
break; break;
} }
printTableAddHeader(&cont, PQfname(result, i),
opt->trans_headers, align);
} }
/* call table printer */ /* set cells */
printTable(opt->title, headers, cells, for (r = 0; r < cont.nrows; r++)
(const char *const *) footers, {
align, &opt->topt, fout, flog); for (c = 0; c < cont.ncolumns; c++)
{
char *cell;
bool translate;
if (PQgetisnull(result, r, c))
cell = opt->nullPrint ? opt->nullPrint : "";
else
cell = PQgetvalue(result, r, c);
translate = (opt->trans_columns && opt->trans_columns[c]);
printTableAddCell(&cont, cell, translate);
}
}
free(headers); /* set footers */
free(cells); if (opt->footers)
if (footers && !opt->footers) {
char **footer;
for (footer = opt->footers; *footer; footer++)
printTableAddFooter(&cont, *footer);
}
else if (!opt->topt.expanded && opt->default_footer)
{ {
free(footers[0]); unsigned long total_records;
free(footers); char default_footer[100];
total_records = opt->topt.prior_records + cont.nrows;
if (total_records == 1)
snprintf(default_footer, 100, _("(1 row)"));
else
snprintf(default_footer, 100, _("(%lu rows)"), total_records);
printTableAddFooter(&cont, default_footer);
} }
free(align);
printTable(&cont, fout, flog);
printTableCleanup(&cont);
} }
...@@ -2215,17 +2302,17 @@ setDecimalLocale(void) ...@@ -2215,17 +2302,17 @@ setDecimalLocale(void)
extlconv = localeconv(); extlconv = localeconv();
if (*extlconv->decimal_point) if (*extlconv->decimal_point)
decimal_point = strdup(extlconv->decimal_point); decimal_point = pg_strdup(extlconv->decimal_point);
else else
decimal_point = "."; /* SQL output standard */ decimal_point = "."; /* SQL output standard */
if (*extlconv->grouping && atoi(extlconv->grouping) > 0) if (*extlconv->grouping && atoi(extlconv->grouping) > 0)
grouping = strdup(extlconv->grouping); grouping = pg_strdup(extlconv->grouping);
else else
grouping = "3"; /* most common */ grouping = "3"; /* most common */
/* similar code exists in formatting.c */ /* similar code exists in formatting.c */
if (*extlconv->thousands_sep) if (*extlconv->thousands_sep)
thousands_sep = strdup(extlconv->thousands_sep); thousands_sep = pg_strdup(extlconv->thousands_sep);
/* Make sure thousands separator doesn't match decimal point symbol. */ /* Make sure thousands separator doesn't match decimal point symbol. */
else if (strcmp(decimal_point, ",") != 0) else if (strcmp(decimal_point, ",") != 0)
thousands_sep = ","; thousands_sep = ",";
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 2000-2008, PostgreSQL Global Development Group * Copyright (c) 2000-2008, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.36 2008/05/08 17:04:26 momjian Exp $ * $PostgreSQL: pgsql/src/bin/psql/print.h,v 1.37 2008/05/12 22:59:58 alvherre Exp $
*/ */
#ifndef PRINT_H #ifndef PRINT_H
#define PRINT_H #define PRINT_H
...@@ -11,11 +11,6 @@ ...@@ -11,11 +11,6 @@
#include "libpq-fe.h" #include "libpq-fe.h"
extern FILE *PageOutput(int lines, unsigned short int pager);
extern void ClosePager(FILE *pagerpipe);
extern void html_escaped_print(const char *in, FILE *fout);
enum printFormat enum printFormat
{ {
PRINT_NOTHING = 0, /* to make sure someone initializes this */ PRINT_NOTHING = 0, /* to make sure someone initializes this */
...@@ -29,7 +24,7 @@ enum printFormat ...@@ -29,7 +24,7 @@ enum printFormat
}; };
typedef struct _printTableOpt typedef struct printTableOpt
{ {
enum printFormat format; /* one of the above */ enum printFormat format; /* one of the above */
bool expanded; /* expanded/vertical output (if supported by bool expanded; /* expanded/vertical output (if supported by
...@@ -52,25 +47,42 @@ typedef struct _printTableOpt ...@@ -52,25 +47,42 @@ typedef struct _printTableOpt
int columns; /* target width for wrapped format */ int columns; /* target width for wrapped format */
} printTableOpt; } printTableOpt;
/* /*
* Use this to print just any table in the supported formats. * Table footers are implemented as a singly-linked list.
* - title is just any string (NULL is fine) *
* - headers is the column headings (NULL ptr terminated). It must be given and * This is so that you don't need to know the number of footers in order to
* complete since the column count is generated from this. * initialise the printTableContent struct, which is very convenient when
* - cells are the data cells to be printed. Now you know why the correct * preparing complex footers (as in describeOneTableDetails).
* column count is important */
* - footers are lines to be printed below the table typedef struct printTableFooter
* - align is an 'l' or an 'r' for every column, if the output format needs it. {
* (You must specify this long enough. Otherwise anything could happen.) char *data;
*/ struct printTableFooter *next;
void printTable(const char *title, const char *const * headers, } printTableFooter;
const char *const * cells, const char *const * footers,
const char *align,
const printTableOpt *opt, FILE *fout, FILE *flog);
/*
* The table content struct holds all the information which will be displayed
* by printTable().
*/
typedef struct printTableContent
{
const printTableOpt *opt;
const char *title; /* May be NULL */
int ncolumns; /* Specified in Init() */
int nrows; /* Specified in Init() */
const char **headers; /* NULL-terminated array of header strings */
const char **header; /* Pointer to the last added header */
const char **cells; /* NULL-terminated array of cell content
strings */
const char **cell; /* Pointer to the last added cell */
printTableFooter *footers; /* Pointer to the first footer */
printTableFooter *footer; /* Pointer to the last added footer */
char *aligns; /* Array of alignment specifiers; 'l' or 'r',
one per column */
char *align; /* Pointer to the last added alignment */
} printTableContent;
typedef struct _printQueryOpt typedef struct printQueryOpt
{ {
printTableOpt topt; /* the options above */ printTableOpt topt; /* the options above */
char *nullPrint; /* how to print null entities */ char *nullPrint; /* how to print null entities */
...@@ -82,15 +94,29 @@ typedef struct _printQueryOpt ...@@ -82,15 +94,29 @@ typedef struct _printQueryOpt
const bool *trans_columns; /* trans_columns[i-1] => do gettext on col i */ const bool *trans_columns; /* trans_columns[i-1] => do gettext on col i */
} printQueryOpt; } printQueryOpt;
/*
* Use this to print query results
*
* It calls the printTable above with all the things set straight.
*/
void printQuery(const PGresult *result, const printQueryOpt *opt,
FILE *fout, FILE *flog);
void setDecimalLocale(void); extern FILE *PageOutput(int lines, unsigned short int pager);
extern void ClosePager(FILE *pagerpipe);
extern void html_escaped_print(const char *in, FILE *fout);
extern void printTableInit(printTableContent *const content,
const printTableOpt *opt, const char *title,
const int ncolumns, const int nrows);
extern void printTableAddHeader(printTableContent *const content,
const char *header, const bool translate, const char align);
extern void printTableAddCell(printTableContent *const content,
const char *cell, const bool translate);
extern void printTableAddFooter(printTableContent *const content,
const char *footer);
extern void printTableSetFooter(printTableContent *const content,
const char *footer);
extern void printTableCleanup(printTableContent *const content);
extern void printTable(const printTableContent *cont, FILE *fout, FILE *flog);
extern void printQuery(const PGresult *result, const printQueryOpt *opt,
FILE *fout, FILE *flog);
extern void setDecimalLocale(void);
#ifndef __CYGWIN__ #ifndef __CYGWIN__
#define DEFAULT_PAGER "more" #define DEFAULT_PAGER "more"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.31 2008/01/01 19:45:56 momjian Exp $ * $PostgreSQL: pgsql/src/bin/scripts/common.c,v 1.32 2008/05/12 22:59:58 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -229,6 +229,27 @@ executeMaintenanceCommand(PGconn *conn, const char *query, bool echo) ...@@ -229,6 +229,27 @@ executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
return r; return r;
} }
/*
* "Safe" wrapper around strdup(). Pulled from psql/common.c
*/
char *
pg_strdup(const char *string)
{
char *tmp;
if (!string)
{
fprintf(stderr, _("pg_strdup: cannot duplicate null pointer (internal error)\n"));
exit(EXIT_FAILURE);
}
tmp = strdup(string);
if (!tmp)
{
fprintf(stderr, _("out of memory\n"));
exit(EXIT_FAILURE);
}
return tmp;
}
/* /*
* Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither. * Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither.
...@@ -274,7 +295,6 @@ yesno_prompt(const char *question) ...@@ -274,7 +295,6 @@ yesno_prompt(const char *question)
} }
} }
/* /*
* SetCancelConn * SetCancelConn
* *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment