text: /*-------------------------------------------------------------------------
   *
   * fe-print.c
   *	  functions for pretty-printing query results
   *
   * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
   * Portions 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
   *	  src/interfaces/libpq/fe-print.c
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres_fe.h"
  
  #include <signal.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
  
  #include "libpq-fe.h"
  #include "libpq-int.h"
  #include "pqsignal.h"
  
  
  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
   *
   * This function should probably be removed sometime since psql
   * doesn't use it anymore. It is unclear to what extent this is used
   * by external clients, however.
   */
  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;
  		char	   *pagerenv;
  
  #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
  		sigset_t	osigset;
  		bool		sigpipe_masked = false;
  		bool		sigpipe_pending;
  #endif
  #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
  		pqsigfunc	oldsigpipehandler = NULL;
  #endif
  
  #ifdef TIOCGWINSZ
  		struct winsize screen_size;
  #else
  		struct winsize
  		{
  			int			ws_row;
  			int			ws_col;
  		}			screen_size;
  #endif
  
  		nTups = PQntuples(res);
  		if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
  		{
  			fprintf(stderr, libpq_gettext("out of memory\n"));
  			exit(1);
  		}
  		if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
  		{
  			fprintf(stderr, libpq_gettext("out of memory\n"));
  			exit(1);
  		}
  		if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
  		{
  			fprintf(stderr, libpq_gettext("out of memory\n"));
  			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 && isatty(fileno(stdin)) &&
  			isatty(fileno(stdout)))
  		{
  			/*
  			 * If we think there'll be more than one screen of output, try to
  			 * pipe to the pager program.
  			 */
  #ifdef TIOCGWINSZ
  			if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
  				screen_size.ws_col == 0 ||
  				screen_size.ws_row == 0)
  			{
  				screen_size.ws_row = 24;
  				screen_size.ws_col = 80;
  			}
  #else
  			screen_size.ws_row = 24;
  			screen_size.ws_col = 80;
  #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;
  #ifndef WIN32
  #ifdef ENABLE_THREAD_SAFETY
  					if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
  						sigpipe_masked = true;
  #else
  					oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
  #endif   /* ENABLE_THREAD_SAFETY */
  #endif   /* WIN32 */
  				}
  				else
  					fout = stdout;
  			}
  		}
  
  		if (!po->expanded && (po->align || po->html3))
  		{
  			if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
  			{
  				fprintf(stderr, libpq_gettext("out of memory\n"));
  				exit(1);
  			}
  		}
  		else if (po->header && !po->html3)
  		{
  			if (po->expanded)
  			{
  				if (po->align)
  					fprintf(fout, libpq_gettext("%-*s%s Value\n"),
  							fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
  				else
  					fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("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, "<center><h2>%s</h2></center>\n", po->caption);
  			else
  				fprintf(fout,
  						"<center><h2>"
  						"Query retrieved %d rows * %d fields"
  						"</h2></center>\n",
  						nTups, nFields);
  		}
  		for (i = 0; i < nTups; i++)
  		{
  			if (po->expanded)
  			{
  				if (po->html3)
  					fprintf(fout,
  							"<table %s><caption align=\"top\">%d</caption>\n",
  							po->tableOpt ? po->tableOpt : "", i);
  				else
  					fprintf(fout, libpq_gettext("-- 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=\"top\">%s</caption>\n",
  								po->tableOpt ? po->tableOpt : "",
  								po->caption);
  					else
  						fprintf(fout,
  								"<table %s><caption align=\"top\">"
  								"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((void *) fieldNames);
  		if (usePipe)
  		{
  #ifdef WIN32
  			_pclose(fout);
  #else
  			pclose(fout);
  
  #ifdef ENABLE_THREAD_SAFETY
  			/* we can't easily verify if EPIPE occurred, so say it did */
  			if (sigpipe_masked)
  				pq_reset_sigpipe(&osigset, sigpipe_pending, true);
  #else
  			pqsignal(SIGPIPE, oldsigpipehandler);
  #endif   /* ENABLE_THREAD_SAFETY */
  #endif   /* WIN32 */
  		}
  		if (po->html3 && !po->expanded)
  			fputs("</table>\n", fout);
  	}
  }
  
  
  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';
  
  			for (p = pval; *p; p += PQmblen(p, res->client_encoding))
  			{
  				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)))
  			{
  				fprintf(stderr, libpq_gettext("out of memory\n"));
  				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			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)
  		{
  			fprintf(stderr, libpq_gettext("out of memory\n"));
  			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);
  }
  
  
  
  /*
   * really old printing routines
   */
  
  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));
  		if (!fLength)
  		{
  			fprintf(stderr, libpq_gettext("out of memory\n"));
  			exit(1);
  		}
  
  		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);
  }
  
  
  
  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 = (char *) malloc(width + 1);
  			if (!tborder)
  			{
  				fprintf(stderr, libpq_gettext("out of memory\n"));
  				exit(1);
  			}
  			for (i = 0; i < width; i++)
  				tborder[i] = '-';
  			tborder[width] = '\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);
  		}
  	}
  
  	if (tborder)
  		free(tborder);
  }
  
  
  /* 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);
  }
  