--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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.1 1998/05/07 14:52:52 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include "libpq/pqsignal.h"
+#include "libpq-fe.h"
+#ifndef HAVE_TERMIOS_H
+#include <sys/termios.h>
+#else
+#include <termios.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(PQprintOpt *po, PGresult *res,
+ const int i, const int j, char *buf, const int fs_len,
+ char *fields[],
+ const int nFields, char *fieldNames[],
+ unsigned char fieldNotNum[], int fieldMax[],
+ const int fieldMaxLen, FILE *fout);
+static char *
+do_header(FILE *fout, PQprintOpt *po, const int nFields,
+ int fieldMax[], char *fieldNames[], unsigned char fieldNotNum[],
+ const int fs_len, PGresult *res);
+static void
+output_row(FILE *fout, 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,
+ PGresult *res,
+ 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;
+ char **fieldNames;
+ int fieldMaxLen = 0;
+ int numFieldName;
+ int fs_len = strlen(po->fieldSep);
+ int total_line_length = 0;
+ int usePipe = 0;
+ char *pagerenv;
+ char buf[8192 * 2 + 1];
+
+ nTups = PQntuples(res);
+ if (!(fieldNames = (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;
+ 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 &&
+ isatty(fileno(stdin)) &&
+ isatty(fileno(stdout)))
+ {
+ /* 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 */
+ )))
+ {
+ fout = popen(pagerenv, "w");
+ if (fout)
+ {
+ usePipe = 1;
+ pqsignal(SIGPIPE, SIG_IGN);
+ }
+ 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++)
+ {
+ 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, buf, 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)
+ {
+ pclose(fout);
+ pqsignal(SIGPIPE, SIG_DFL);
+ }
+ if (po->html3 && !po->expanded)
+ fputs("</table>\n", fout);
+ }
+}
+
+
+/*
+ * PQdisplayTuples()
+ * kept for backward compatibility
+ */
+
+void
+PQdisplayTuples(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[MAX_FIELDS];
+
+ 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;
+
+ /* Zero the initial field lengths */
+ for (j = 0; j < nFields; j++)
+ {
+ fLength[j] = strlen(PQfname(res, j));
+ }
+ /* Find the max length of each field in the result */
+ /* will be somewhat time consuming for very large results */
+ if (fillAlign)
+ {
+ for (i = 0; i < nTuples; i++)
+ {
+ for (j = 0; j < nFields; j++)
+ {
+ if (PQgetlength(res, i, j) > fLength[j])
+ fLength[j] = PQgetlength(res, i, j);
+ }
+ }
+ }
+
+ 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);
+}
+
+
+
+/*
+ * PQprintTuples()
+ *
+ * kept for backward compatibility
+ *
+ */
+void
+PQprintTuples(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++)
+ {
+ char *pval = PQgetvalue(res, i, j);
+
+ fprintf(fout, formatString,
+ TerseOutput ? "" : "|",
+ pval ? pval : "");
+ }
+ if (TerseOutput)
+ fprintf(fout, "\n");
+ else
+ fprintf(fout, "|\n%s\n", tborder);
+ }
+ }
+}
+
+
+
+static void
+do_field(PQprintOpt *po, PGresult *res,
+ const int i, const int j, char *buf, const int fs_len,
+ char *fields[],
+ const int nFields, char *fieldNames[],
+ unsigned char fieldNotNum[], int fieldMax[],
+ const int fieldMaxLen, FILE *fout)
+{
+
+ char *pval,
+ *p,
+ *o;
+ 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)
+ {
+ for (p = pval, o = buf; *p; *(o++) = *(p++))
+ {
+ if ((fs_len == 1 && (*p == *(po->fieldSep))) || *p == '\\' || *p == '\n')
+ *(o++) = '\\';
+ if (po->align && (*pval == 'E' || *pval == 'e' ||
+ !((*p >= '0' && *p <= '9') ||
+ *p == '.' ||
+ *p == 'E' ||
+ *p == 'e' ||
+ *p == ' ' ||
+ *p == '-')))
+ fieldNotNum[j] = 1;
+ }
+ *o = '\0';
+ if (!po->expanded && (po->align || po->html3))
+ {
+ int n = strlen(buf);
+
+ if (n > fieldMax[j])
+ fieldMax[j] = n;
+ if (!(fields[i * nFields + j] = (char *) malloc(n + 1)))
+ {
+ perror("malloc");
+ exit(1);
+ }
+ strcpy(fields[i * nFields + j], buf);
+ }
+ 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",
+ buf);
+ else
+ {
+ if (po->align)
+ fprintf(fout,
+ "%-*s%s %s\n",
+ fieldMaxLen - fs_len, fieldNames[j], po->fieldSep,
+ buf);
+ else
+ fprintf(fout, "%s%s%s\n", fieldNames[j], po->fieldSep, buf);
+ }
+ }
+ else
+ {
+ if (!po->html3)
+ {
+ fputs(buf, fout);
+ efield:
+ if ((j + 1) < nFields)
+ fputs(po->fieldSep, fout);
+ else
+ fputc('\n', fout);
+ }
+ }
+ }
+ }
+}
+
+
+static char *
+do_header(FILE *fout, PQprintOpt *po, const int nFields, int fieldMax[],
+ char *fieldNames[], unsigned char fieldNotNum[],
+ const int fs_len, 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++)
+ {
+ 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, 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);
+}