/*-------------------------------------------------------------------------
 *
 * fe-connect.c
 *	  functions related to setting up a connection to the backend
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.148 2000/11/17 04:22:52 ishii Exp $
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>

#include "libpq-fe.h"
#include "libpq-int.h"
#include "fe-auth.h"

#ifdef WIN32
#include "win32.h"
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#endif

#ifndef HAVE_STRDUP
#include "strdup.h"
#endif
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif

#ifdef MULTIBYTE
#include "miscadmin.h"
#include "mb/pg_wchar.h"
#endif

#ifdef WIN32
static int
inet_aton(const char *cp, struct in_addr * inp)
{
	unsigned long a = inet_addr(cp);

	if (a == -1)
		return 0;
	inp->s_addr = a;
	return 1;
}

#endif


#ifdef USE_SSL
static SSL_CTX *SSL_context = NULL;
#endif

#define NOTIFYLIST_INITIAL_SIZE 10
#define NOTIFYLIST_GROWBY 10


/* ----------
 * Definition of the conninfo parameters and their fallback resources.
 *
 * If Environment-Var and Compiled-in are specified as NULL, no
 * fallback is available. If after all no value can be determined
 * for an option, an error is returned.
 *
 * The values for dbname and user are treated specially in conninfo_parse.
 * If the Compiled-in resource is specified as a NULL value, the
 * user is determined by fe_getauthname() and for dbname the user
 * name is copied.
 *
 * The Label and Disp-Char entries are provided for applications that
 * want to use PQconndefaults() to create a generic database connection
 * dialog. Disp-Char is defined as follows:
 *		""		Normal input field
 *		"*"		Password field - hide value
 *		"D"		Debug option - don't show by default
 *
 * PQconninfoOptions[] is a constant static array that we use to initialize
 * a dynamically allocated working copy.  All the "val" fields in
 * PQconninfoOptions[] *must* be NULL.	In a working copy, non-null "val"
 * fields point to malloc'd strings that should be freed when the working
 * array is freed (see PQconninfoFree).
 * ----------
 */
static const PQconninfoOption PQconninfoOptions[] = {

	/*
	 * "authtype" is no longer used, so mark it "don't show".  We keep it
	 * in the array so as not to reject conninfo strings from old apps
	 * that might still try to set it.
	 */
	{"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
	"Database-Authtype", "D", 20},

	{"service", "PGSERVICE", NULL, NULL,
	 "Database-Service", "", 20},

	{"user", "PGUSER", NULL, NULL,
	"Database-User", "", 20},

	{"password", "PGPASSWORD", DefaultPassword, NULL,
	"Database-Password", "*", 20},

	{"dbname", "PGDATABASE", NULL, NULL,
	"Database-Name", "", 20},

	{"host", "PGHOST", NULL, NULL,
	"Database-Host", "", 40},

	{"hostaddr", "PGHOSTADDR", NULL, NULL,
	"Database-Host-IPv4-Address", "", 15},		/* Room for
												 * abc.def.ghi.jkl */

	{"port", "PGPORT", DEF_PGPORT_STR, NULL,
	"Database-Port", "", 6},

	{"unixsocket", "PGUNIXSOCKET", NULL, NULL,
	"Unix-Socket", "", 80},

	{"tty", "PGTTY", DefaultTty, NULL,
	"Backend-Debug-TTY", "D", 40},

	{"options", "PGOPTIONS", DefaultOption, NULL,
	"Backend-Debug-Options", "D", 40},

#ifdef USE_SSL
	{"requiressl", "PGREQUIRESSL", "0", NULL,
	 "Require-SSL", "", 1 },
#endif

	/* Terminating entry --- MUST BE LAST */
	{NULL, NULL, NULL, NULL,
	NULL, NULL, 0}
};

static const struct EnvironmentOptions
{
	const char *envName,
			   *pgName;
}			EnvironmentOptions[] =

{
	/* common user-interface settings */
	{
		"PGDATESTYLE", "datestyle"
	},
	{
		"PGTZ", "timezone"
	},
#ifdef MULTIBYTE
	{
		"PGCLIENTENCODING", "client_encoding"
	},
#endif
	/* internal performance-related settings */
	{
		"PGGEQO", "geqo"
	},
	{
		NULL, NULL
	}
};


static int	connectDBStart(PGconn *conn);
static int	connectDBComplete(PGconn *conn);
static bool PQsetenvStart(PGconn *conn);
static PostgresPollingStatusType PQsetenvPoll(PGconn *conn);
static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
static PQconninfoOption *conninfo_parse(const char *conninfo,
			   PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
				const char *keyword);
static void defaultNoticeProcessor(void *arg, const char *message);
static int  parseServiceInfo(PQconninfoOption *options, 
			     PQExpBuffer errorMessage);


/* ----------------
 *		Connecting to a Database
 *
 * There are now four different ways a user of this API can connect to the
 * database.  Two are not recommended for use in new code, because of their
 * lack of extensibility with respect to the passing of options to the
 * backend.  These are PQsetdb and PQsetdbLogin (the former now being a macro
 * to the latter).
 *
 * If it is desired to connect in a synchronous (blocking) manner, use the
 * function PQconnectdb.
 *
 * To connect in an asychronous (non-blocking) manner, use the functions
 * PQconnectStart, and PQconnectPoll.
 *
 * Internally, the static functions connectDBStart, connectDBComplete
 * are part of the connection procedure.
 *
 * ----------------
 */

/* ----------------
 *		PQconnectdb
 *
 * establishes a connection to a postgres backend through the postmaster
 * using connection information in a string.
 *
 * The conninfo string is a white-separated list of
 *
 *	   option = value
 *
 * definitions. Value might be a single value containing no whitespaces or
 * a single quoted string. If a single quote should appear anywhere in
 * the value, it must be escaped with a backslash like \'
 *
 * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
 * if a memory allocation failed.
 * If the status field of the connection returned is CONNECTION_BAD,
 * then some fields may be null'ed out instead of having valid values.
 *
 * You should call PQfinish (if conn is not NULL) regardless of whether this
 * call succeeded.
 *
 * ----------------
 */
PGconn *
PQconnectdb(const char *conninfo)
{
	PGconn	   *conn = PQconnectStart(conninfo);

	if (conn && conn->status != CONNECTION_BAD)
		(void) connectDBComplete(conn);

	return conn;
}

/* ----------------
 *		PQconnectStart
 *
 * Begins the establishment of a connection to a postgres backend through the
 * postmaster using connection information in a string.
 *
 * See comment for PQconnectdb for the definition of the string format.
 *
 * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
 * you should not attempt to proceed with this connection.	If the status
 * field of the connection returned is CONNECTION_BAD, an error has
 * occurred. In this case you should call PQfinish on the result, (perhaps
 * inspecting the error message first).  Other fields of the structure may not
 * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
 * this stage has succeeded - call PQconnectPoll, using select(2) to see when
 * this is necessary.
 *
 * See PQconnectPoll for more info.
 *
 * ----------------
 */
PGconn *
PQconnectStart(const char *conninfo)
{
	PGconn	   *conn;
	PQconninfoOption *connOptions;
	char	   *tmp;

	/* ----------
	 * Allocate memory for the conn structure
	 * ----------
	 */

	conn = makeEmptyPGconn();
	if (conn == NULL)
		return (PGconn *) NULL;

	/* ----------
	 * Parse the conninfo string
	 * ----------
	 */
	connOptions = conninfo_parse(conninfo, &conn->errorMessage);
	if (connOptions == NULL)
	{
		conn->status = CONNECTION_BAD;
		/* errorMessage is already set */
		return conn;
	}

	/*
	 * Move option values into conn structure
	 */
	tmp = conninfo_getval(connOptions, "hostaddr");
	conn->pghostaddr = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "host");
	conn->pghost = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "port");
	conn->pgport = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "unixsocket");
	conn->pgunixsocket = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "tty");
	conn->pgtty = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "options");
	conn->pgoptions = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "dbname");
	conn->dbName = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "user");
	conn->pguser = tmp ? strdup(tmp) : NULL;
	tmp = conninfo_getval(connOptions, "password");
	conn->pgpass = tmp ? strdup(tmp) : NULL;
#ifdef USE_SSL
	tmp = conninfo_getval(connOptions, "requiressl");
	conn->require_ssl = tmp ? (tmp[0]=='1'?true:false) : false;
#endif

	/* ----------
	 * Free the option info - all is in conn now
	 * ----------
	 */
	PQconninfoFree(connOptions);

	/* ----------
	 * Allow unix socket specification in the host name
	 * ----------
	 */
	if (conn->pghost && conn->pghost[0] == '/')
	{
		if (conn->pgunixsocket)
			free(conn->pgunixsocket);
		conn->pgunixsocket = conn->pghost;
		conn->pghost = NULL;
	}

	/* ----------
	 * Connect to the database
	 * ----------
	 */
	if (!connectDBStart(conn))
	{
		/* Just in case we failed to set it in connectDBStart */
		conn->status = CONNECTION_BAD;
	}

	return conn;
}

/* ----------------
 *		PQconndefaults
 *
 * Parse an empty string like PQconnectdb() would do and return the
 * working connection options array.
 *
 * Using this function, an application may determine all possible options
 * and their current default values.
 *
 * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated
 * and should be freed when no longer needed via PQconninfoFree().	(In prior
 * versions, the returned array was static, but that's not thread-safe.)
 * Pre-7.0 applications that use this function will see a small memory leak
 * until they are updated to call PQconninfoFree.
 * ----------------
 */
PQconninfoOption *
PQconndefaults(void)
{
	PQExpBufferData errorBuf;
	PQconninfoOption *connOptions;

	initPQExpBuffer(&errorBuf);
	connOptions = conninfo_parse("", &errorBuf);
	termPQExpBuffer(&errorBuf);
	return connOptions;
}

/* ----------------
 *		PQsetdbLogin
 *
 * establishes a connection to a postgres backend through the postmaster
 * at the specified host and port.
 *
 * returns a PGconn* which is needed for all subsequent libpq calls
 * if the status field of the connection returned is CONNECTION_BAD,
 * then some fields may be null'ed out instead of having valid values
 *
 *	Uses these environment variables:
 *
 *	  PGHOST	   identifies host to which to connect if <pghost> argument
 *				   is NULL or a null string.
 *
 *	  PGPORT	   identifies TCP port to which to connect if <pgport> argument
 *				   is NULL or a null string.
 *
 *	  PGUNIXSOCKET	   identifies Unix-domain socket to which to connect; default
 *				   is computed from the TCP port.
 *
 *	  PGTTY		   identifies tty to which to send messages if <pgtty> argument
 *				   is NULL or a null string.
 *
 *	  PGOPTIONS    identifies connection options if <pgoptions> argument is
 *				   NULL or a null string.
 *
 *	  PGUSER	   Postgres username to associate with the connection.
 *
 *	  PGPASSWORD   The user's password.
 *
 *	  PGDATABASE   name of database to which to connect if <pgdatabase>
 *				   argument is NULL or a null string
 *
 *	  None of the above need be defined.  There are defaults for all of them.
 *
 * To support "delimited identifiers" for database names, only convert
 * the database name to lower case if it is not surrounded by double quotes.
 * Otherwise, strip the double quotes but leave the reset of the string intact.
 * - thomas 1997-11-08
 *
 * ----------------
 */
PGconn *
PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
			 const char *pgtty, const char *dbName, const char *login,
			 const char *pwd)
{
	PGconn	   *conn;
	char	   *tmp;			/* An error message from some service we
								 * call. */
	bool		error = FALSE;	/* We encountered an error. */

	conn = makeEmptyPGconn();
	if (conn == NULL)
		return (PGconn *) NULL;

	if (pghost)
		conn->pghost = strdup(pghost);
	else if ((tmp = getenv("PGHOST")) != NULL)
		conn->pghost = strdup(tmp);

	if (pgport == NULL)
	{
		if ((tmp = getenv("PGPORT")) == NULL)
			tmp = DEF_PGPORT_STR;
		conn->pgport = strdup(tmp);
	}
	else
		conn->pgport = strdup(pgport);

	/* ----------
	 * We don't allow unix socket path as a function parameter.
	 * This allows unix socket specification in the host name.
	 * ----------
	 */
	if (conn->pghost && conn->pghost[0] == '/')
	{
		if (conn->pgunixsocket)
			free(conn->pgunixsocket);
		conn->pgunixsocket = conn->pghost;
		conn->pghost = NULL;
	}

	if (pgtty == NULL)
	{
		if ((tmp = getenv("PGTTY")) == NULL)
			tmp = DefaultTty;
		conn->pgtty = strdup(tmp);
	}
	else
		conn->pgtty = strdup(pgtty);

	if (pgoptions == NULL)
	{
		if ((tmp = getenv("PGOPTIONS")) == NULL)
			tmp = DefaultOption;
		conn->pgoptions = strdup(tmp);
	}
	else
		conn->pgoptions = strdup(pgoptions);

	if (login)
		conn->pguser = strdup(login);
	else if ((tmp = getenv("PGUSER")) != NULL)
		conn->pguser = strdup(tmp);
	else
	{
		/* fe-auth.c has not been fixed to support PQExpBuffers, so: */
		conn->pguser = fe_getauthname(conn->errorMessage.data);
		conn->errorMessage.len = strlen(conn->errorMessage.data);
	}

	if (conn->pguser == NULL)
	{
		error = TRUE;
		printfPQExpBuffer(&conn->errorMessage,
						  "FATAL: PQsetdbLogin(): Unable to determine a Postgres username!\n");
	}

	if (pwd)
		conn->pgpass = strdup(pwd);
	else if ((tmp = getenv("PGPASSWORD")) != NULL)
		conn->pgpass = strdup(tmp);
	else
		conn->pgpass = strdup(DefaultPassword);

	if (dbName == NULL)
	{
		if ((tmp = getenv("PGDATABASE")) != NULL)
			conn->dbName = strdup(tmp);
		else if (conn->pguser)
			conn->dbName = strdup(conn->pguser);
	}
	else
		conn->dbName = strdup(dbName);


#ifdef USE_SSL
	if ((tmp = getenv("PGREQUIRESSL")) != NULL)
		conn->require_ssl = (tmp[0]=='1')?true:false;
	else
		conn->require_ssl = 0;
#endif

	if (error)
		conn->status = CONNECTION_BAD;
	else
	{
		if (connectDBStart(conn))
			(void) connectDBComplete(conn);
	}

	return conn;
}


/*
 * update_db_info -
 * get all additional info out of dbName
 *
 */
static int
update_db_info(PGconn *conn)
{
	char	   *tmp, *tmp2,
			   *old = conn->dbName;

	if (strchr(conn->dbName, '@') != NULL)
	{
		/* old style: dbname[@server][:port] */
		tmp = strrchr(conn->dbName, ':');
		if (tmp != NULL)		/* port number given */
		{
			if (conn->pgport)
				free(conn->pgport);
			conn->pgport = strdup(tmp + 1);
			*tmp = '\0';
		}

		tmp = strrchr(conn->dbName, '@');
		if (tmp != NULL)		/* host name given */
		{
			if (conn->pghost)
				free(conn->pghost);
			conn->pghost = strdup(tmp + 1);
			*tmp = '\0';
		}

		conn->dbName = strdup(old);
		free(old);
	}
	else
	{
		int			offset;

		/*
		 * only allow protocols tcp and unix
		 */
		if (strncmp(conn->dbName, "tcp:", 4) == 0)
			offset = 4;
		else if (strncmp(conn->dbName, "unix:", 5) == 0)
			offset = 5;
		else
			return 0;

		if (strncmp(conn->dbName + offset, "postgresql://", strlen("postgresql://")) == 0)
		{

			/*
			 * new style:
			 * <tcp|unix>:postgresql://server[:port|:/unixsocket/path:][/dbname][?options]
			 */
			offset += strlen("postgresql://");

			tmp = strrchr(conn->dbName + offset, '?');
			if (tmp != NULL)	/* options given */
			{
				if (conn->pgoptions)
					free(conn->pgoptions);
				conn->pgoptions = strdup(tmp + 1);
				*tmp = '\0';
			}

			tmp = strrchr(conn->dbName + offset, '/');
			if (tmp != NULL)	/* database name given */
			{
				if (conn->dbName)
					free(conn->dbName);
				conn->dbName = strdup(tmp + 1);
				*tmp = '\0';
			}
			else
			{
				/* Why do we default only this value from the environment again?  */
				if ((tmp = getenv("PGDATABASE")) != NULL)
				{
					if (conn->dbName)
						free(conn->dbName);
					conn->dbName = strdup(tmp);
				}
				else if (conn->pguser)
				{
					if (conn->dbName)
						free(conn->dbName);
					conn->dbName = strdup(conn->pguser);
				}
			}

			tmp = strrchr(old + offset, ':');
			if (tmp != NULL)	/* port number or Unix socket path given */
			{
				*tmp = '\0';
				if ((tmp2 = strchr(tmp + 1, ':')) != NULL)
				{
					if (strncmp(old, "unix:", 5) != 0)
					{
						printfPQExpBuffer(&conn->errorMessage,
								  "connectDBStart() -- "
								  "socket name can only be specified with "
								  "non-TCP\n");
						return 1; 
					}
					*tmp2 = '\0';
					if (conn->pgunixsocket)
						free(conn->pgunixsocket);
					conn->pgunixsocket = strdup(tmp + 1);
				}
				else
				{
					if (conn->pgport)
						free(conn->pgport);
					conn->pgport = strdup(tmp + 1);
					if (conn->pgunixsocket)
						free(conn->pgunixsocket);
					conn->pgunixsocket = NULL;
				}
			}

			if (strncmp(old, "unix:", 5) == 0)
			{
				if (conn->pghost)
					free(conn->pghost);
				conn->pghost = NULL;
				if (strcmp(old + offset, "localhost") != 0)
				{
					printfPQExpBuffer(&conn->errorMessage,
									  "connectDBStart() -- "
									  "non-TCP access only possible on "
									  "localhost\n");
					return 1;
				}
			}
			else
			{
				if (conn->pghost)
					free(conn->pghost);
				conn->pghost = strdup(old + offset);
			}
			free(old);
		}
	}

	return 0;
}

/* ----------
 * connectMakeNonblocking -
 * Make a connection non-blocking.
 * Returns 1 if successful, 0 if not.
 * ----------
 */
static int
connectMakeNonblocking(PGconn *conn)
{
#ifdef WIN32
	int			on = 1;
	if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)
#elif defined(__BEOS__)
	int			on = 1;
    if (ioctl(conn->sock, FIONBIO, &on) != 0)
#else
	if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)
#endif
	{
		printfPQExpBuffer(&conn->errorMessage,
			  "connectMakeNonblocking -- fcntl() failed: errno=%d\n%s\n",
						  errno, strerror(errno));
		return 0;
	}

	return 1;
}

/* ----------
 * connectNoDelay -
 * Sets the TCP_NODELAY socket option.
 * Returns 1 if successful, 0 if not.
 * ----------
 */
static int
connectNoDelay(PGconn *conn)
{
	int			on = 1;

	if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY,
				   (char *) &on,
				   sizeof(on)) < 0)
	{
		printfPQExpBuffer(&conn->errorMessage,
				 "connectNoDelay() -- setsockopt failed: errno=%d\n%s\n",
						  errno, strerror(errno));
#ifdef WIN32
		printf("Winsock error: %i\n", WSAGetLastError());
#endif
		return 0;
	}

	return 1;
}


/* ----------
 * connectDBStart -
 * Start to make a connection to the backend so it is ready to receive
 * queries.
 * Returns 1 if successful, 0 if not.
 * ----------
 */
static int
connectDBStart(PGconn *conn)
{
	int			portno,
				family;

#ifdef USE_SSL
	StartupPacket np;			/* Used to negotiate SSL connection */
	char		SSLok;

#endif

	if (!conn)
		return 0;

	/*
	 * parse dbName to get all additional info in it, if any
	 */
	if (update_db_info(conn) != 0)
		goto connect_errReturn;

	/* Ensure our buffers are empty */
	conn->inStart = conn->inCursor = conn->inEnd = 0;
	conn->outCount = 0;

	/*
	 * Set up the connection to postmaster/backend. Note that this
	 * supports IPv4 and UDP only.
	 */

	MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));

	if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
	{
		/* Using pghostaddr avoids a hostname lookup */
		/* Note that this supports IPv4 only */
		struct in_addr addr;

		if (!inet_aton(conn->pghostaddr, &addr))
		{
			printfPQExpBuffer(&conn->errorMessage,
							  "connectDBStart() -- "
						 	  "invalid host address: %s\n", conn->pghostaddr);
			goto connect_errReturn;
		}

		family = AF_INET;

		memmove((char *) &(conn->raddr.in.sin_addr),
				(char *) &addr, sizeof(addr));
	}
	else if (conn->pghost != NULL && conn->pghost[0] != '\0')
	{
		/* Using pghost, so we have to look-up the hostname */
		struct hostent *hp;

		hp = gethostbyname(conn->pghost);
		if ((hp == NULL) || (hp->h_addrtype != AF_INET))
		{
			printfPQExpBuffer(&conn->errorMessage,
						   "connectDBStart() --  unknown hostname: %s\n",
							  conn->pghost);
			goto connect_errReturn;
		}
		family = AF_INET;

		memmove((char *) &(conn->raddr.in.sin_addr),
				(char *) hp->h_addr,
				hp->h_length);
	}
	else
	{
		/* pghostaddr and pghost are NULL, so use UDP */
		family = AF_UNIX;
	}

	/* Set family */
	conn->raddr.sa.sa_family = family;

	/* Set port number */
	portno = atoi(conn->pgport);
	if (family == AF_INET)
	{
		conn->raddr.in.sin_port = htons((unsigned short) (portno));
		conn->raddr_len = sizeof(struct sockaddr_in);
	}
#ifdef HAVE_UNIX_SOCKETS
	else
	{
		UNIXSOCK_PATH(conn->raddr.un, portno, conn->pgunixsocket);
		conn->raddr_len = UNIXSOCK_LEN(conn->raddr.un);
	}
#endif


	/* Open a socket */
	if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
	{
		printfPQExpBuffer(&conn->errorMessage,
						  "connectDBStart() -- "
						  "socket() failed: errno=%d\n%s\n",
						  errno, strerror(errno));
		goto connect_errReturn;
	}

	/* ----------
	 * Set the right options. Normally, we need nonblocking I/O, and we don't
	 * want delay of outgoing data for AF_INET sockets.  If we are using SSL,
	 * then we need the blocking I/O (XXX Can this be fixed?).
	 * ---------- */

	if (family == AF_INET)
	{
		if (!connectNoDelay(conn))
			goto connect_errReturn;
	}

	/* ----------
	 * Since I have no idea whether this is a valid thing to do under Windows
	 * before a connection is made, and since I have no way of testing it, I
	 * leave the code looking as below.  When someone decides that they want
	 * non-blocking connections under Windows, they can define
	 * WIN32_NON_BLOCKING_CONNECTIONS before compilation.  If it works, then
	 * this code can be cleaned up.
	 *
	 *	 Ewan Mellor <eem21@cam.ac.uk>.
	 * ---------- */
#if (!defined(WIN32) || defined(WIN32_NON_BLOCKING_CONNECTIONS)) && !defined(USE_SSL)
	if (connectMakeNonblocking(conn) == 0)
		goto connect_errReturn;
#endif

	/* ----------
	 * Start / make connection.  We are hopefully in non-blocking mode
	 * now, but it is possible that:
	 *	 1. Older systems will still block on connect, despite the
	 *		non-blocking flag. (Anyone know if this is true?)
	 *	 2. We are running under Windows, and aren't even trying
	 *		to be non-blocking (see above).
	 *	 3. We are using SSL.
	 * Thus, we have make arrangements for all eventualities.
	 * ----------
	 */
	if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
	{
#ifndef WIN32
		if (errno == EINPROGRESS || errno == 0)
#else
		if (WSAGetLastError() == WSAEINPROGRESS)
#endif
		{

			/*
			 * This is fine - we're in non-blocking mode, and the
			 * connection is in progress.
			 */
			conn->status = CONNECTION_STARTED;
		}
		else
		{
			/* Something's gone wrong */
			printfPQExpBuffer(&conn->errorMessage,
							  "connectDBStart() -- connect() failed: %s\n"
							  "\tIs the postmaster running%s at '%s'\n"
							  "\tand accepting connections on %s '%s'?\n",
							  strerror(errno),
							  (family == AF_INET) ? " (with -i)" : "",
							  conn->pghost ? conn->pghost : "localhost",
							  (family == AF_INET) ?
							  "TCP/IP port" : "Unix socket",
							  (family == AF_UNIX && conn->pgunixsocket) ?
							  conn->pgunixsocket : conn->pgport);
			goto connect_errReturn;
		}
	}
	else
	{
		/* We're connected already */
		conn->status = CONNECTION_MADE;
	}

#ifdef USE_SSL
	/* Attempt to negotiate SSL usage */
	if (conn->allow_ssl_try)
	{
		memset((char *) &np, 0, sizeof(np));
		np.protoVersion = htonl(NEGOTIATE_SSL_CODE);
		if (pqPacketSend(conn, (char *) &np, sizeof(StartupPacket)) != STATUS_OK)
		{
			printfPQExpBuffer(&conn->errorMessage,
					"connectDB() -- couldn't send SSL negotiation packet: errno=%d\n%s\n",
					errno, strerror(errno));
			goto connect_errReturn;
		}
		/* Now receive the postmasters response */
		if (recv(conn->sock, &SSLok, 1, 0) != 1)
		{
			printfPQExpBuffer(&conn->errorMessage, "PQconnectDB() -- couldn't read postmaster response: errno=%d\n%s\n",
					errno, strerror(errno));
			goto connect_errReturn;
		}
		if (SSLok == 'S')
		{
			if (!SSL_context)
			{
				SSL_load_error_strings();
				SSL_library_init();
				SSL_context = SSL_CTX_new(SSLv23_method());
				if (!SSL_context)
				{
					printfPQExpBuffer(&conn->errorMessage,
					  "connectDB() -- couldn't create SSL context: %s\n",
							ERR_reason_error_string(ERR_get_error()));
					goto connect_errReturn;
				}
			}
			if (!(conn->ssl = SSL_new(SSL_context)) ||
				!SSL_set_fd(conn->ssl, conn->sock) ||
				SSL_connect(conn->ssl) <= 0)
			{
				printfPQExpBuffer(&conn->errorMessage,
				"connectDB() -- couldn't establish SSL connection: %s\n",
						ERR_reason_error_string(ERR_get_error()));
				goto connect_errReturn;
			}
			/* SSL connection finished. Continue to send startup packet */
		}
		else if (SSLok == 'E')
		{
			/* Received error - probably protocol mismatch */
			if (conn->Pfdebug)
				fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");
			close(conn->sock);
			conn->allow_ssl_try = FALSE;
			return connectDBStart(conn);
		}
		else if (SSLok != 'N')
		{
			printfPQExpBuffer(&conn->errorMessage,
				   "Received invalid negotiation response.\n");
			goto connect_errReturn;
		}
	}
	if (conn->require_ssl && !conn->ssl) 
	{
		/* Require SSL, but server does not support/want it */
		printfPQExpBuffer(&conn->errorMessage,
						  "Server does not support SSL when SSL was required.\n");
		goto connect_errReturn;
	}
#endif


	/*
	 * This makes the connection non-blocking, for all those cases which
	 * forced us not to do it above.
	 */
#if (defined(WIN32) && !defined(WIN32_NON_BLOCKING_CONNECTIONS)) || defined(USE_SSL)
	if (connectMakeNonblocking(conn) == 0)
		goto connect_errReturn;
#endif

	return 1;

connect_errReturn:
	if (conn->sock >= 0)
	{
#ifdef WIN32
		closesocket(conn->sock);
#else
		close(conn->sock);
#endif
		conn->sock = -1;
	}
	conn->status = CONNECTION_BAD;

	return 0;
}


/* ----------------
 *		connectDBComplete
 *
 * Block and complete a connection.
 *
 * Returns 1 on success, 0 on failure.
 * ----------------
 */
static int
connectDBComplete(PGconn *conn)
{
	PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
	if (conn == NULL || conn->status == CONNECTION_BAD)
		return 0;

	for (;;)
	{

		/*
		 * Wait, if necessary.	Note that the initial state (just after
		 * PQconnectStart) is to wait for the socket to select for
		 * writing.
		 */
		switch (flag)
		{
			case PGRES_POLLING_ACTIVE:
				break;

			case PGRES_POLLING_OK:
				return 1;		/* success! */

			case PGRES_POLLING_READING:
				if (pqWait(1, 0, conn))
				{
					conn->status = CONNECTION_BAD;
					return 0;
				}
				break;

			case PGRES_POLLING_WRITING:
				if (pqWait(0, 1, conn))
				{
					conn->status = CONNECTION_BAD;
					return 0;
				}
				break;

			default:
				/* Just in case we failed to set it in PQconnectPoll */
				conn->status = CONNECTION_BAD;
				return 0;
		}

		/*
		 * Now try to advance the state machine.
		 */
		flag = PQconnectPoll(conn);
	}
}

/* ----------------
 *		PQconnectPoll
 *
 * Poll an asynchronous connection.
 *
 * Returns a PostgresPollingStatusType.
 * Before calling this function, use select(2) to determine when data arrive.
 *
 * You must call PQfinish whether or not this fails.
 *
 * This function and PQconnectStart are intended to allow connections to be
 * made without blocking the execution of your program on remote I/O. However,
 * there are a number of caveats:
 *
 *	 o	If you call PQtrace, ensure that the stream object into which you trace
 *		will not block.
 *	 o	If you do not supply an IP address for the remote host (i.e. you
 *		supply a host name instead) then this function will block on
 *		gethostbyname.	You will be fine if using Unix sockets (i.e. by
 *		supplying neither a host name nor a host address).
 *	 o	If your backend wants to use Kerberos authentication then you must
 *		supply both a host name and a host address, otherwise this function
 *		may block on gethostname.
 *	 o	This function will block if compiled with USE_SSL.
 *
 * ----------------
 */
PostgresPollingStatusType
PQconnectPoll(PGconn *conn)
{
	PGresult   *res;
	if (conn == NULL)
		return PGRES_POLLING_FAILED;

	/* Get the new data */
	switch (conn->status)
	{

			/*
			 * We really shouldn't have been polled in these two cases,
			 * but we can handle it.
			 */
		case CONNECTION_BAD:
			return PGRES_POLLING_FAILED;
		case CONNECTION_OK:
			return PGRES_POLLING_OK;

			/* These are reading states */
		case CONNECTION_AWAITING_RESPONSE:
		case CONNECTION_AUTH_OK:
			{
				/* Load waiting data */
				int			n = pqReadData(conn);

				if (n < 0)
					goto error_return;
				if (n == 0)
					return PGRES_POLLING_READING;

				break;
			}

			/* These are writing states, so we just proceed. */
		case CONNECTION_STARTED:
		case CONNECTION_MADE:
			break;

		case CONNECTION_SETENV:
			/* We allow PQsetenvPoll to decide whether to proceed */
			break;

		default:
			printfPQExpBuffer(&conn->errorMessage,
						 "PQconnectPoll() -- unknown connection state - "
						  "probably indicative of memory corruption!\n");
			goto error_return;
	}


keep_going:						/* We will come back to here until there
								 * is nothing left to parse. */
	switch (conn->status)
	{
		case CONNECTION_STARTED:
			{
				ACCEPT_TYPE_ARG3 laddrlen;

#ifndef WIN32
				int			optval;

#else
				char		optval;

#endif
				ACCEPT_TYPE_ARG3 optlen = sizeof(optval);

				/*
				 * Write ready, since we've made it here, so the
				 * connection has been made.
				 */

				/*
				 * Now check (using getsockopt) that there is not an error
				 * state waiting for us on the socket.
				 */

				if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
							   (char *) &optval, &optlen) == -1)
				{
					printfPQExpBuffer(&conn->errorMessage,
							   "PQconnectPoll() -- getsockopt() failed: "
									  "errno=%d\n%s\n",
									  errno, strerror(errno));
					goto error_return;
				}
				else if (optval != 0)
				{

					/*
					 * When using a nonblocking connect, we will typically
					 * see connect failures at this point, so provide a
					 * friendly error message.
					 */
					printfPQExpBuffer(&conn->errorMessage,
							  "PQconnectPoll() -- connect() failed: %s\n"
								"\tIs the postmaster running%s at '%s'\n"
							 "\tand accepting connections on %s '%s'?\n",
									  strerror(optval),
									  (conn->raddr.sa.sa_family == AF_INET) ? " (with -i)" : "",
							   conn->pghost ? conn->pghost : "localhost",
								  (conn->raddr.sa.sa_family == AF_INET) ?
									  "TCP/IP port" : "Unix socket",
							  (conn->raddr.sa.sa_family == AF_UNIX && conn->pgunixsocket) ?
									  conn->pgunixsocket : conn->pgport);
					goto error_return;
				}

				/* Fill in the client address */
				laddrlen = sizeof(conn->laddr);
				if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
				{
					printfPQExpBuffer(&conn->errorMessage,
							  "PQconnectPoll() -- getsockname() failed: "
									  "errno=%d\n%s\n",
									  errno, strerror(errno));
					goto error_return;
				}

				conn->status = CONNECTION_MADE;
				return PGRES_POLLING_WRITING;
			}

		case CONNECTION_MADE:
			{
				StartupPacket sp;

				/*
				 * Initialize the startup packet.
				 */

				MemSet((char *) &sp, 0, sizeof(StartupPacket));

				sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LIBPQ);

				strncpy(sp.user, conn->pguser, SM_USER);
				strncpy(sp.database, conn->dbName, SM_DATABASE);
				strncpy(sp.tty, conn->pgtty, SM_TTY);

				if (conn->pgoptions)
					strncpy(sp.options, conn->pgoptions, SM_OPTIONS);

				/*
				 * Send the startup packet.
				 *
				 * Theoretically, this could block, but it really shouldn't
				 * since we only got here if the socket is write-ready.
				 */

				if (pqPacketSend(conn, (char *) &sp,
								 sizeof(StartupPacket)) != STATUS_OK)
				{
					printfPQExpBuffer(&conn->errorMessage,
									  "PQconnectPoll() --  "
									  "couldn't send startup packet: "
									  "errno=%d\n%s\n",
									  errno, strerror(errno));
					goto error_return;
				}

				conn->status = CONNECTION_AWAITING_RESPONSE;
				return PGRES_POLLING_READING;
			}

			/*
			 * Handle the authentication exchange: wait for postmaster
			 * messages and respond as necessary.
			 */
		case CONNECTION_AWAITING_RESPONSE:
			{
				char		beresp;
				AuthRequest areq;

				/*
				 * Scan the message from current point (note that if we
				 * find the message is incomplete, we will return without
				 * advancing inStart, and resume here next time).
				 */
				conn->inCursor = conn->inStart;

				if (pqGetc(&beresp, conn))
				{
					/* We'll come back when there are more data */
					return PGRES_POLLING_READING;
				}

				/* Handle errors. */
				if (beresp == 'E')
				{
					if (pqGets(&conn->errorMessage, conn))
					{
						/* We'll come back when there are more data */
						return PGRES_POLLING_READING;
					}
					/* OK, we read the message; mark data consumed */
					conn->inStart = conn->inCursor;

					/*
					 * The postmaster typically won't end its message with
					 * a newline, so add one to conform to libpq
					 * conventions.
					 */
					appendPQExpBufferChar(&conn->errorMessage, '\n');
					goto error_return;
				}

				/* Otherwise it should be an authentication request. */
				if (beresp != 'R')
				{
					printfPQExpBuffer(&conn->errorMessage,
									  "PQconnectPoll() -- expected "
									  "authentication request\n");
					goto error_return;
				}

				/* Get the type of request. */
				if (pqGetInt((int *) &areq, 4, conn))
				{
					/* We'll come back when there are more data */
					return PGRES_POLLING_READING;
				}

				/* Get the password salt if there is one. */
				if (areq == AUTH_REQ_CRYPT)
				{
					if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
					{
						/* We'll come back when there are more data */
						return PGRES_POLLING_READING;
					}
				}

				/*
				 * OK, we successfully read the message; mark data
				 * consumed
				 */
				conn->inStart = conn->inCursor;

				/* Respond to the request if necessary. */

				/*
				 * Note that conn->pghost must be non-NULL if we are going
				 * to avoid the Kerberos code doing a hostname look-up.
				 */

				/*
				 * XXX fe-auth.c has not been fixed to support
				 * PQExpBuffers, so:
				 */
				if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
								conn->errorMessage.data) != STATUS_OK)
				{
					conn->errorMessage.len = strlen(conn->errorMessage.data);
					goto error_return;
				}
				conn->errorMessage.len = strlen(conn->errorMessage.data);

				/*
				 * Just make sure that any data sent by fe_sendauth is
				 * flushed out.  Although this theoretically could block,
				 * it really shouldn't since we don't send large auth
				 * responses.
				 */
				if (pqFlush(conn))
					goto error_return;

				if (areq == AUTH_REQ_OK)
				{
					/* We are done with authentication exchange */
					conn->status = CONNECTION_AUTH_OK;

					/*
					 * Set asyncStatus so that PQsetResult will think that
					 * what comes back next is the result of a query.  See
					 * below.
					 */
					conn->asyncStatus = PGASYNC_BUSY;
				}

				/* Look to see if we have more data yet. */
				goto keep_going;
			}

		case CONNECTION_AUTH_OK:
			{
				/* ----------
				 * Now we expect to hear from the backend. A ReadyForQuery
				 * message indicates that startup is successful, but we might
				 * also get an Error message indicating failure. (Notice
				 * messages indicating nonfatal warnings are also allowed by
				 * the protocol, as is a BackendKeyData message.) Easiest way
				 * to handle this is to let PQgetResult() read the messages. We
				 * just have to fake it out about the state of the connection,
				 * by setting asyncStatus = PGASYNC_BUSY (done above).
				 *----------
				 */

				if (PQisBusy(conn))
					return PGRES_POLLING_READING;

				res = PQgetResult(conn);

				/*
				 * NULL return indicating we have gone to IDLE state is
				 * expected
				 */
				if (res)
				{
					if (res->resultStatus != PGRES_FATAL_ERROR)
						printfPQExpBuffer(&conn->errorMessage,
								 "PQconnectPoll() -- unexpected message "
										  "during startup\n");

					/*
					 * if the resultStatus is FATAL, then
					 * conn->errorMessage already has a copy of the error;
					 * needn't copy it back. But add a newline if it's not
					 * there already, since postmaster error messages may
					 * not have one.
					 */
					if (conn->errorMessage.len <= 0 ||
						conn->errorMessage.data[conn->errorMessage.len - 1] != '\n')
						appendPQExpBufferChar(&conn->errorMessage, '\n');
					PQclear(res);
					goto error_return;
				}

				/*
				 * Post-connection housekeeping. Prepare to send
				 * environment variables to server.
				 */
				if (!PQsetenvStart(conn))
					goto error_return;

				conn->status = CONNECTION_SETENV;

				goto keep_going;
			}

		case CONNECTION_SETENV:

			/*
			 * We pretend that the connection is OK for the duration of
			 * these queries.
			 */
			conn->status = CONNECTION_OK;

			switch (PQsetenvPoll(conn))
			{
				case PGRES_POLLING_OK:	/* Success */
					conn->status = CONNECTION_OK;
					return PGRES_POLLING_OK;

				case PGRES_POLLING_READING:		/* Still going */
					conn->status = CONNECTION_SETENV;
					return PGRES_POLLING_READING;

				case PGRES_POLLING_WRITING:		/* Still going */
					conn->status = CONNECTION_SETENV;
					return PGRES_POLLING_WRITING;

				default:
					conn->status = CONNECTION_SETENV;
					goto error_return;
			}
			/* Unreachable */

		default:
			printfPQExpBuffer(&conn->errorMessage,
						 "PQconnectPoll() -- unknown connection state - "
						  "probably indicative of memory corruption!\n");
			goto error_return;
	}

	/* Unreachable */

error_return:
	/* ----------
	 * We used to close the socket at this point, but that makes it awkward
	 * for those above us if they wish to remove this socket from their
	 * own records (an fd_set for example).  We'll just have this socket
	 * closed when PQfinish is called (which is compulsory even after an
	 * error, since the connection structure must be freed).
	 * ----------
	 */
	return PGRES_POLLING_FAILED;
}


/* ----------------
 *		PQsetenvStart
 *
 * Starts the process of passing the values of a standard set of environment
 * variables to the backend.
 *
 * ----------------
 */
static bool
PQsetenvStart(PGconn *conn)
{
	if (conn == NULL ||
		conn->status == CONNECTION_BAD ||
		conn->setenv_state != SETENV_STATE_IDLE)
		return false;

#ifdef MULTIBYTE
	conn->setenv_state = SETENV_STATE_ENCODINGS_SEND;
#else
	conn->setenv_state = SETENV_STATE_OPTION_SEND;
#endif

	conn->next_eo = EnvironmentOptions;

	return true;
}

/* ----------------
 *		PQsetenvPoll
 *
 * Polls the process of passing the values of a standard set of environment
 * variables to the backend.
 *
 * ----------------
 */
static PostgresPollingStatusType
PQsetenvPoll(PGconn *conn)
{
	PGresult   *res;

#ifdef MULTIBYTE
	static const char envname[] = "PGCLIENTENCODING";

#endif

	if (conn == NULL || conn->status == CONNECTION_BAD)
		return PGRES_POLLING_FAILED;

	/* Check whether there are any data for us */
	switch (conn->setenv_state)
	{
			/* These are reading states */
#ifdef MULTIBYTE
		case SETENV_STATE_ENCODINGS_WAIT:
#endif
		case SETENV_STATE_OPTION_WAIT:
			{
				/* Load waiting data */
				int			n = pqReadData(conn);

				if (n < 0)
					goto error_return;
				if (n == 0)
					return PGRES_POLLING_READING;

				break;
			}

			/* These are writing states, so we just proceed. */
#ifdef MULTIBYTE
		case SETENV_STATE_ENCODINGS_SEND:
#endif
		case SETENV_STATE_OPTION_SEND:
			break;

			/* Should we raise an error if called when not active? */
		case SETENV_STATE_IDLE:
			return PGRES_POLLING_OK;

		default:
			printfPQExpBuffer(&conn->errorMessage,
							  "PQsetenvPoll() -- unknown state - "
						  "probably indicative of memory corruption!\n");
			goto error_return;
	}


keep_going:						/* We will come back to here until there
								 * is nothing left to parse. */
	switch (conn->setenv_state)
	{

#ifdef MULTIBYTE
		case SETENV_STATE_ENCODINGS_SEND:
			{
				const char *env;

				env = getenv(envname);
				if (!env || *env == '\0')
				{
					/* query server encoding if PGCLIENTENCODING
					   is not specified */
					if (!PQsendQuery(conn,
									 "select getdatabaseencoding()"))
						goto error_return;

					conn->setenv_state = SETENV_STATE_ENCODINGS_WAIT;
					return PGRES_POLLING_READING;
				}
				else
				{
					/* otherwise set client encoding in pg_conn struct */
					int encoding = pg_char_to_encoding(env);
					if (encoding < 0)
					{
						strcpy(conn->errorMessage.data,
							   "PGCLIENTENCODING has no valid encoding name.\n");
						goto error_return;
					}
					conn->client_encoding = encoding;
				}
					
			}

		case SETENV_STATE_ENCODINGS_WAIT:
			{
				if (PQisBusy(conn))
					return PGRES_POLLING_READING;

				res = PQgetResult(conn);

				if (res)
				{
					char	   *encoding;

					if (PQresultStatus(res) != PGRES_TUPLES_OK)
					{
						PQclear(res);
						goto error_return;
					}

					/* set client encoding in pg_conn struct */
					encoding = PQgetvalue(res, 0, 0);
					if (!encoding)		/* this should not happen */
						conn->client_encoding = SQL_ASCII;
					else
						conn->client_encoding = pg_char_to_encoding(encoding);
					PQclear(res);

					/*
					 * We have to keep going in order to clear up the
					 * query
					 */
					goto keep_going;
				}

				/* NULL result indicates that the query is finished */

				/* Move on to setting the environment options */
				conn->setenv_state = SETENV_STATE_OPTION_SEND;
				goto keep_going;
			}
#endif

		case SETENV_STATE_OPTION_SEND:
			{
				/* Send an Environment Option */
				char		setQuery[100];		/* note length limits in
												 * sprintf's below */

				if (conn->next_eo->envName)
				{
					const char *val;

					if ((val = getenv(conn->next_eo->envName)))
					{
						if (strcasecmp(val, "default") == 0)
							sprintf(setQuery, "SET %s = %.60s",
									conn->next_eo->pgName, val);
						else
							sprintf(setQuery, "SET %s = '%.60s'",
									conn->next_eo->pgName, val);
#ifdef CONNECTDEBUG
						printf("Use environment variable %s to send %s\n",
							   conn->next_eo->envName, setQuery);
#endif
						if (!PQsendQuery(conn, setQuery))
							goto error_return;

						conn->setenv_state = SETENV_STATE_OPTION_WAIT;
					}
					else
						conn->next_eo++;
				}
				else
				{
					/* No more options to send, so we are done. */
					conn->setenv_state = SETENV_STATE_IDLE;
				}

				goto keep_going;
			}

		case SETENV_STATE_OPTION_WAIT:
			{
				if (PQisBusy(conn))
					return PGRES_POLLING_READING;

				res = PQgetResult(conn);

				if (res)
				{
					if (PQresultStatus(res) != PGRES_COMMAND_OK)
					{
						PQclear(res);
						goto error_return;
					}
					/* Don't need the result */
					PQclear(res);

					/*
					 * We have to keep going in order to clear up the
					 * query
					 */
					goto keep_going;
				}

				/* NULL result indicates that the query is finished */

				/* Send the next option */
				conn->next_eo++;
				conn->setenv_state = SETENV_STATE_OPTION_SEND;
				goto keep_going;
			}

		case SETENV_STATE_IDLE:
			return PGRES_POLLING_OK;

		default:
			printfPQExpBuffer(&conn->errorMessage,
							  "PQsetenvPoll() -- unknown state - "
						  "probably indicative of memory corruption!\n");
			goto error_return;
	}

	/* Unreachable */

error_return:
	conn->setenv_state = SETENV_STATE_IDLE;
	return PGRES_POLLING_FAILED;
}


#ifdef NOT_USED

/* ----------------
 *		PQsetenv
 *
 * Passes the values of a standard set of environment variables to the
 * backend.
 *
 * Returns true on success, false on failure.
 *
 * This function used to be exported for no particularly good reason.
 * Since it's no longer used by libpq itself, let's try #ifdef'ing it out
 * and see if anyone complains.
 * ----------------
 */
static bool
PQsetenv(PGconn *conn)
{
	PostgresPollingStatusType flag = PGRES_POLLING_WRITING;

	if (!PQsetenvStart(conn))
		return false;

	for (;;)
	{

		/*
		 * Wait, if necessary.	Note that the initial state (just after
		 * PQsetenvStart) is to wait for the socket to select for writing.
		 */
		switch (flag)
		{
			case PGRES_POLLING_ACTIVE:
				break;

			case PGRES_POLLING_OK:
				return true;	/* success! */

			case PGRES_POLLING_READING:
				if (pqWait(1, 0, conn))
				{
					conn->status = CONNECTION_BAD;
					return false;
				}
				break;

			case PGRES_POLLING_WRITING:
				if (pqWait(0, 1, conn))
				{
					conn->status = CONNECTION_BAD;
					return false;
				}
				break;

			default:
				/* Just in case we failed to set it in PQsetenvPoll */
				conn->status = CONNECTION_BAD;
				return false;
		}

		/*
		 * Now try to advance the state machine.
		 */
		flag = PQsetenvPoll(conn);
	}
}

#endif	 /* NOT_USED */


/*
 * makeEmptyPGconn
 *	 - create a PGconn data structure with (as yet) no interesting data
 */
static PGconn *
makeEmptyPGconn(void)
{
	PGconn	   *conn = (PGconn *) malloc(sizeof(PGconn));

	if (conn == NULL)
		return conn;

	/* Zero all pointers and booleans */
	MemSet((char *) conn, 0, sizeof(PGconn));

	conn->noticeHook = defaultNoticeProcessor;
	conn->status = CONNECTION_BAD;
	conn->asyncStatus = PGASYNC_IDLE;
	conn->setenv_state = SETENV_STATE_IDLE;
	conn->notifyList = DLNewList();
	conn->sock = -1;
#ifdef USE_SSL
	conn->allow_ssl_try = TRUE;
#endif

	/*
	 * The output buffer size is set to 8K, which is the usual size of
	 * pipe buffers on Unix systems.  That way, when we are sending a
	 * large amount of data, we avoid incurring extra kernel context swaps
	 * for partial bufferloads.  Note that we currently don't ever enlarge
	 * the output buffer.
	 *
	 * With the same goal of minimizing context swaps, the input buffer will
	 * be enlarged anytime it has less than 8K free, so we initially
	 * allocate twice that.
	 */
	conn->inBufSize = 16 * 1024;
	conn->inBuffer = (char *) malloc(conn->inBufSize);
	conn->outBufSize = 8 * 1024;
	conn->outBuffer = (char *) malloc(conn->outBufSize);
	conn->nonblocking = FALSE;
	initPQExpBuffer(&conn->errorMessage);
	initPQExpBuffer(&conn->workBuffer);
	if (conn->inBuffer == NULL ||
		conn->outBuffer == NULL ||
		conn->errorMessage.data == NULL ||
		conn->workBuffer.data == NULL)
	{
		/* out of memory already :-( */
		freePGconn(conn);
		conn = NULL;
	}
	return conn;
}

/*
 * freePGconn
 *	 - free the PGconn data structure
 *
 */
static void
freePGconn(PGconn *conn)
{
	if (!conn)
		return;
	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
#ifdef USE_SSL
	if (conn->ssl)
		SSL_free(conn->ssl);
#endif
	if (conn->sock >= 0)
#ifdef WIN32
		closesocket(conn->sock);
#else
		close(conn->sock);
#endif
	if (conn->pghost)
		free(conn->pghost);
	if (conn->pghostaddr)
		free(conn->pghostaddr);
	if (conn->pgport)
		free(conn->pgport);
	if (conn->pgunixsocket)
		free(conn->pgunixsocket);
	if (conn->pgtty)
		free(conn->pgtty);
	if (conn->pgoptions)
		free(conn->pgoptions);
	if (conn->dbName)
		free(conn->dbName);
	if (conn->pguser)
		free(conn->pguser);
	if (conn->pgpass)
		free(conn->pgpass);
	/* Note that conn->Pfdebug is not ours to close or free */
	if (conn->notifyList)
		DLFreeList(conn->notifyList);
	if (conn->lobjfuncs)
		free(conn->lobjfuncs);
	if (conn->inBuffer)
		free(conn->inBuffer);
	if (conn->outBuffer)
		free(conn->outBuffer);
	termPQExpBuffer(&conn->errorMessage);
	termPQExpBuffer(&conn->workBuffer);
	free(conn);
}

/*
   closePGconn
	 - properly close a connection to the backend
*/
static void
closePGconn(PGconn *conn)
{
	if (conn->sock >= 0)
	{

		/*
		 * Try to send "close connection" message to backend. Ignore any
		 * error. Note: this routine used to go to substantial lengths to
		 * avoid getting SIGPIPE'd if the connection were already closed.
		 * Now we rely on pqFlush to avoid the signal.
		 */
		(void) pqPuts("X", conn);
		(void) pqFlush(conn);
	}

	/*
	 * must reset the blocking status so a possible reconnect will work
	 * don't call PQsetnonblocking() because it will fail if it's unable
	 * to flush the connection.
	 */
	conn->nonblocking = FALSE;

	/*
	 * Close the connection, reset all transient state, flush I/O buffers.
	 */
	if (conn->sock >= 0)
#ifdef WIN32
		closesocket(conn->sock);
#else
		close(conn->sock);
#endif
	conn->sock = -1;
	conn->status = CONNECTION_BAD;		/* Well, not really _bad_ - just
										 * absent */
	conn->asyncStatus = PGASYNC_IDLE;
	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
	if (conn->lobjfuncs)
		free(conn->lobjfuncs);
	conn->lobjfuncs = NULL;
	conn->inStart = conn->inCursor = conn->inEnd = 0;
	conn->outCount = 0;
	conn->nonblocking = FALSE;

}

/*
   PQfinish:
	  properly close a connection to the backend
	  also frees the PGconn data structure so it shouldn't be re-used
	  after this
*/
void
PQfinish(PGconn *conn)
{
	if (conn)
	{
		closePGconn(conn);
		freePGconn(conn);
	}
}

/* PQreset :
   resets the connection to the backend
   closes the existing connection and makes a new one
*/
void
PQreset(PGconn *conn)
{
	if (conn)
	{
		closePGconn(conn);

		if (connectDBStart(conn))
			(void) connectDBComplete(conn);
	}
}


/* PQresetStart :
   resets the connection to the backend
   closes the existing connection and makes a new one
   Returns 1 on success, 0 on failure.
*/
int
PQresetStart(PGconn *conn)
{
	if (conn)
	{
		closePGconn(conn);

		return connectDBStart(conn);
	}

	return 0;
}


/* PQresetPoll :
   resets the connection to the backend
   closes the existing connection and makes a new one
*/

PostgresPollingStatusType
PQresetPoll(PGconn *conn)
{
	if (conn)
		return PQconnectPoll(conn);

	return PGRES_POLLING_FAILED;
}


/*
 * PQrequestCancel: attempt to request cancellation of the current operation.
 *
 * The return value is TRUE if the cancel request was successfully
 * dispatched, FALSE if not (in which case conn->errorMessage is set).
 * Note: successful dispatch is no guarantee that there will be any effect at
 * the backend.  The application must read the operation result as usual.
 *
 * XXX it was a bad idea to have the error message returned in
 * conn->errorMessage, since it could overwrite a message already there.
 * Would be better to return it in a char array passed by the caller.
 *
 * CAUTION: we want this routine to be safely callable from a signal handler
 * (for example, an application might want to call it in a SIGINT handler).
 * This means we cannot use any C library routine that might be non-reentrant.
 * malloc/free are often non-reentrant, and anything that might call them is
 * just as dangerous.  We avoid sprintf here for that reason.  Building up
 * error messages with strcpy/strcat is tedious but should be quite safe.
 *
 * NOTE: this routine must not generate any error message longer than
 * INITIAL_EXPBUFFER_SIZE (currently 256), since we dare not try to
 * expand conn->errorMessage!
 */

int
PQrequestCancel(PGconn *conn)
{
	int			tmpsock = -1;
	struct
	{
		uint32		packetlen;
		CancelRequestPacket cp;
	}			crp;

	/* Check we have an open connection */
	if (!conn)
		return FALSE;

	if (conn->sock < 0)
	{
		strcpy(conn->errorMessage.data,
			   "PQrequestCancel() -- connection is not open\n");
		conn->errorMessage.len = strlen(conn->errorMessage.data);
		return FALSE;
	}

	/*
	 * We need to open a temporary connection to the postmaster. Use the
	 * information saved by connectDB to do this with only kernel calls.
	 */
	if ((tmpsock = socket(conn->raddr.sa.sa_family, SOCK_STREAM, 0)) < 0)
	{
		strcpy(conn->errorMessage.data,
			   "PQrequestCancel() -- socket() failed: ");
		goto cancel_errReturn;
	}
	if (connect(tmpsock, &conn->raddr.sa, conn->raddr_len) < 0)
	{
		strcpy(conn->errorMessage.data,
			   "PQrequestCancel() -- connect() failed: ");
		goto cancel_errReturn;
	}

	/*
	 * We needn't set nonblocking I/O or NODELAY options here.
	 */

	/* Create and send the cancel request packet. */

	crp.packetlen = htonl((uint32) sizeof(crp));
	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
	crp.cp.backendPID = htonl(conn->be_pid);
	crp.cp.cancelAuthCode = htonl(conn->be_key);

	if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
	{
		strcpy(conn->errorMessage.data,
			   "PQrequestCancel() -- send() failed: ");
		goto cancel_errReturn;
	}

	/* Sent it, done */
#ifdef WIN32
	closesocket(tmpsock);
#else
	close(tmpsock);
#endif

	return TRUE;

cancel_errReturn:
	strcat(conn->errorMessage.data, strerror(errno));
	strcat(conn->errorMessage.data, "\n");
	conn->errorMessage.len = strlen(conn->errorMessage.data);
	if (tmpsock >= 0)
	{
#ifdef WIN32
		closesocket(tmpsock);
#else
		close(tmpsock);
#endif
	}
	return FALSE;
}


/*
 * pqPacketSend() -- send a single-packet message.
 * this is like PacketSend(), defined in backend/libpq/pqpacket.c
 *
 * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
 * SIDE_EFFECTS: may block.
*/
int
pqPacketSend(PGconn *conn, const char *buf, size_t len)
{
	/* Send the total packet size. */

	if (pqPutInt(4 + len, 4, conn))
		return STATUS_ERROR;

	/* Send the packet itself. */

	if (pqPutnchar(buf, len, conn))
		return STATUS_ERROR;

	if (pqFlush(conn))
		return STATUS_ERROR;

	return STATUS_OK;
}



#ifndef SYSCONFDIR
# error "You must compile this file with SYSCONFDIR defined."
#endif

#define MAXBUFSIZE 256

int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) {
  char *service = conninfo_getval(options, "service");
  char *serviceFile = SYSCONFDIR "/pg_service.conf";
  int  group_found = 0;
  int  linenr=0, i;

  if(service != NULL) {
    FILE *f;
    char buf[MAXBUFSIZE], *line;
    
    f = fopen(serviceFile, "r");
    if(f == NULL) {
      printfPQExpBuffer(errorMessage, "ERROR: Service file '%s' not found\n",
			serviceFile);
      return 1;
    }

    /* As default, set the database name to the name of the service */
    for(i = 0; options[i].keyword; i++)
      if(strcmp(options[i].keyword, "dbname") == 0) {
	if(options[i].val != NULL)
	  free(options[i].val);
	options[i].val = strdup(service);
      }
    
    while((line = fgets(buf, MAXBUFSIZE-1, f)) != NULL) {
      linenr++;

      if(strlen(line) >= MAXBUFSIZE - 2) {
	fclose(f);
	printfPQExpBuffer(errorMessage,
			 "ERROR: line %d too long in service file '%s'\n",
			  linenr,
			 serviceFile);
	return 2;
      }

      /* ignore EOL at end of line */
      if(strlen(line) && line[strlen(line)-1] == '\n')
	line[strlen(line)-1] = 0;

      /* ignore leading blanks */
      while(*line && isspace(line[0]))
	line++;

      /* ignore comments and empty lines */
      if(strlen(line) == 0 || line[0] == '#')
	continue;

      /* Check for right groupname */
      if(line[0] == '[') {
	if(group_found) {
	  /* group info already read */
	  fclose(f);
	  return 0;
	}

	if(strncmp(line+1, service, strlen(service)) == 0 &&
	   line[strlen(service)+1] == ']')
	  group_found = 1;
	else
	  group_found = 0;
      } else {
	if(group_found) {
	  /* Finally, we are in the right group and can parse the line */
	  char *key, *val;
	  int found_keyword;

	  key = strtok(line, "=");
	  if(key == NULL) {
	    printfPQExpBuffer(errorMessage,
			      "ERROR: syntax error in service file '%s', line %d\n",
			      serviceFile,
			      linenr);
	    fclose(f);
	    return 3;
	  }
	  val = line + strlen(line) + 1;
	  
	  found_keyword = 0;
	  for(i = 0; options[i].keyword; i++) {
	    if(strcmp(options[i].keyword, key) == 0) {
 	      if(options[i].val != NULL)
 		free(options[i].val);
 	      options[i].val = strdup(val);
	      found_keyword = 1;
	    }
	  }

	  if(!found_keyword) {
	    printfPQExpBuffer(errorMessage,
			      "ERROR: syntax error in service file '%s', line %d\n",
			      serviceFile,
			      linenr);
	    fclose(f);
	    return 3;
	  }
	}
      }
    }

    fclose(f);
  }

  return 0;
}


/* ----------------
 * Conninfo parser routine
 *
 * If successful, a malloc'd PQconninfoOption array is returned.
 * If not successful, NULL is returned and an error message is
 * left in errorMessage.
 * ----------------
 */
static PQconninfoOption *
conninfo_parse(const char *conninfo, PQExpBuffer errorMessage)
{
	char	   *pname;
	char	   *pval;
	char	   *buf;
	char	   *tmp;
	char	   *cp;
	char	   *cp2;
	PQconninfoOption *options;
	PQconninfoOption *option;
	char		errortmp[INITIAL_EXPBUFFER_SIZE];

	/* Make a working copy of PQconninfoOptions */
	options = malloc(sizeof(PQconninfoOptions));
	if (options == NULL)
	{
		printfPQExpBuffer(errorMessage,
		"FATAL: cannot allocate memory for copy of PQconninfoOptions\n");
		return NULL;
	}
	memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));

	/* Need a modifiable copy of the input string */
	if ((buf = strdup(conninfo)) == NULL)
	{
		printfPQExpBuffer(errorMessage,
		  "FATAL: cannot allocate memory for copy of conninfo string\n");
		PQconninfoFree(options);
		return NULL;
	}
	cp = buf;

	while (*cp)
	{
		/* Skip blanks before the parameter name */
		if (isspace((int) *cp))
		{
			cp++;
			continue;
		}

		/* Get the parameter name */
		pname = cp;
		while (*cp)
		{
			if (*cp == '=')
				break;
			if (isspace((int) *cp))
			{
				*cp++ = '\0';
				while (*cp)
				{
					if (!isspace((int) *cp))
						break;
					cp++;
				}
				break;
			}
			cp++;
		}

		/* Check that there is a following '=' */
		if (*cp != '=')
		{
			printfPQExpBuffer(errorMessage,
						   "ERROR: Missing '=' after '%s' in conninfo\n",
							  pname);
			PQconninfoFree(options);
			free(buf);
			return NULL;
		}
		*cp++ = '\0';

		/* Skip blanks after the '=' */
		while (*cp)
		{
			if (!isspace((int) *cp))
				break;
			cp++;
		}

		/* Get the parameter value */
		pval = cp;

		if (*cp != '\'')
		{
			cp2 = pval;
			while (*cp)
			{
				if (isspace((int) *cp))
				{
					*cp++ = '\0';
					break;
				}
				if (*cp == '\\')
				{
					cp++;
					if (*cp != '\0')
						*cp2++ = *cp++;
				}
				else
					*cp2++ = *cp++;
			}
			*cp2 = '\0';
		}
		else
		{
			cp2 = pval;
			cp++;
			for (;;)
			{
				if (*cp == '\0')
				{
					printfPQExpBuffer(errorMessage,
									  "ERROR: PQconnectdb() - unterminated quoted string in conninfo\n");
					PQconninfoFree(options);
					free(buf);
					return NULL;
				}
				if (*cp == '\\')
				{
					cp++;
					if (*cp != '\0')
						*cp2++ = *cp++;
					continue;
				}
				if (*cp == '\'')
				{
					*cp2 = '\0';
					cp++;
					break;
				}
				*cp2++ = *cp++;
			}
		}

		/* ----------
		 * Now we have the name and the value. Search
		 * for the param record.
		 * ----------
		 */
		for (option = options; option->keyword != NULL; option++)
		{
			if (strcmp(option->keyword, pname) == 0)
				break;
		}
		if (option->keyword == NULL)
		{
			printfPQExpBuffer(errorMessage,
							  "ERROR: Unknown conninfo option '%s'\n",
							  pname);
			PQconninfoFree(options);
			free(buf);
			return NULL;
		}

		/* ----------
		 * Store the value
		 * ----------
		 */
		if (option->val)
			free(option->val);
		option->val = strdup(pval);

	}

	/* Now check for service info */	
	if(parseServiceInfo(options, errorMessage)) {
	  PQconninfoFree(options);
	  free(buf);
	  return NULL;
	}

	/* Done with the modifiable input string */
	free(buf);

	/* ----------
	 * Get the fallback resources for parameters not specified
	 * in the conninfo string.
	 * ----------
	 */
	for (option = options; option->keyword != NULL; option++)
	{
		if (option->val != NULL)
			continue;			/* Value was in conninfo */

		/* ----------
		 * Try to get the environment variable fallback
		 * ----------
		 */
		if (option->envvar != NULL)
		{
			if ((tmp = getenv(option->envvar)) != NULL)
			{
				option->val = strdup(tmp);
				continue;
			}
		}

		/* ----------
		 * No environment variable specified or this one isn't set -
		 * try compiled in
		 * ----------
		 */
		if (option->compiled != NULL)
		{
			option->val = strdup(option->compiled);
			continue;
		}

		/* ----------
		 * Special handling for user
		 * ----------
		 */
		if (strcmp(option->keyword, "user") == 0)
		{
			option->val = fe_getauthname(errortmp);
			/* note any error message is thrown away */
			continue;
		}

		/* ----------
		 * Special handling for dbname
		 * ----------
		 */
		if (strcmp(option->keyword, "dbname") == 0)
		{
			tmp = conninfo_getval(options, "user");
			if (tmp)
				option->val = strdup(tmp);
			continue;
		}
	}

	return options;
}


static char *
conninfo_getval(PQconninfoOption *connOptions,
				const char *keyword)
{
	PQconninfoOption *option;

	for (option = connOptions; option->keyword != NULL; option++)
	{
		if (strcmp(option->keyword, keyword) == 0)
			return option->val;
	}

	return NULL;
}


void
PQconninfoFree(PQconninfoOption *connOptions)
{
	PQconninfoOption *option;

	if (connOptions == NULL)
		return;

	for (option = connOptions; option->keyword != NULL; option++)
	{
		if (option->val != NULL)
			free(option->val);
	}
	free(connOptions);
}

/* =========== accessor functions for PGconn ========= */
char *
PQdb(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->dbName;
}

char *
PQuser(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pguser;
}

char *
PQpass(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pgpass;
}

char *
PQhost(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pghost;
}

char *
PQport(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pgport;
}

char *
PQunixsocket(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pgunixsocket;
}

char *
PQtty(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pgtty;
}

char *
PQoptions(const PGconn *conn)
{
	if (!conn)
		return (char *) NULL;
	return conn->pgoptions;
}

ConnStatusType
PQstatus(const PGconn *conn)
{
	if (!conn)
		return CONNECTION_BAD;
	return conn->status;
}

char *
PQerrorMessage(const PGconn *conn)
{
	if (!conn)
		return "PQerrorMessage: conn pointer is NULL\n";

	return conn->errorMessage.data;
}

int
PQsocket(const PGconn *conn)
{
	if (!conn)
		return -1;
	return conn->sock;
}

int
PQbackendPID(const PGconn *conn)
{
	if (!conn || conn->status != CONNECTION_OK)
		return 0;
	return conn->be_pid;
}

int
PQclientEncoding(const PGconn *conn)
{
	if (!conn || conn->status != CONNECTION_OK)
		return -1;
	return conn->client_encoding;
}

#ifdef MULTIBYTE
int
PQsetClientEncoding(PGconn *conn, const char *encoding)
{
	char		qbuf[128];
	static char query[] = "set client_encoding to '%s'";
	PGresult   *res;
	int			status;

	if (!conn || conn->status != CONNECTION_OK)
		return -1;

	if (!encoding)
		return -1;

	/* check query buffer overflow */
	if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
		return -1;

	/* ok, now send a query */
	sprintf(qbuf, query, encoding);
	res = PQexec(conn, qbuf);

	if (res == (PGresult *) NULL)
		return -1;
	if (res->resultStatus != PGRES_COMMAND_OK)
		status = -1;
	else
	{
		/* change libpq internal encoding */
		conn->client_encoding = pg_char_to_encoding(encoding);
		status = 0;				/* everything is ok */
	}
	PQclear(res);
	return (status);
}

#else
int
PQsetClientEncoding(PGconn *conn, const char *encoding)
{
	return -1;
}

#endif

#ifdef USE_SSL
SSL *PQgetssl(PGconn *conn)
{
	if (!conn)
		return NULL;
	return conn->ssl;
}
#endif

void
PQtrace(PGconn *conn, FILE *debug_port)
{
	if (conn == NULL ||
		conn->status == CONNECTION_BAD)
		return;
	PQuntrace(conn);
	conn->Pfdebug = debug_port;
}

void
PQuntrace(PGconn *conn)
{
	/* note: better allow untrace even when connection bad */
	if (conn == NULL)
		return;
	if (conn->Pfdebug)
	{
		fflush(conn->Pfdebug);
		conn->Pfdebug = NULL;
	}
}

PQnoticeProcessor
PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
{
	PQnoticeProcessor old;

	if (conn == NULL)
		return NULL;

	old = conn->noticeHook;
	if (proc)
	{
		conn->noticeHook = proc;
		conn->noticeArg = arg;
	}
	return old;
}

/*
 * The default notice/error message processor just prints the
 * message on stderr.  Applications can override this if they
 * want the messages to go elsewhere (a window, for example).
 * Note that simply discarding notices is probably a bad idea.
 */

static void
defaultNoticeProcessor(void *arg, const char *message)
{
	(void) arg;					/* not used */
	/* Note: we expect the supplied string to end with a newline already. */
	fprintf(stderr, "%s", message);
}

#ifdef MULTIBYTE
/*
 * convert an encoding string to encoding symbol value.
 * case is ignored.
 * if there's no valid encoding, returns -1
 */

typedef struct {
	int encoding;	/* encoding symbol value */
	char *name;	/* encoding string */
} PQ_encoding_conv_tbl;

static PQ_encoding_conv_tbl pq_conv_tbl[] = {
		{SQL_ASCII, "SQL_ASCII"},
		{EUC_JP, "EUC_JP"},
		{EUC_CN, "EUC_CN"},
		{EUC_KR, "EUC_KR"},
		{EUC_TW, "EUC_TW"},
		{UNICODE, "UNICODE"},
		{MULE_INTERNAL, "MULE_INTERNAL"},
		{LATIN1, "LATIN1"},
		{LATIN2, "LATIN2"},
		{LATIN3, "LATIN3"},
		{LATIN4, "LATIN4"},
		{LATIN5, "LATIN5"},
		{KOI8, "KOI8"},
		{WIN, "WIN"},
		{ALT, "ALT"},
		{SJIS, "SJIS"},
		{BIG5, "BIG5"},
		{WIN1250, "WIN1250"},
		{-1, ""}
};

int
pg_char_to_encoding(const char *s)
{
	PQ_encoding_conv_tbl *p = pq_conv_tbl;

	if (!s)
		return (-1);

	for (; p->encoding >= 0; p++)
	{
		if (!strcasecmp(s, p->name))
			break;
	}
	return (p->encoding);
}

/*
 * convert encoding symbol to encoding char.
 * if there's no valid encoding symbol, returns ""
 */
const char *
pg_encoding_to_char(int encoding)
{
	PQ_encoding_conv_tbl *p = pq_conv_tbl;

	for (; p->encoding >= 0; p++)
	{
		if (p->encoding == encoding)
			return (p->name);
	}
	return ("");
}
#endif
