1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00
Files
postgres/src/interfaces/libpq/fe-print.c
Bruce Momjian 0cb6bc70ce Hi!
Here is a patch to bring both libpq and psql to a state where it compiles on
win32 (native) again. A lot of things have changed, and I have not been able
to keep up with them all, so it has been broken for quite a while.
After this patch, at least it compiles. It also talks "basic talk" to the
server, but I have not yet tested all things. Sending queries, and using
e.g. \d or \dt works fine. The rest will have to be tested further.
It also bumps the version on libpq.dll to 7.0.

Everything should be enclosed in #ifdef WIN32, unless I have missed
something. Except for one or maybe two places where I have moved a #include
that should not be used on win32 from the "global area" into a "#ifndef
WIN32 area".


//Magnus
2000-01-18 19:05:31 +00:00

797 lines
16 KiB
C

/*-------------------------------------------------------------------------
*
* fe-print.c
* functions for pretty-printing query results
*
* Copyright (c) 1994, Regents of the University of California
*
* These functions were formerly part of fe-exec.c, but they
* didn't really belong there.
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.30 2000/01/18 19:05:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <signal.h>
#include "postgres.h"
#include "libpq-fe.h"
#include "libpq-int.h"
#include "pqsignal.h"
#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#include <sys/ioctl.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#else
#ifndef WIN32
#include <sys/termios.h>
#endif
#endif
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif
#ifdef TIOCGWINSZ
static struct winsize screen_size;
#else
static struct winsize
{
int ws_row;
int ws_col;
} screen_size;
#endif
static void do_field(const PQprintOpt *po, const PGresult *res,
const int i, const int j, const int fs_len,
char **fields,
const int nFields, const char **fieldNames,
unsigned char *fieldNotNum, int *fieldMax,
const int fieldMaxLen, FILE *fout);
static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
const int fs_len, const PGresult *res);
static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
unsigned char *fieldNotNum, int *fieldMax, char *border,
const int row_index);
static void fill(int length, int max, char filler, FILE *fp);
/*
* PQprint()
*
* Format results of a query for printing.
*
* PQprintOpt is a typedef (structure) that containes
* various flags and options. consult libpq-fe.h for
* details
*
* Obsoletes PQprintTuples.
*/
void
PQprint(FILE *fout,
const PGresult *res,
const PQprintOpt *po)
{
int nFields;
nFields = PQnfields(res);
if (nFields > 0)
{ /* only print rows with at least 1 field. */
int i,
j;
int nTups;
int *fieldMax = NULL; /* in case we don't use them */
unsigned char *fieldNotNum = NULL;
char *border = NULL;
char **fields = NULL;
const char **fieldNames;
int fieldMaxLen = 0;
int numFieldName;
int fs_len = strlen(po->fieldSep);
int total_line_length = 0;
int usePipe = 0;
pqsigfunc oldsigpipehandler = NULL;
char *pagerenv;
nTups = PQntuples(res);
if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
{
perror("calloc");
exit(1);
}
if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
{
perror("calloc");
exit(1);
}
if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
{
perror("calloc");
exit(1);
}
for (numFieldName = 0;
po->fieldName && po->fieldName[numFieldName];
numFieldName++)
;
for (j = 0; j < nFields; j++)
{
int len;
const char *s = (j < numFieldName && po->fieldName[j][0]) ?
po->fieldName[j] : PQfname(res, j);
fieldNames[j] = s;
len = s ? strlen(s) : 0;
fieldMax[j] = len;
len += fs_len;
if (len > fieldMaxLen)
fieldMaxLen = len;
total_line_length += len;
}
total_line_length += nFields * strlen(po->fieldSep) + 1;
if (fout == NULL)
fout = stdout;
if (po->pager && fout == stdout
#ifndef WIN32
&&
isatty(fileno(stdin)) &&
isatty(fileno(stdout))
#endif
)
{
/* try to pipe to the pager program if possible */
#ifdef TIOCGWINSZ
if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
screen_size.ws_col == 0 ||
screen_size.ws_row == 0)
{
#endif
screen_size.ws_row = 24;
screen_size.ws_col = 80;
#ifdef TIOCGWINSZ
}
#endif
pagerenv = getenv("PAGER");
if (pagerenv != NULL &&
pagerenv[0] != '\0' &&
!po->html3 &&
((po->expanded &&
nTups * (nFields + 1) >= screen_size.ws_row) ||
(!po->expanded &&
nTups * (total_line_length / screen_size.ws_col + 1) *
(1 + (po->standard != 0)) >= screen_size.ws_row -
(po->header != 0) *
(total_line_length / screen_size.ws_col + 1) * 2
- (po->header != 0) * 2 /* row count and newline */
)))
{
#ifdef WIN32
fout = _popen(pagerenv, "w");
#else
fout = popen(pagerenv, "w");
#endif
if (fout)
{
usePipe = 1;
#ifndef WIN32
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
#endif
}
else
fout = stdout;
}
}
if (!po->expanded && (po->align || po->html3))
{
if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
{
perror("calloc");
exit(1);
}
}
else if (po->header && !po->html3)
{
if (po->expanded)
{
if (po->align)
fprintf(fout, "%-*s%s Value\n",
fieldMaxLen - fs_len, "Field", po->fieldSep);
else
fprintf(fout, "%s%sValue\n", "Field", po->fieldSep);
}
else
{
int len = 0;
for (j = 0; j < nFields; j++)
{
const char *s = fieldNames[j];
fputs(s, fout);
len += strlen(s) + fs_len;
if ((j + 1) < nFields)
fputs(po->fieldSep, fout);
}
fputc('\n', fout);
for (len -= fs_len; len--; fputc('-', fout));
fputc('\n', fout);
}
}
if (po->expanded && po->html3)
{
if (po->caption)
fprintf(fout, "<centre><h2>%s</h2></centre>\n", po->caption);
else
fprintf(fout,
"<centre><h2>"
"Query retrieved %d rows * %d fields"
"</h2></centre>\n",
nTups, nFields);
}
for (i = 0; i < nTups; i++)
{
if (po->expanded)
{
if (po->html3)
fprintf(fout,
"<table %s><caption align=high>%d</caption>\n",
po->tableOpt ? po->tableOpt : "", i);
else
fprintf(fout, "-- RECORD %d --\n", i);
}
for (j = 0; j < nFields; j++)
do_field(po, res, i, j, fs_len, fields, nFields,
fieldNames, fieldNotNum,
fieldMax, fieldMaxLen, fout);
if (po->html3 && po->expanded)
fputs("</table>\n", fout);
}
if (!po->expanded && (po->align || po->html3))
{
if (po->html3)
{
if (po->header)
{
if (po->caption)
fprintf(fout,
"<table %s><caption align=high>%s</caption>\n",
po->tableOpt ? po->tableOpt : "",
po->caption);
else
fprintf(fout,
"<table %s><caption align=high>"
"Retrieved %d rows * %d fields"
"</caption>\n",
po->tableOpt ? po->tableOpt : "", nTups, nFields);
}
else
fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
}
if (po->header)
border = do_header(fout, po, nFields, fieldMax, fieldNames,
fieldNotNum, fs_len, res);
for (i = 0; i < nTups; i++)
output_row(fout, po, nFields, fields,
fieldNotNum, fieldMax, border, i);
free(fields);
if (border)
free(border);
}
if (po->header && !po->html3)
fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
(PQntuples(res) == 1) ? "" : "s");
free(fieldMax);
free(fieldNotNum);
free(fieldNames);
if (usePipe)
{
#ifdef WIN32
_pclose(fout);
#else
pclose(fout);
pqsignal(SIGPIPE, oldsigpipehandler);
#endif
}
if (po->html3 && !po->expanded)
fputs("</table>\n", fout);
}
}
/*
* PQdisplayTuples()
* kept for backward compatibility
*/
void
PQdisplayTuples(const PGresult *res,
FILE *fp, /* where to send the output */
int fillAlign, /* pad the fields with spaces */
const char *fieldSep, /* field separator */
int printHeader,/* display headers? */
int quiet
)
{
#define DEFAULT_FIELD_SEP " "
int i,
j;
int nFields;
int nTuples;
int *fLength = NULL;
if (fieldSep == NULL)
fieldSep = DEFAULT_FIELD_SEP;
/* Get some useful info about the results */
nFields = PQnfields(res);
nTuples = PQntuples(res);
if (fp == NULL)
fp = stdout;
/* Figure the field lengths to align to */
/* will be somewhat time consuming for very large results */
if (fillAlign)
{
fLength = (int *) malloc(nFields * sizeof(int));
for (j = 0; j < nFields; j++)
{
fLength[j] = strlen(PQfname(res, j));
for (i = 0; i < nTuples; i++)
{
int flen = PQgetlength(res, i, j);
if (flen > fLength[j])
fLength[j] = flen;
}
}
}
if (printHeader)
{
/* first, print out the attribute names */
for (i = 0; i < nFields; i++)
{
fputs(PQfname(res, i), fp);
if (fillAlign)
fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
fputs(fieldSep, fp);
}
fprintf(fp, "\n");
/* Underline the attribute names */
for (i = 0; i < nFields; i++)
{
if (fillAlign)
fill(0, fLength[i], '-', fp);
fputs(fieldSep, fp);
}
fprintf(fp, "\n");
}
/* next, print out the instances */
for (i = 0; i < nTuples; i++)
{
for (j = 0; j < nFields; j++)
{
fprintf(fp, "%s", PQgetvalue(res, i, j));
if (fillAlign)
fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
fputs(fieldSep, fp);
}
fprintf(fp, "\n");
}
if (!quiet)
fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
(PQntuples(res) == 1) ? "" : "s");
fflush(fp);
if (fLength)
free(fLength);
}
/*
* PQprintTuples()
*
* kept for backward compatibility
*
*/
void
PQprintTuples(const PGresult *res,
FILE *fout, /* output stream */
int PrintAttNames,/* print attribute names or not */
int TerseOutput, /* delimiter bars or not? */
int colWidth /* width of column, if 0, use variable
* width */
)
{
int nFields;
int nTups;
int i,
j;
char formatString[80];
char *tborder = NULL;
nFields = PQnfields(res);
nTups = PQntuples(res);
if (colWidth > 0)
sprintf(formatString, "%%s %%-%ds", colWidth);
else
sprintf(formatString, "%%s %%s");
if (nFields > 0)
{ /* only print rows with at least 1 field. */
if (!TerseOutput)
{
int width;
width = nFields * 14;
tborder = malloc(width + 1);
for (i = 0; i <= width; i++)
tborder[i] = '-';
tborder[i] = '\0';
fprintf(fout, "%s\n", tborder);
}
for (i = 0; i < nFields; i++)
{
if (PrintAttNames)
{
fprintf(fout, formatString,
TerseOutput ? "" : "|",
PQfname(res, i));
}
}
if (PrintAttNames)
{
if (TerseOutput)
fprintf(fout, "\n");
else
fprintf(fout, "|\n%s\n", tborder);
}
for (i = 0; i < nTups; i++)
{
for (j = 0; j < nFields; j++)
{
const char *pval = PQgetvalue(res, i, j);
fprintf(fout, formatString,
TerseOutput ? "" : "|",
pval ? pval : "");
}
if (TerseOutput)
fprintf(fout, "\n");
else
fprintf(fout, "|\n%s\n", tborder);
}
}
}
#ifdef MULTIBYTE
/*
* returns the byte length of the word beginning s.
* Client side encoding is determined by the environment variable
* "PGCLIENTENCODING".
* if this variable is not defined, the same encoding as
* the backend is assumed.
*/
int
PQmblen(const unsigned char *s, int encoding)
{
return (pg_encoding_mblen(encoding, s));
}
/*
* Get encoding id from environment variable PGCLIENTENCODING.
*/
int
PQenv2encoding(void)
{
char *str;
int encoding = SQL_ASCII;
str = getenv("PGCLIENTENCODING");
if (str && *str != '\0')
encoding = pg_char_to_encoding(str);
return(encoding);
}
#else
/* Provide a default definition in case someone calls it anyway */
int
PQmblen(const unsigned char *s, int encoding)
{
return 1;
}
int
PQenv2encoding(void)
{
return 0;
}
#endif /* MULTIBYTE */
static void
do_field(const PQprintOpt *po, const PGresult *res,
const int i, const int j, const int fs_len,
char **fields,
const int nFields, char const **fieldNames,
unsigned char *fieldNotNum, int *fieldMax,
const int fieldMaxLen, FILE *fout)
{
const char *pval,
*p;
int plen;
bool skipit;
plen = PQgetlength(res, i, j);
pval = PQgetvalue(res, i, j);
if (plen < 1 || !pval || !*pval)
{
if (po->align || po->expanded)
skipit = true;
else
{
skipit = false;
goto efield;
}
}
else
skipit = false;
if (!skipit)
{
if (po->align && ! fieldNotNum[j])
{
/* Detect whether field contains non-numeric data */
char ch = '0';
#ifdef MULTIBYTE
for (p = pval; *p; p += PQmblen(p, PQclientencoding(res->conn)))
#else
for (p = pval; *p; p++)
#endif
{
ch = *p;
if (! ((ch >= '0' && ch <= '9') ||
ch == '.' ||
ch == 'E' ||
ch == 'e' ||
ch == ' ' ||
ch == '-'))
{
fieldNotNum[j] = 1;
break;
}
}
/*
* Above loop will believe E in first column is numeric; also, we
* insist on a digit in the last column for a numeric. This test
* is still not bulletproof but it handles most cases.
*/
if (*pval == 'E' || *pval == 'e' ||
!(ch >= '0' && ch <= '9'))
fieldNotNum[j] = 1;
}
if (!po->expanded && (po->align || po->html3))
{
if (plen > fieldMax[j])
fieldMax[j] = plen;
if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
{
perror("malloc");
exit(1);
}
strcpy(fields[i * nFields + j], pval);
}
else
{
if (po->expanded)
{
if (po->html3)
fprintf(fout,
"<tr><td align=left><b>%s</b></td>"
"<td align=%s>%s</td></tr>\n",
fieldNames[j],
fieldNotNum[j] ? "left" : "right",
pval);
else
{
if (po->align)
fprintf(fout,
"%-*s%s %s\n",
fieldMaxLen - fs_len, fieldNames[j],
po->fieldSep,
pval);
else
fprintf(fout,
"%s%s%s\n",
fieldNames[j], po->fieldSep, pval);
}
}
else
{
if (!po->html3)
{
fputs(pval, fout);
efield:
if ((j + 1) < nFields)
fputs(po->fieldSep, fout);
else
fputc('\n', fout);
}
}
}
}
}
static char *
do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
const char **fieldNames, unsigned char *fieldNotNum,
const int fs_len, const PGresult *res)
{
int j; /* for loop index */
char *border = NULL;
if (po->html3)
fputs("<tr>", fout);
else
{
int j; /* for loop index */
int tot = 0;
int n = 0;
char *p = NULL;
for (; n < nFields; n++)
tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
if (po->standard)
tot += fs_len * 2 + 2;
border = malloc(tot + 1);
if (!border)
{
perror("malloc");
exit(1);
}
p = border;
if (po->standard)
{
char *fs = po->fieldSep;
while (*fs++)
*p++ = '+';
}
for (j = 0; j < nFields; j++)
{
int len;
for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
if (po->standard || (j + 1) < nFields)
{
char *fs = po->fieldSep;
while (*fs++)
*p++ = '+';
}
}
*p = '\0';
if (po->standard)
fprintf(fout, "%s\n", border);
}
if (po->standard)
fputs(po->fieldSep, fout);
for (j = 0; j < nFields; j++)
{
const char *s = PQfname(res, j);
if (po->html3)
{
fprintf(fout, "<th align=%s>%s</th>",
fieldNotNum[j] ? "left" : "right", fieldNames[j]);
}
else
{
int n = strlen(s);
if (n > fieldMax[j])
fieldMax[j] = n;
if (po->standard)
fprintf(fout,
fieldNotNum[j] ? " %-*s " : " %*s ",
fieldMax[j], s);
else
fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
if (po->standard || (j + 1) < nFields)
fputs(po->fieldSep, fout);
}
}
if (po->html3)
fputs("</tr>\n", fout);
else
fprintf(fout, "\n%s\n", border);
return border;
}
static void
output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
unsigned char *fieldNotNum, int *fieldMax, char *border,
const int row_index)
{
int field_index; /* for loop index */
if (po->html3)
fputs("<tr>", fout);
else if (po->standard)
fputs(po->fieldSep, fout);
for (field_index = 0; field_index < nFields; field_index++)
{
char *p = fields[row_index * nFields + field_index];
if (po->html3)
fprintf(fout, "<td align=%s>%s</td>",
fieldNotNum[field_index] ? "left" : "right", p ? p : "");
else
{
fprintf(fout,
fieldNotNum[field_index] ?
(po->standard ? " %-*s " : "%-*s") :
(po->standard ? " %*s " : "%*s"),
fieldMax[field_index],
p ? p : "");
if (po->standard || field_index + 1 < nFields)
fputs(po->fieldSep, fout);
}
if (p)
free(p);
}
if (po->html3)
fputs("</tr>", fout);
else if (po->standard)
fprintf(fout, "\n%s", border);
fputc('\n', fout);
}
/* simply send out max-length number of filler characters to fp */
static void
fill(int length, int max, char filler, FILE *fp)
{
int count;
count = max - length;
while (count-- >= 0)
putc(filler, fp);
}