diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 00000000..91114bf2 --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,2 @@ +^.*\.Rproj$ +^\.Rproj\.user$ diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..807ea251 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.Rproj.user +.Rhistory +.RData diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 00000000..7c7c6b6a --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,14 @@ +Package: rpg +Version: 0.1 +Title: R interface to the PostgreSQL database system +Author: Joe Conway, Dirk Eddelbuettel, Tomoaki Nishiyama, Sameer Kumar + Prayaga (during 2008), Neil Tiffin +Maintainer: Tomoaki Nishiyama +Description: Database interface and PostgreSQL driver for R This + package provides a Database Interface (DBI) compliant driver + for R to access PostgreSQL database systems. +LazyLoad: true +Depends: R (>= 2.9.0), methods, DBI (>= 0.1-4) +License: GPL-2 | file LICENSE +Copyright: Authors listed above, PostgreSQL Global Development Group, + and The Regents of the University of California diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..03b0de4e --- /dev/null +++ b/LICENSE @@ -0,0 +1,34 @@ +This package as a whole is distributed under GPL-2 (GNU GENERAL PUBLIC LICENSE +version 2). See the file COPYING in the top level of the R directory +tree for further details. + +The files under src/libpq/ and libpq.dll are distributed under the +PostgreSQL License (see below). That is, if you take only that part out of the +package you may redistribute only under the restriction of +PostgreSQL License. Most of this package are distributed under GPL-2 +and if you redistribute any part of this, you must follow GPL-2. + +PostgreSQL License: +PostgreSQL Database Management System +(formerly known as Postgres, then as Postgres95) + +Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS +DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 00000000..e69de29b diff --git a/config.guess b/config.guess new file mode 100755 index 00000000..88e3544d --- /dev/null +++ b/config.guess @@ -0,0 +1,1519 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011 Free Software Foundation, Inc. + +timestamp='2011-10-19' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + hexagon:Linux:*:*) + echo hexagon-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.status b/config.status new file mode 100755 index 00000000..a61f3d52 --- /dev/null +++ b/config.status @@ -0,0 +1,864 @@ +#! /bin/sh +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=${CONFIG_SHELL-/bin/sh} +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by RPostgreSQL $as_me 0.2, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +# Files that config.status was made for. +config_files=" src/Makevars" + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." + +ac_cs_version="\ +RPostgreSQL config.status 0.2 +configured by ./configure, generated by GNU Autoconf 2.63, + with options \"\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='/Users/hadley/Documents/databases/rpg' +srcdir='.' +test -n "$AWK" || AWK=awk +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +if $ac_cs_recheck; then + set X '/bin/sh' './configure' $ac_configure_extra_args --no-create --no-recursion + shift + $as_echo "running CONFIG_SHELL=/bin/sh $*" >&6 + CONFIG_SHELL='/bin/sh' + export CONFIG_SHELL + exec "$@" +fi + +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=' ' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +cat >>"$tmp/subs1.awk" <<\_ACAWK && +S["LTLIBOBJS"]="" +S["LIBOBJS"]="" +S["ENABLE_LIBPQ"]="" +S["PKG_LIBS"]="-L/usr/local/Cellar/postgresql/9.3.0/lib -lpq" +S["PKG_CPPFLAGS"]="-I/usr/local/Cellar/postgresql/9.3.0/include" +S["R_OS_TYPE"]="darwin" +S["PG_CONFIG"]="/usr/local/bin/pg_config" +S["target_os"]="darwin14.0.0" +S["target_vendor"]="apple" +S["target_cpu"]="x86_64" +S["target"]="x86_64-apple-darwin14.0.0" +S["host_os"]="darwin14.0.0" +S["host_vendor"]="apple" +S["host_cpu"]="x86_64" +S["host"]="x86_64-apple-darwin14.0.0" +S["build_os"]="darwin14.0.0" +S["build_vendor"]="apple" +S["build_cpu"]="x86_64" +S["build"]="x86_64-apple-darwin14.0.0" +S["OBJEXT"]="o" +S["EXEEXT"]="" +S["ac_ct_CC"]="gcc" +S["CPPFLAGS"]="" +S["LDFLAGS"]="" +S["CFLAGS"]="-g -O2" +S["CC"]="gcc" +S["target_alias"]="" +S["host_alias"]="" +S["build_alias"]="" +S["LIBS"]="" +S["ECHO_T"]="" +S["ECHO_N"]="" +S["ECHO_C"]="\\c" +S["DEFS"]="-DPACKAGE_NAME=\\\"RPostgreSQL\\\" -DPACKAGE_TARNAME=\\\"rpostgresql\\\" -DPACKAGE_VERSION=\\\"0.2\\\" -DPACKAGE_STRING=\\\"RPostgreSQL\\ 0.2\\\" -DPACKAGE_BUGREPORT"\ +"=\\\"\\\"" +S["mandir"]="${datarootdir}/man" +S["localedir"]="${datarootdir}/locale" +S["libdir"]="${exec_prefix}/lib" +S["psdir"]="${docdir}" +S["pdfdir"]="${docdir}" +S["dvidir"]="${docdir}" +S["htmldir"]="${docdir}" +S["infodir"]="${datarootdir}/info" +S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}" +S["oldincludedir"]="/usr/include" +S["includedir"]="${prefix}/include" +S["localstatedir"]="${prefix}/var" +S["sharedstatedir"]="${prefix}/com" +S["sysconfdir"]="${prefix}/etc" +S["datadir"]="${datarootdir}" +S["datarootdir"]="${prefix}/share" +S["libexecdir"]="${exec_prefix}/libexec" +S["sbindir"]="${exec_prefix}/sbin" +S["bindir"]="${exec_prefix}/bin" +S["program_transform_name"]="s,x,x," +S["prefix"]="/usr/local" +S["exec_prefix"]="${prefix}" +S["PACKAGE_BUGREPORT"]="" +S["PACKAGE_STRING"]="RPostgreSQL 0.2" +S["PACKAGE_VERSION"]="0.2" +S["PACKAGE_TARNAME"]="rpostgresql" +S["PACKAGE_NAME"]="RPostgreSQL" +S["PATH_SEPARATOR"]=":" +S["SHELL"]="/bin/sh" +_ACAWK +cat >>"$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + ac_datarootdir_hack=' + s&@datadir@&${datarootdir}&g + s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g + s&@infodir@&${datarootdir}/info&g + s&@localedir@&${datarootdir}/locale&g + s&@mandir@&${datarootdir}/man&g + s&\${datarootdir}&${prefix}/share&g' ;; +esac +ac_sed_extra="/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +} + +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } diff --git a/config.sub b/config.sub new file mode 100644 index 00000000..71713b86 --- /dev/null +++ b/config.sub @@ -0,0 +1,1767 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011 Free Software Foundation, Inc. + +timestamp='2011-10-19' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 \ + | ns16k | ns32k \ + | open8 \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | picochip) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure b/configure new file mode 100755 index 00000000..930e3ddc --- /dev/null +++ b/configure @@ -0,0 +1,4147 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.63 for RPostgreSQL 0.2. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell bug-autoconf@gnu.org about your system, + echo including any error possibly output before this message. + echo This can help us improve future autoconf versions. + echo Configuration will now proceed without shell functions. +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME='RPostgreSQL' +PACKAGE_TARNAME='rpostgresql' +PACKAGE_VERSION='0.2' +PACKAGE_STRING='RPostgreSQL 0.2' +PACKAGE_BUGREPORT='' + +ac_subst_vars='LTLIBOBJS +LIBOBJS +ENABLE_LIBPQ +PKG_LIBS +PKG_CPPFLAGS +R_OS_TYPE +PG_CONFIG +target_os +target_vendor +target_cpu +target +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid feature name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid package name: $ac_useropt" >&2 + { (exit 1); exit 1; }; } + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { $as_echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { $as_echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { $as_echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) { $as_echo "$as_me: error: unrecognized options: $ac_unrecognized_opts" >&2 + { (exit 1); exit 1; }; } ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { $as_echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { $as_echo "$as_me: error: working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { $as_echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { $as_echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { $as_echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures RPostgreSQL 0.2 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/rpostgresql] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of RPostgreSQL 0.2:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +RPostgreSQL configure 0.2 +generated by GNU Autoconf 2.63 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by RPostgreSQL $as_me 0.2, which was +generated by GNU Autoconf 2.63. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + ac_site_file1=$CONFIG_SITE +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test -r "$ac_site_file"; then + { $as_echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { $as_echo "$as_me:$LINENO: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:$LINENO: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:$LINENO: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:$LINENO: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:$LINENO: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { $as_echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +$as_echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Checks for common programs using default macros +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:$LINENO: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + +# Provide some information about the compiler. +$as_echo "$as_me:$LINENO: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ $as_echo "$as_me:$LINENO: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +if test -z "$ac_file"; then + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } + fi + fi +fi +{ $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +{ $as_echo "$as_me:$LINENO: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +{ $as_echo "$as_me:$LINENO: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest$ac_cv_exeext +{ $as_echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ $as_echo "$as_me:$LINENO: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:$LINENO: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:$LINENO: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { $as_echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +$as_echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + { { $as_echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 +$as_echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} + { (exit 1); exit 1; }; } + +{ $as_echo "$as_me:$LINENO: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if test "${ac_cv_build+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + { { $as_echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +$as_echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 +$as_echo "$as_me: error: invalid value of canonical build" >&2;} + { (exit 1); exit 1; }; };; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:$LINENO: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if test "${ac_cv_host+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 +$as_echo "$as_me: error: invalid value of canonical host" >&2;} + { (exit 1); exit 1; }; };; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:$LINENO: checking target system type" >&5 +$as_echo_n "checking target system type... " >&6; } +if test "${ac_cv_target+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + { { $as_echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&5 +$as_echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +$as_echo "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) { { $as_echo "$as_me:$LINENO: error: invalid value of canonical target" >&5 +$as_echo "$as_me: error: invalid value of canonical target" >&2;} + { (exit 1); exit 1; }; };; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- +case "${host_os}" in + darwin*) + R_OS_TYPE="darwin" + ;; +esac + + +# Check for non-standard programs: pg_config(1) to configure PostgreSQL builds +# Extract the first word of "pg_config", so it can be a program name with args. +set dummy pg_config; ac_word=$2 +{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_PG_CONFIG+set}" = set; then + $as_echo_n "(cached) " >&6 +else + case $PG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +PG_CONFIG=$ac_cv_path_PG_CONFIG +if test -n "$PG_CONFIG"; then + { $as_echo "$as_me:$LINENO: result: $PG_CONFIG" >&5 +$as_echo "$PG_CONFIG" >&6; } +else + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# By default, we will not use the accompanied libpq +ENABLE_LIBPQ= + +# If pg_config was found, let's use it +if test "${PG_CONFIG}" != ""; then + + # Use pg_config for header and linker arguments + PG_INCDIR=`${PG_CONFIG} --includedir` + PG_LIBDIR=`${PG_CONFIG} --libdir` + +else + + # let's look around -- code copied from RdbuiPgSQL but modified to use test -f + # added fink standard install location Neil 8/30/2009 + { $as_echo "$as_me:$LINENO: checking for PostgreSQL header files" >&5 +$as_echo "$as_me: checking for PostgreSQL header files" >&6;} + if ! test $PG_INCDIR + then + for dir in \ + /usr/include \ + /usr/include/pgsql \ + /usr/include/postgresql \ + /usr/local/include \ + /usr/local/include/pgsql \ + /usr/local/include/postgresql \ + /usr/local/pgsql/include \ + /usr/local/postgresql/include \ + /opt/include \ + /opt/include/pgsql \ + /opt/include/postgresql \ + /opt/local/include \ + /opt/local/include/postgresql \ + /opt/local/include/postgresql84 \ + /sw/opt/postgresql-8.4/include \ + /Library/PostgresPlus/8.4SS/include \ + /sw/include/postgresql + do + { $as_echo "$as_me:$LINENO: Checking include ${dir}." >&5 +$as_echo "$as_me: Checking include ${dir}." >&6;} + if test -f ${dir}/libpq-fe.h + then + PG_INCDIR=${dir} + break + fi + done + fi + + # likewise, let's look around for libpq.so + if ! test $PG_LIBDIR + then + for dir in \ + /usr/lib \ + /usr/lib/pgsql \ + /usr/lib/postgresql \ + /usr/local/lib \ + /usr/local/lib/pgsql \ + /usr/local/lib/postgresql \ + /usr/local/pgsql/lib \ + /usr/local/postgresql/lib \ + /opt/lib \ + /opt/lib/pgsql \ + /opt/lib/postgresql \ + /opt/local/lib \ + /opt/local/lib/postgresql \ + /opt/local/lib/postgresql84 \ + /sw/opt/postgresql-8.4/lib \ + /Library/PostgresPlus/8.4SS/lib \ + /sw/lib + do + { $as_echo "$as_me:$LINENO: Checking lib ${dir}." >&5 +$as_echo "$as_me: Checking lib ${dir}." >&6;} + if test -f ${dir}/libpq.so + then + PG_LIBDIR=${dir} + break + fi + if test -f ${dir}/libpq.dylib + then + PG_LIBDIR=${dir} + break + fi + done + fi + if ! test $PG_LIBDIR + then + if test "$R_OS_TYPE" = "darwin" ; then +# in case we cannot find any libpq library we will use the accompanied libpq +# This content would be written into src/Makevars at the end of this script + ENABLE_LIBPQ=' + +PKG_CPPFLAGS=-Ilibpq +PKG_LIBS=libpq/libpq.a + +.PHONY: all +all: $(SHLIB) +$(SHLIB): libpq/libpq.5.dylib + +libpq/libpq.5.dylib: + (cd libpq; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" -f Makefile.darwin) + +clean: + (cd libpq; $(MAKE) -f Makefile.darwin clean) +' + fi + fi +fi + +# Expand into arguments +PKG_CPPFLAGS="-I${PG_INCDIR}" +PKG_LIBS="-L${PG_LIBDIR} -lpq" +# Test for sanity by looking for libpq-fe.h, no explicit action on found, error on failure +as_ac_File=`$as_echo "ac_cv_file_"${PG_INCDIR}/libpq-fe.h"" | $as_tr_sh` +{ $as_echo "$as_me:$LINENO: checking for \"${PG_INCDIR}/libpq-fe.h\"" >&5 +$as_echo_n "checking for \"${PG_INCDIR}/libpq-fe.h\"... " >&6; } +if { as_var=$as_ac_File; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + test "$cross_compiling" = yes && + { { $as_echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5 +$as_echo "$as_me: error: cannot check for file existence when cross compiling" >&2;} + { (exit 1); exit 1; }; } +if test -r ""${PG_INCDIR}/libpq-fe.h""; then + eval "$as_ac_File=yes" +else + eval "$as_ac_File=no" +fi +fi +ac_res=`eval 'as_val=${'$as_ac_File'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +as_val=`eval 'as_val=${'$as_ac_File'} + $as_echo "$as_val"'` + + +# Now substitute these two variable in src/Makevars.in to create src/Makevars + + + + +ac_config_files="$ac_config_files src/Makevars" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:$LINENO: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:$LINENO: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +if (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by RPostgreSQL $as_me 0.2, which was +generated by GNU Autoconf 2.63. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTION]... [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_version="\\ +RPostgreSQL config.status 0.2 +configured by $0, generated by GNU Autoconf 2.63, + with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + CONFIG_FILES="$CONFIG_FILES '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { $as_echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/Makevars") CONFIG_FILES="$CONFIG_FILES src/Makevars" ;; + + *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +$as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + $as_echo "$as_me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=' ' +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + { { $as_echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\).*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\).*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || { { $as_echo "$as_me:$LINENO: error: could not setup config files machinery" >&5 +$as_echo "$as_me: error: could not setup config files machinery" >&2;} + { (exit 1); exit 1; }; } +_ACEOF + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { $as_echo "$as_me:$LINENO: error: invalid tag $ac_tag" >&5 +$as_echo "$as_me: error: invalid tag $ac_tag" >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { $as_echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +$as_echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + ac_file_inputs="$ac_file_inputs '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:$LINENO: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { $as_echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +$as_echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || { { $as_echo "$as_me:$LINENO: error: could not create $ac_file" >&5 +$as_echo "$as_me: error: could not create $ac_file" >&2;} + { (exit 1); exit 1; }; } + ;; + + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + { { $as_echo "$as_me:$LINENO: error: write failure creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: error: write failure creating $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:$LINENO: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..096333f5 --- /dev/null +++ b/configure.in @@ -0,0 +1,143 @@ +# Process this file with autoconf to produce a configure script. +# +# Configure.in for RPostgreSQL +# Copyright (C) 2008 Dirk Eddelbuettel and licensed under GNU GPL +# +# This file draws heavily on configure.in files from littler, RMySQL, and RdbiPgSQL + +# Set the name and version -- the version set here will propagate to other files from here +AC_INIT(RPostgreSQL, 0.2) + +# Checks for common programs using default macros +AC_PROG_CC + +AC_CANONICAL_HOST +AC_CANONICAL_TARGET +case "${host_os}" in + darwin*) + R_OS_TYPE="darwin" + ;; +esac + + +# Check for non-standard programs: pg_config(1) to configure PostgreSQL builds +AC_PATH_PROG([PG_CONFIG], [pg_config]) + +# By default, we will not use the accompanied libpq +ENABLE_LIBPQ= + +# If pg_config was found, let's use it +if test "${PG_CONFIG}" != ""; then + + # Use pg_config for header and linker arguments + PG_INCDIR=`${PG_CONFIG} --includedir` + PG_LIBDIR=`${PG_CONFIG} --libdir` + +else + + # let's look around -- code copied from RdbuiPgSQL but modified to use test -f + # added fink standard install location Neil 8/30/2009 + AC_MSG_NOTICE([checking for PostgreSQL header files]) + if ! test $PG_INCDIR + then + for dir in \ + /usr/include \ + /usr/include/pgsql \ + /usr/include/postgresql \ + /usr/local/include \ + /usr/local/include/pgsql \ + /usr/local/include/postgresql \ + /usr/local/pgsql/include \ + /usr/local/postgresql/include \ + /opt/include \ + /opt/include/pgsql \ + /opt/include/postgresql \ + /opt/local/include \ + /opt/local/include/postgresql \ + /opt/local/include/postgresql84 \ + /sw/opt/postgresql-8.4/include \ + /Library/PostgresPlus/8.4SS/include \ + /sw/include/postgresql + do + AC_MSG_NOTICE([Checking include ${dir}.]) + if test -f ${dir}/libpq-fe.h + then + PG_INCDIR=${dir} + break + fi + done + fi + + # likewise, let's look around for libpq.so + if ! test $PG_LIBDIR + then + for dir in \ + /usr/lib \ + /usr/lib/pgsql \ + /usr/lib/postgresql \ + /usr/local/lib \ + /usr/local/lib/pgsql \ + /usr/local/lib/postgresql \ + /usr/local/pgsql/lib \ + /usr/local/postgresql/lib \ + /opt/lib \ + /opt/lib/pgsql \ + /opt/lib/postgresql \ + /opt/local/lib \ + /opt/local/lib/postgresql \ + /opt/local/lib/postgresql84 \ + /sw/opt/postgresql-8.4/lib \ + /Library/PostgresPlus/8.4SS/lib \ + /sw/lib + do + AC_MSG_NOTICE([Checking lib ${dir}.]) + if test -f ${dir}/libpq.so + then + PG_LIBDIR=${dir} + break + fi + if test -f ${dir}/libpq.dylib + then + PG_LIBDIR=${dir} + break + fi + done + fi + if ! test $PG_LIBDIR + then + if test "$R_OS_TYPE" = "darwin" ; then +# in case we cannot find any libpq library we will use the accompanied libpq +# This content would be written into src/Makevars at the end of this script + ENABLE_LIBPQ=' + +PKG_CPPFLAGS=-Ilibpq +PKG_LIBS=libpq/libpq.a + +.PHONY: all +all: $(SHLIB) +$(SHLIB): libpq/libpq.5.dylib + +libpq/libpq.5.dylib: + (cd libpq; $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" -f Makefile.darwin) + +clean: + (cd libpq; $(MAKE) -f Makefile.darwin clean) +' + fi + fi +fi + +# Expand into arguments +PKG_CPPFLAGS="-I${PG_INCDIR}" +PKG_LIBS="-L${PG_LIBDIR} -lpq" +# Test for sanity by looking for libpq-fe.h, no explicit action on found, error on failure +AC_CHECK_FILE(["${PG_INCDIR}/libpq-fe.h"], + , + AC_SUBST(R_OS_TYPE)) + +# Now substitute these two variable in src/Makevars.in to create src/Makevars +AC_SUBST(PKG_CPPFLAGS) +AC_SUBST(PKG_LIBS) + +AC_SUBST(ENABLE_LIBPQ) +AC_OUTPUT(src/Makevars) diff --git a/configure.win b/configure.win new file mode 100644 index 00000000..e69de29b diff --git a/install-sh b/install-sh new file mode 100644 index 00000000..e69de29b diff --git a/man/dbApply-methods.Rd b/man/dbApply-methods.Rd new file mode 100644 index 00000000..bffcb421 --- /dev/null +++ b/man/dbApply-methods.Rd @@ -0,0 +1,42 @@ +% $Id: dbApply-methods.Rd,v 0.1 2008/08/10 18:04:01 psk Exp $ +\name{dbApply-methods} +\docType{methods} +\alias{dbApply-methods} +\alias{dbApply,PostgreSQLResult-method} +\title{Apply R/S-Plus functions to remote groups of DBMS rows (experimental)} +\description{ + Applies R/S-Plus functions to groups of remote DBMS rows without + bringing an entire result set all at once. The result set + is expected to be sorted by the grouping field. +} +\section{Methods}{\describe{ + \item{res}{a PostgreSQL result set (see \code{\link[DBI]{dbSendQuery}}).} + \item{...}{any additional arguments to be passed to \code{FUN}.} +} +} +\references{ + See the Database Interface definition document + \code{DBI.pdf} in the base directory of this package + or + \url{http://stat.bell-labs.com/RS-DBI}. +} +\seealso{ + \code{\link{PostgreSQL}} + \code{\link{postgresqlDBApply}} + \code{\link[DBI]{dbSendQuery}} + \code{\link[DBI]{fetch}} +} + +\examples{\dontrun{ +## compute quanitiles for each network agent +con <- dbConnect(PostgreSQL(), user="user", password="passwd",dbname="dbname") +rs <- dbSendQuery(con, + "select Agent, ip_addr, DATA from pseudo_data order by Agent") +out <- dbApply(rs, INDEX = "Agent", + FUN = function(x, grp) quantile(x$DATA, names=FALSE)) +} +} +\keyword{programming} +\keyword{interface} +\keyword{database} +% vim: syntax=tex diff --git a/rpg.Rproj b/rpg.Rproj new file mode 100644 index 00000000..cba1b6b7 --- /dev/null +++ b/rpg.Rproj @@ -0,0 +1,21 @@ +Version: 1.0 + +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace diff --git a/src/Makevars b/src/Makevars new file mode 100644 index 00000000..2f0aaf31 --- /dev/null +++ b/src/Makevars @@ -0,0 +1,5 @@ +PKG_CPPFLAGS=-I/usr/local/Cellar/postgresql/9.3.0/include +PKG_LIBS=-L/usr/local/Cellar/postgresql/9.3.0/lib -lpq +R_OS_TYPE=darwin + + diff --git a/src/Makevars.in b/src/Makevars.in new file mode 100644 index 00000000..2ea537ff --- /dev/null +++ b/src/Makevars.in @@ -0,0 +1,5 @@ +PKG_CPPFLAGS=@PKG_CPPFLAGS@ +PKG_LIBS=@PKG_LIBS@ +R_OS_TYPE=@R_OS_TYPE@ + +@ENABLE_LIBPQ@ diff --git a/src/Makevars.win b/src/Makevars.win new file mode 100644 index 00000000..117b2719 --- /dev/null +++ b/src/Makevars.win @@ -0,0 +1,11 @@ +PKG_CPPFLAGS=-I./libpq +PKG_LIBS=libpq/libpq.a -lshfolder -lwsock32 -lws2_32 -lsecur32 + +.PHONY: all +all: $(SHLIB) +$(SHLIB): libpq/libpq.a + +export CC + +libpq/libpq.a: + (cd libpq; make -f Makefile.win) diff --git a/src/libpq/COPYRIGHT b/src/libpq/COPYRIGHT new file mode 100644 index 00000000..4babbf33 --- /dev/null +++ b/src/libpq/COPYRIGHT @@ -0,0 +1,23 @@ +PostgreSQL Database Management System +(formerly known as Postgres, then as Postgres95) + +Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS +DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/src/libpq/Makefile.darwin b/src/libpq/Makefile.darwin new file mode 100644 index 00000000..3e34a221 --- /dev/null +++ b/src/libpq/Makefile.darwin @@ -0,0 +1,126 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/interfaces/libpq library +# +# Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/interfaces/libpq/Makefile +# +#------------------------------------------------------------------------- + +top_builddir = .. +include Makefile.global.darwin + +all: pg_config.h pg_config_os.h +pg_config.h: pg_config.h.darwin + cp pg_config.h.darwin pg_config.h +pg_config_os.h: pg_config_os.h.darwin + cp pg_config_os.h.darwin pg_config_os.h +pg_config_paths.h: pg_config_paths.h.darwin + cp pg_config_paths.h.darwin pg_config_paths.h + +# shared library parameters +NAME= pq +SO_MAJOR_VERSION= 5 +SO_MINOR_VERSION= 4 + +override CPPFLAGS := -DFRONTEND -DUNSAFE_STAT_OK -I$(srcdir) $(CPPFLAGS) +ifneq ($(PORTNAME), win32) +override CFLAGS += $(PTHREAD_CFLAGS) +endif + +# Need to recompile any external C files because we need +# all object files to use the same compile flags as libpq; some +# platforms require special flags. +LIBS := $(LIBS:-lpgport=) + +# We can't use Makefile variables here because the MSVC build system scrapes +# OBJS from this file. +OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ + fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \ + libpq-events.o +# libpgport C files we always use +OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o thread.o +# libpgport C files that are needed if identified by configure +OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o win32setlocale.o, $(LIBOBJS)) +# backend/libpq +OBJS += ip.o md5.o +# utils/mb +OBJS += encnames.o wchar.o + +ifeq ($(PORTNAME), cygwin) +override shlib = cyg$(NAME)$(DLSUFFIX) +endif + +ifeq ($(PORTNAME), win32) +# pgsleep.o is from libpgport +OBJS += pgsleep.o win32.o libpqrc.o + +libpqrc.o: libpq.rc + $(WINDRES) -i $< -o $@ + +ifeq ($(enable_thread_safety), yes) +OBJS += pthread-win32.o +endif +endif + + +# Add libraries that libpq depends (or might depend) on into the +# shared library link. (The order in which you list them here doesn't +# matter.) +ifneq ($(PORTNAME), win32) +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS) +else +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE) +endif +ifeq ($(PORTNAME), win32) +SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS)) +endif + +SHLIB_EXPORTS = exports.txt + +all: all-lib + +# Shared library stuff +include Makefile.shlib + +distprep: libpq-dist.rc + +libpq.rc libpq-dist.rc: libpq.rc.in + sed -e 's/\(VERSION.*\),0 *$$/\1,'`date '+%y%j' | sed 's/^0*//'`'/' $< >$@ + +# Depend on Makefile.global to force rebuild on re-run of configure. +# (But libpq-dist.rc is shipped in the distribution for shell-less +# installations and is only updated by distprep.) + +fe-connect.o: fe-connect.c pg_config_paths.h + + +install: all installdirs install-lib + $(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)' + $(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)' + $(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)' + $(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)' + $(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample' + +installdirs: installdirs-lib + $(MKDIR_P) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)' + +uninstall: uninstall-lib + rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' + rm -f '$(DESTDIR)$(includedir)/libpq-events.h' + rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h' + rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' + rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample' + +clean distclean: clean-lib + rm -f $(OBJS) pthread.h libpq.rc + rm -f libpq.dylib libpq.$(SO_MAJOR_VERSION).dylib libpq.$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION).dylib +# Might be left over from a Win32 client-only build + rm -f pg_config.h + rm -f pg_config_os.h + rm -f pg_config_paths.h + +maintainer-clean: distclean maintainer-clean-lib + rm -f libpq-dist.rc diff --git a/src/libpq/Makefile.global.darwin b/src/libpq/Makefile.global.darwin new file mode 100644 index 00000000..833cfaf5 --- /dev/null +++ b/src/libpq/Makefile.global.darwin @@ -0,0 +1,741 @@ +# -*-makefile-*- +# src/Makefile.global.in + +#------------------------------------------------------------------------------ +# All PostgreSQL makefiles include this file and use the variables it sets, +# which in turn are put here by the configure script. There is no need for +# users to edit this file -- if it turns out to be necessary then that's a +# bug. +# +# A makefile that includes this file needs to set the variable `subdir' to +# the relative path from the top to itself and `top_builddir' to the relative +# path from itself to the top before including this file. (The "top" is the +# parent directory of the directory this file is in.) +#------------------------------------------------------------------------------ + + +########################################################################## +# +# Meta configuration + +standard_targets = all install installdirs uninstall distprep clean distclean maintainer-clean coverage check installcheck maintainer-check +# these targets should recurse even into subdirectories not being built: +standard_always_targets = distprep clean distclean maintainer-clean + +.PHONY: $(standard_targets) install-strip html man installcheck-parallel + +# make `all' the default target +all: + +# Delete target files if the command fails after it has +# started to update the file. +.DELETE_ON_ERROR: + +# PostgreSQL version number +VERSION = 9.1.1 +MAJORVERSION = 9.1 + +# Support for VPATH builds +vpath_build = no + +ifneq ($(vpath_build),yes) +top_srcdir = $(top_builddir) +srcdir = . +else # vpath_build = yes +top_srcdir = $(abs_top_srcdir) +srcdir = $(top_srcdir)/$(subdir) + +endif + +vpathsearch = `for f in $(addsuffix /$(1),$(subst :, ,. $(VPATH))); do test -r $$f && echo $$f && break; done` + +# Saved arguments from configure +configure_args = + + +########################################################################## +# +# Installation directories +# +# These are set by the equivalent --xxxdir configure options. We +# append "postgresql" to some of them, if the string does not already +# contain "pgsql" or "postgres", in order to avoid directory clutter. +# +# In a PGXS build, we cannot use the values inserted into Makefile.global +# by configure, since the installation tree may have been relocated. +# Instead get the path values from pg_config. + +ifndef PGXS + +# Note that prefix, exec_prefix, and datarootdir aren't defined in a PGXS build; +# makefiles may only use the derived variables such as bindir. + +prefix := /usr/local/pgsql +exec_prefix := ${prefix} +datarootdir := ${prefix}/share + +bindir := ${exec_prefix}/bin + +datadir := ${datarootdir} +ifeq "$(findstring pgsql, $(datadir))" "" +ifeq "$(findstring postgres, $(datadir))" "" +override datadir := $(datadir)/postgresql +endif +endif + +sysconfdir := ${prefix}/etc +ifeq "$(findstring pgsql, $(sysconfdir))" "" +ifeq "$(findstring postgres, $(sysconfdir))" "" +override sysconfdir := $(sysconfdir)/postgresql +endif +endif + +libdir := ${exec_prefix}/lib + +pkglibdir = $(libdir) +ifeq "$(findstring pgsql, $(pkglibdir))" "" +ifeq "$(findstring postgres, $(pkglibdir))" "" +override pkglibdir := $(pkglibdir)/postgresql +endif +endif + +includedir := ${prefix}/include + +pkgincludedir = $(includedir) +ifeq "$(findstring pgsql, $(pkgincludedir))" "" +ifeq "$(findstring postgres, $(pkgincludedir))" "" +override pkgincludedir := $(pkgincludedir)/postgresql +endif +endif + +mandir := ${datarootdir}/man + +docdir := ${datarootdir}/doc/${PACKAGE_TARNAME} +ifeq "$(findstring pgsql, $(docdir))" "" +ifeq "$(findstring postgres, $(docdir))" "" +override docdir := $(docdir)/postgresql +endif +endif + +htmldir := ${docdir} + +localedir := ${datarootdir}/locale + +else # PGXS case + +# Extension makefiles should set PG_CONFIG, but older ones might not +ifndef PG_CONFIG +PG_CONFIG = pg_config +endif + +bindir := $(shell $(PG_CONFIG) --bindir) +datadir := $(shell $(PG_CONFIG) --sharedir) +sysconfdir := $(shell $(PG_CONFIG) --sysconfdir) +libdir := $(shell $(PG_CONFIG) --libdir) +pkglibdir := $(shell $(PG_CONFIG) --pkglibdir) +includedir := $(shell $(PG_CONFIG) --includedir) +pkgincludedir := $(shell $(PG_CONFIG) --pkgincludedir) +mandir := $(shell $(PG_CONFIG) --mandir) +docdir := $(shell $(PG_CONFIG) --docdir) +localedir := $(shell $(PG_CONFIG) --localedir) + +endif # PGXS + +# These derived path variables aren't separately configurable. + +includedir_server = $(pkgincludedir)/server +includedir_internal = $(pkgincludedir)/internal +pgxsdir = $(pkglibdir)/pgxs + + +########################################################################## +# +# Features +# +# Records the choice of the various --enable-xxx and --with-xxx options. + +with_perl = no +with_python = no +with_tcl = no +with_openssl = no +with_ossp_uuid = no +with_selinux = no +with_libxml = no +with_libxslt = no +with_system_tzdata = +with_zlib = yes +enable_shared = yes +enable_rpath = yes +enable_nls = no +enable_debug = no +enable_dtrace = no +enable_coverage = no +enable_thread_safety = yes + +python_includespec = +python_libdir = +python_libspec = +python_additional_libs = +python_configdir = +python_majorversion = +python_version = + +krb_srvtab = + +TCLSH = +TCL_LIB_FILE = +TCL_LIBS = +TCL_LIB_SPEC = +TCL_INCLUDE_SPEC = +TCL_SHARED_BUILD = +TCL_SHLIB_LD_LIBS = + +PTHREAD_CFLAGS = -Kthread -kthread -pthread -pthreads -D_REENTRANT -D_THREAD_SAFE -D_POSIX_PTHREAD_SEMANTICS +PTHREAD_LIBS = -lpthread + + +########################################################################## +# +# Programs and flags + +# Compilers + +CPP = gcc -E +CPPFLAGS = + +ifdef PGXS +override CPPFLAGS := -I$(includedir_server) -I$(includedir_internal) $(CPPFLAGS) +else # not PGXS +override CPPFLAGS := -I$(top_srcdir)/src/include $(CPPFLAGS) +ifdef VPATH +override CPPFLAGS := -I$(top_builddir)/src/include $(CPPFLAGS) +endif +endif # not PGXS + +CC = gcc +GCC = yes +SUN_STUDIO_CC = no +CFLAGS = -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv + +# Kind-of compilers + +BISON = /usr/bin/bison +BISONFLAGS = $(YFLAGS) +FLEX = /usr/bin/flex +FLEXFLAGS = $(LFLAGS) +DTRACE = +DTRACEFLAGS = +ZIC = + +# Linking + +AR = ar +DLLTOOL = +DLLWRAP = +LIBS = -lz -lreadline -lm +LDAP_LIBS_FE = +LDAP_LIBS_BE = +OSSP_UUID_LIBS = +#LD = /usr/libexec/gcc/i686-apple-darwin10/4.2.1/ld +with_gnu_ld = no +ld_R_works = + +# We want -L for libpgport.a to be first in LDFLAGS. We also need LDFLAGS +# to be a "recursively expanded" variable, else adjustments to rpathdir +# don't work right. So we must NOT do LDFLAGS := something, meaning this has +# to be done first and elsewhere we must only do LDFLAGS += something. +ifdef PGXS + LDFLAGS = -L$(libdir) +else + LDFLAGS = -L. +endif +LDFLAGS += -Wl,-dead_strip_dylibs + +LDFLAGS_EX = +# LDFLAGS_SL might have already been assigned by calling makefile +LDFLAGS_SL += +LDREL = -r +LDOUT = -o +RANLIB = ranlib +WINDRES = +X = + +# Perl + +ifneq (/usr/bin/perl,) + # quoted to protect pathname with spaces + PERL = '/usr/bin/perl' +else + PERL = $(missing) perl +endif +perl_archlibexp = +perl_privlibexp = +perl_useshrplib = +perl_embed_ldflags = + +# Miscellaneous + +AWK = awk +LN_S = ln -s +MSGFMT = +MSGMERGE = +PYTHON = +TAR = /usr/bin/tar +XGETTEXT = + +GZIP = gzip +BZIP2 = bzip2 + +# Installation. + +INSTALL = $(SHELL) $(top_srcdir)/config/install-sh -c + +INSTALL_SCRIPT_MODE = 755 +INSTALL_DATA_MODE = 644 +INSTALL_PROGRAM = $(INSTALL_PROGRAM_ENV) $(INSTALL) $(INSTALL_STRIP_FLAG) +INSTALL_SCRIPT = $(INSTALL) -m $(INSTALL_SCRIPT_MODE) +INSTALL_DATA = $(INSTALL) -m $(INSTALL_DATA_MODE) +INSTALL_STLIB = $(INSTALL_STLIB_ENV) $(INSTALL_DATA) $(INSTALL_STRIP_FLAG) +INSTALL_SHLIB = $(INSTALL_SHLIB_ENV) $(INSTALL) $(INSTALL_SHLIB_OPTS) $(INSTALL_STRIP_FLAG) +# Override in Makefile.port if necessary +INSTALL_SHLIB_OPTS = -m 755 + +MKDIR_P = ${SHELL} ${top_srcdir}/config/install-sh -c -d + +missing = $(SHELL) $(top_srcdir)/config/missing + +STRIP = strip +STRIP_STATIC_LIB = : +STRIP_SHARED_LIB = : + +# Documentation + +have_docbook = no +COLLATEINDEX = +DOCBOOKSTYLE = +JADE = +NSGMLS = +OSX = +XSLTPROC = xsltproc + +# Code coverage + +GCOV = +LCOV = +GENHTML = + +ifeq ($(enable_coverage),yes) +# ccache loses .gcno files +export CCACHE_DISABLE = 1 +endif + +# Feature settings + +DEF_PGPORT = 5432 +WANTED_LANGUAGES = + + +########################################################################## +# +# Additional platform-specific settings +# + +# Name of the "template" +PORTNAME= darwin + +build_os = darwin10.8.0 + +host_tuple = x86_64-apple-darwin10.8.0 +host_os = darwin10.8.0 +host_cpu = x86_64 + +# Make HAVE_IPV6 available for initdb script creation +HAVE_IPV6= yes + +# The HP-UX port makefile, for one, needs access to this symbol +HAVE_POSIX_SIGNALS= yes + +# This is mainly for use on FreeBSD, where we have both a.out and elf +# systems now. May be applicable to other systems to? +ELF_SYSTEM= + +# Backend stack size limit has to be hard-wired on Windows (it's in bytes) +WIN32_STACK_RLIMIT=4194304 + +# Set if we have a working win32 crashdump header +have_win32_dbghelp = no + +# Pull in platform-specific magic +include Makefile.port.darwin + +# Set up rpath if enabled. By default it will point to our libdir, +# but individual Makefiles can force other rpath paths if needed. +rpathdir = $(libdir) + +ifeq ($(enable_rpath), yes) +LDFLAGS += $(rpath) +endif + + +########################################################################## +# +# Some variables needed to find some client interfaces + +ifdef PGXS +# some contribs assumes headers and libs are in the source tree... +libpq_srcdir = $(includedir) +libpq_builddir = $(libdir) +else +libpq_srcdir = $(top_srcdir)/src/interfaces/libpq +libpq_builddir = $(top_builddir)/src/interfaces/libpq +endif + +# This macro is for use by libraries linking to libpq. (Because libpgport +# isn't created with the same link flags as libpq, it can't be used.) +libpq = -L$(libpq_builddir) -lpq + +# If doing static linking, shared library dependency info isn't available, +# so add in the libraries that libpq depends on. +ifeq ($(enable_shared), no) +libpq += $(filter -lintl -lssl -lcrypto -lkrb5 -lcrypt, $(LIBS)) \ + $(LDAP_LIBS_FE) $(PTHREAD_LIBS) +endif + +# This macro is for use by client executables (not libraries) that use libpq. +# We force clients to pull symbols from the non-shared library libpgport +# rather than pulling some libpgport symbols from libpq just because +# libpq uses those functions too. This makes applications less +# dependent on changes in libpq's usage of pgport. To do this we link to +# pgport before libpq. This does cause duplicate -lpgport's to appear +# on client link lines. +ifdef PGXS +libpq_pgport = -L$(libdir) -lpgport $(libpq) +else +libpq_pgport = -L$(top_builddir)/src/port -lpgport $(libpq) +endif + + +submake-libpq: + $(MAKE) -C $(libpq_builddir) all + +submake-libpgport: + $(MAKE) -C $(top_builddir)/src/port all + +.PHONY: submake-libpq submake-libpgport + + +########################################################################## +# +# Testing support + +PL_TESTDB = pl_regression +CONTRIB_TESTDB = contrib_regression + +ifdef NO_LOCALE +NOLOCALE += --no-locale +endif + +pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) + +pg_regress_check = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) $(pg_regress_locale_flags) +pg_regress_installcheck = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir='$(PSQLDIR)' $(pg_regress_locale_flags) + +pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log/ + + +########################################################################## +# +# Customization +# +# This includes your local customizations if Makefile.custom exists +# in the source directory. This file doesn't exist in the original +# distribution so that it doesn't get overwritten when you upgrade. +# +# NOTE: Makefile.custom is from the pre-Autoconf days of PostgreSQL. +# You are liable to shoot yourself in the foot if you use it without +# knowing exactly what you're doing. The preferred (and more +# reliable) method is to communicate what you want to do to the +# configure script, and leave the makefiles alone. + +-include $(top_srcdir)/src/Makefile.custom + +ifneq ($(CUSTOM_INSTALL),) +INSTALL= $(CUSTOM_INSTALL) +endif + +ifneq ($(CUSTOM_CC),) + CC= $(CUSTOM_CC) +endif + +ifneq ($(CUSTOM_COPT),) + COPT= $(CUSTOM_COPT) +endif + +ifdef COPT + CFLAGS += $(COPT) + LDFLAGS += $(COPT) +endif + +ifdef PROFILE + CFLAGS += $(PROFILE) + LDFLAGS += $(PROFILE) +endif + + +########################################################################## +# +# substitute implementations of C library routines (see src/port/) +# note we already included -L.../src/port in LDFLAGS above + +LIBOBJS = + +LIBS := -lpgport $(LIBS) + +# to make ws2_32.lib the last library, and always link with shfolder, +# so SHGetFolderName isn't picked up from shell32.dll +ifeq ($(PORTNAME),win32) +LIBS += -lws2_32 -lshfolder +endif + +# Not really standard libc functions, used by the backend. +TAS = + + +########################################################################## +# +# Global targets and rules + +%.i: %.c + $(CPP) $(CPPFLAGS) -o $@ $< + +%.gz: % + $(GZIP) --best -c $< >$@ + +%.bz2: % + $(BZIP2) -c $< >$@ + +ifndef PGXS + +# Remake Makefile.global from Makefile.global.in if the latter +# changed. In order to trigger this rule, the including file must +# write `include $(top_builddir)/src/Makefile.global', not some +# shortcut thereof. +$(top_builddir)/src/Makefile.global: $(top_srcdir)/src/Makefile.global.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/Makefile.global + +# Remake pg_config.h from pg_config.h.in if the latter changed. +# config.status will not change the timestamp on pg_config.h if it +# doesn't change, so as to avoid recompiling the entire tree +# unnecessarily. Therefore we make config.status update a timestamp file +# stamp-h everytime it runs, so that we don't trigger this rule everytime. +# (We do trigger the null rule for stamp-h to pg_config.h everytime; so it's +# important for that rule to be null!) +# +# Of course you need to turn on dependency tracking to get any +# dependencies on pg_config.h. +$(top_builddir)/src/include/pg_config.h: $(top_builddir)/src/include/stamp-h + +$(top_builddir)/src/include/stamp-h: $(top_srcdir)/src/include/pg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/include/pg_config.h + +# Also remake ecpg_config.h from ecpg_config.h.in if the latter changed, same +# logic as above. +$(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h: $(top_builddir)/src/interfaces/ecpg/include/stamp-h + + $(top_builddir)/src/interfaces/ecpg/include/stamp-h: $(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/interfaces/ecpg/include/ecpg_config.h + +# When configure changes, rerun configure with the same options as +# last time. To change configure, you need to run autoconf manually. +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && ./config.status --recheck + +endif # not PGXS + + +install-strip: + @$(MAKE) INSTALL_PROGRAM_ENV="STRIPPROG='$(STRIP)'" \ + INSTALL_STLIB_ENV="STRIPPROG='$(STRIP_STATIC_LIB)'" \ + INSTALL_SHLIB_ENV="STRIPPROG='$(STRIP_SHARED_LIB)'" \ + INSTALL_STRIP_FLAG=-s \ + install + + +########################################################################## +# +# Recursive make support +# ---------------------- +# Instead of recursing through subdirectories with a for loop or +# repeated $(MAKE) -C whatever calls, this is a little smarter: it +# allows parallel make across directories and lets make -k and -q work +# correctly. + +# We need the $(eval) function and order-only prerequisites, which are +# available in GNU make 3.80. That also happens to be the version +# where the .VARIABLES variable was introduced, so this is a simple check. +ifndef .VARIABLES +$(error GNU make 3.80 or newer is required. You are using version $(MAKE_VERSION)) +endif + +# This function is only for internal use below. It should be called +# using $(eval). It will set up a target so that it recurses into +# a given subdirectory. Note that to avoid a nasty bug in make 3.80, +# this function has to avoid using any complicated constructs (like +# multiple targets on a line) and also not contain any lines that expand +# to more than about 200 bytes. This is why we make it apply to just one +# subdirectory at a time, rather than to a list of subdirectories. +# $1: target name, e.g., all +# $2: subdir name +# $3: target to run in subdir, usually same as $1 +define _create_recursive_target +.PHONY: $(1)-$(2)-recurse +$(1): $(1)-$(2)-recurse +$(1)-$(2)-recurse: + $$(MAKE) -C $(2) $(3) +endef +# Note that the use of $$ on the last line above is important; we want +# $(MAKE) to be evaluated when the rule is run, not when the $(eval) is run +# to create the rule. This is necessary to get make -q working. + +# Call this function in a makefile that needs to recurse into subdirectories. +# In the normal case all arguments can be defaulted. +# $1: targets to make recursive (defaults to list of standard targets) +# $2: list of subdirs (defaults to SUBDIRS variable) +# $3: target to run in subdir (defaults to current element of $1) +recurse = $(foreach target,$(if $1,$1,$(standard_targets)),$(foreach subdir,$(if $2,$2,$(SUBDIRS)),$(eval $(call _create_recursive_target,$(target),$(subdir),$(if $3,$3,$(target)))))) + +# If a makefile's list of SUBDIRS varies depending on configuration, then +# any subdirectories excluded from SUBDIRS should instead be added to +# ALWAYS_SUBDIRS, and then it must call recurse_always as well as recurse. +# This ensures that distprep, distclean, etc will apply to all subdirectories. +# In the normal case all arguments will be defaulted. +# $1: targets to make recursive (defaults to standard_always_targets) +# $2: list of subdirs (defaults to ALWAYS_SUBDIRS variable) +# $3: target to run in subdir (defaults to current element of $1) +recurse_always = $(foreach target,$(if $1,$1,$(standard_always_targets)),$(foreach subdir,$(if $2,$2,$(ALWAYS_SUBDIRS)),$(eval $(call _create_recursive_target,$(target),$(subdir),$(if $3,$3,$(target)))))) + + +########################################################################## +# +# Automatic dependency generation +# ------------------------------- +# When we configure with --enable-depend then we override the default +# compilation rule with the magic below. While or after creating the +# actual output file we also create a dependency list for the .c file. +# Next time we invoke make we will have top-notch information about +# whether this file needs to be updated. The dependency files are kept +# in the .deps subdirectory of each directory. + +autodepend = + +ifeq ($(autodepend), yes) + +ifndef COMPILE.c +COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c +endif + +DEPDIR = .deps + +ifeq ($(GCC), yes) + +# GCC allows us to create object and dependency file in one invocation. +%.o : %.c + @if test ! -d $(DEPDIR); then mkdir -p $(DEPDIR); fi + $(COMPILE.c) -o $@ $< -MMD -MP -MF $(DEPDIR)/$(*F).Po + +endif # GCC + +# Include all the dependency files generated for the current +# directory. List /dev/null as dummy because if the wildcard expands +# to nothing then make would complain. +-include $(wildcard $(DEPDIR)/*.Po) /dev/null + +# hook for clean-up +clean distclean maintainer-clean: clean-deps + +.PHONY: clean-deps +clean-deps: + @rm -rf $(DEPDIR) + +# When in automatic dependency mode, never delete any intermediate +# files automatically. Otherwise, the following could happen: When +# starting from a clean source tree, the first build would delete the +# intermediate file, but also create the dependency file, which +# mentions the intermediate file, thus making it non-intermediate. +# The second build will then need to rebuild the now non-intermediate +# missing file. So the second build will do work even though nothing +# had changed. One place where this happens is the .c -> .o -> .so +# chain for some contrib modules. +.SECONDARY: + +endif # autodepend + + +########################################################################## +# +# Native language support + +ifeq ($(enable_nls), yes) +ifneq (,$(wildcard $(srcdir)/nls.mk)) + +include $(top_srcdir)/src/nls-global.mk + +endif # nls.mk +endif # enable_nls + + +########################################################################## +# +# Coverage + +# Explanation of involved files: +# foo.c source file +# foo.o object file +# foo.gcno gcov graph (a.k.a. "notes") file, created at compile time +# (by gcc -ftest-coverage) +# foo.gcda gcov data file, created when the program is run (for +# programs compiled with gcc -fprofile-arcs) +# foo.c.gcov gcov output file with coverage information, created by +# gcov from foo.gcda (by "make coverage") +# foo.c.gcov.out stdout captured when foo.c.gcov is created, mildly +# interesting +# lcov.info lcov tracefile, built from gcda files in one directory, +# later collected by "make coverage-html" + +ifeq ($(enable_coverage), yes) + +# There is a strange interaction between lcov and existing .gcov +# output files. Hence the rm command and the ordering dependency. + +gcda_files := $(wildcard *.gcda) + +lcov.info: $(gcda_files) + rm -f *.gcov + $(if $^,$(LCOV) -d . -c -o $@ $(LCOVFLAGS)) + +%.c.gcov: %.gcda | lcov.info + $(GCOV) -b -f -p -o . $(GCOVFLAGS) $*.c >$*.c.gcov.out + +coverage: $(gcda_files:.gcda=.c.gcov) lcov.info + +.PHONY: coverage-html +coverage-html: coverage + rm -rf coverage + mkdir coverage + $(GENHTML) --show-details --legend --output-directory=coverage --title=PostgreSQL --num-spaces=4 --prefix=$(abs_top_srcdir) `find . -name lcov.info -print` + + +# hook for clean-up +clean distclean maintainer-clean: clean-coverage + +.PHONY: clean-coverage +clean-coverage: + rm -rf coverage + rm -f *.gcda *.gcno lcov.info *.gcov *.gcov.out + + +# User-callable target to reset counts between test runs +coverage-clean: + rm -f `find . -name '*.gcda' -print` + +endif # enable_coverage diff --git a/src/libpq/Makefile.global.win32 b/src/libpq/Makefile.global.win32 new file mode 100644 index 00000000..eb75bfb7 --- /dev/null +++ b/src/libpq/Makefile.global.win32 @@ -0,0 +1,686 @@ +# -*-makefile-*- +# src/Makefile.global.in + +#------------------------------------------------------------------------------ +# All PostgreSQL makefiles include this file and use the variables it sets, +# which in turn are put here by the configure script. There is no need for +# users to edit this file -- if it turns out to be necessary then that's a +# bug. +# +# A makefile that includes this file needs to set the variable `subdir' to +# the relative path from the top to itself and `top_builddir' to the relative +# path from itself to the top before including this file. (The "top" is the +# parent directory of the directory this file is in.) +#------------------------------------------------------------------------------ + + +########################################################################## +# +# Meta configuration + +standard_targets = all install installdirs uninstall distprep clean distclean maintainer-clean coverage check installcheck maintainer-check +# these targets should recurse even into subdirectories not being built: +standard_always_targets = distprep clean distclean maintainer-clean + +.PHONY: $(standard_targets) install-strip html man installcheck-parallel + +# make `all' the default target +all: + +# Delete target files if the command fails after it has +# started to update the file. +.DELETE_ON_ERROR: + +# PostgreSQL version number +VERSION = 9.1.1 +MAJORVERSION = 9.1 + +# Support for VPATH builds +vpath_build = no +abs_top_srcdir = /cygdrive/c/R/postgresql-9.1.1 + +ifneq ($(vpath_build),yes) +top_srcdir = $(top_builddir) +srcdir = . +else # vpath_build = yes +top_srcdir = $(abs_top_srcdir) +srcdir = $(top_srcdir)/$(subdir) + +endif + +vpathsearch = `for f in $(addsuffix /$(1),$(subst :, ,. $(VPATH))); do test -r $$f && echo $$f && break; done` + +# Saved arguments from configure +configure_args = '--without-zlib' + + +########################################################################## +# +# Installation directories +# +# These are set by the equivalent --xxxdir configure options. We +# append "postgresql" to some of them, if the string does not already +# contain "pgsql" or "postgres", in order to avoid directory clutter. +# +# In a PGXS build, we cannot use the values inserted into Makefile.global +# by configure, since the installation tree may have been relocated. +# Instead get the path values from pg_config. + +ifndef PGXS + +# Note that prefix, exec_prefix, and datarootdir aren't defined in a PGXS build; +# makefiles may only use the derived variables such as bindir. + +prefix := /usr/local/pgsql +exec_prefix := ${prefix} +datarootdir := ${prefix}/share + +bindir := ${exec_prefix}/bin + +datadir := ${datarootdir} +ifeq "$(findstring pgsql, $(datadir))" "" +ifeq "$(findstring postgres, $(datadir))" "" +override datadir := $(datadir)/postgresql +endif +endif + +sysconfdir := ${prefix}/etc +ifeq "$(findstring pgsql, $(sysconfdir))" "" +ifeq "$(findstring postgres, $(sysconfdir))" "" +override sysconfdir := $(sysconfdir)/postgresql +endif +endif + +libdir := ${exec_prefix}/lib + +pkglibdir = $(libdir) +ifeq "$(findstring pgsql, $(pkglibdir))" "" +ifeq "$(findstring postgres, $(pkglibdir))" "" +override pkglibdir := $(pkglibdir)/postgresql +endif +endif + +includedir := ${prefix}/include + +pkgincludedir = $(includedir) +ifeq "$(findstring pgsql, $(pkgincludedir))" "" +ifeq "$(findstring postgres, $(pkgincludedir))" "" +override pkgincludedir := $(pkgincludedir)/postgresql +endif +endif + +mandir := ${datarootdir}/man + +docdir := ${datarootdir}/doc/${PACKAGE_TARNAME} +ifeq "$(findstring pgsql, $(docdir))" "" +ifeq "$(findstring postgres, $(docdir))" "" +override docdir := $(docdir)/postgresql +endif +endif + +htmldir := ${docdir} + +localedir := ${datarootdir}/locale + +else # PGXS case + +# Extension makefiles should set PG_CONFIG, but older ones might not +ifndef PG_CONFIG +PG_CONFIG = pg_config +endif + +bindir := $(shell $(PG_CONFIG) --bindir) +datadir := $(shell $(PG_CONFIG) --sharedir) +sysconfdir := $(shell $(PG_CONFIG) --sysconfdir) +libdir := $(shell $(PG_CONFIG) --libdir) +pkglibdir := $(shell $(PG_CONFIG) --pkglibdir) +includedir := $(shell $(PG_CONFIG) --includedir) +pkgincludedir := $(shell $(PG_CONFIG) --pkgincludedir) +mandir := $(shell $(PG_CONFIG) --mandir) +docdir := $(shell $(PG_CONFIG) --docdir) +localedir := $(shell $(PG_CONFIG) --localedir) + +endif # PGXS + +# These derived path variables aren't separately configurable. + +includedir_server = $(pkgincludedir)/server +includedir_internal = $(pkgincludedir)/internal +pgxsdir = $(pkglibdir)/pgxs + + +########################################################################## +# +# Features +# +# Records the choice of the various --enable-xxx and --with-xxx options. + +with_perl = no +with_python = no +with_tcl = no +with_openssl = no +with_ossp_uuid = no +with_selinux = no +with_libxml = no +with_libxslt = no +with_system_tzdata = +with_zlib = no +enable_shared = yes +enable_rpath = yes +enable_nls = no +enable_debug = no +enable_dtrace = no +enable_coverage = no +enable_thread_safety = yes + +python_includespec = +python_libdir = +python_libspec = +python_additional_libs = +python_configdir = +python_majorversion = +python_version = + +krb_srvtab = + +TCLSH = +TCL_LIB_FILE = +TCL_LIBS = +TCL_LIB_SPEC = +TCL_INCLUDE_SPEC = +TCL_SHARED_BUILD = +TCL_SHLIB_LD_LIBS = + +PTHREAD_CFLAGS = +PTHREAD_LIBS = + + +########################################################################## +# +# Programs and flags + +# Compilers + +CPP = gcc -E +CPPFLAGS = -I./wininclude -DEXEC_BACKEND + +ifdef PGXS +override CPPFLAGS := -I$(includedir_server) -I$(includedir_internal) $(CPPFLAGS) +else # not PGXS +ifdef VPATH +endif +endif # not PGXS + +CC = gcc +GCC = yes +SUN_STUDIO_CC = no +CFLAGS = -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv + +# Kind-of compilers + +BISON = /bin/bison +BISONFLAGS = $(YFLAGS) +FLEX = /bin/flex +FLEXFLAGS = $(LFLAGS) +DTRACE = +DTRACEFLAGS = +ZIC = + +# Linking + +AR = ar +DLLTOOL = dlltool +DLLWRAP = dllwrap +LIBS = -lm +LDAP_LIBS_FE = +LDAP_LIBS_BE = +OSSP_UUID_LIBS = +LD = c:/mingw/mingw32/bin/ld.exe +with_gnu_ld = yes +ld_R_works = + +# We want -L for libpgport.a to be first in LDFLAGS. We also need LDFLAGS +# to be a "recursively expanded" variable, else adjustments to rpathdir +# don't work right. So we must NOT do LDFLAGS := something, meaning this has +# to be done first and elsewhere we must only do LDFLAGS += something. +ifdef PGXS + LDFLAGS = -L$(libdir) +else + LDFLAGS = -L$(top_builddir)/src/port +endif +LDFLAGS += -Wl,--allow-multiple-definition -Wl,--as-needed + +LDFLAGS_EX = +# LDFLAGS_SL might have already been assigned by calling makefile +LDFLAGS_SL += +LDREL = -r +LDOUT = -o +RANLIB = ranlib +WINDRES = windres +X = .exe + +# Perl + +ifneq (/bin/perl,) + # quoted to protect pathname with spaces + PERL = '/bin/perl' +else + PERL = $(missing) perl +endif +perl_archlibexp = +perl_privlibexp = +perl_useshrplib = +perl_embed_ldflags = + +# Miscellaneous + +AWK = gawk +LN_S = cp -p +MSGFMT = +MSGMERGE = +PYTHON = +TAR = /bin/tar +XGETTEXT = + +GZIP = gzip +BZIP2 = bzip2 + +# Installation. + +INSTALL = $(SHELL) $(top_srcdir)/config/install-sh -c + +INSTALL_SCRIPT_MODE = 755 +INSTALL_DATA_MODE = 644 +INSTALL_PROGRAM = $(INSTALL_PROGRAM_ENV) $(INSTALL) $(INSTALL_STRIP_FLAG) +INSTALL_SCRIPT = $(INSTALL) -m $(INSTALL_SCRIPT_MODE) +INSTALL_DATA = $(INSTALL) -m $(INSTALL_DATA_MODE) +INSTALL_STLIB = $(INSTALL_STLIB_ENV) $(INSTALL_DATA) $(INSTALL_STRIP_FLAG) +INSTALL_SHLIB = $(INSTALL_SHLIB_ENV) $(INSTALL) $(INSTALL_SHLIB_OPTS) $(INSTALL_STRIP_FLAG) +# Override in Makefile.port if necessary +INSTALL_SHLIB_OPTS = -m 755 + +MKDIR_P = /bin/mkdir -p + +missing = $(SHELL) $(top_srcdir)/config/missing + +STRIP = strip +STRIP_STATIC_LIB = strip -x +STRIP_SHARED_LIB = strip --strip-unneeded + +# Documentation + +have_docbook = no +COLLATEINDEX = +DOCBOOKSTYLE = +JADE = +NSGMLS = +OSX = +XSLTPROC = + +# Code coverage + +GCOV = +LCOV = +GENHTML = + +ifeq ($(enable_coverage),yes) +# ccache loses .gcno files +export CCACHE_DISABLE = 1 +endif + +# Feature settings + +DEF_PGPORT = 5432 +WANTED_LANGUAGES = + + +########################################################################## +# +# Additional platform-specific settings +# + +# Name of the "template" +PORTNAME= win32 + +build_os = mingw32 + +host_tuple = i686-pc-mingw32 +host_os = mingw32 +host_cpu = i686 + +# Make HAVE_IPV6 available for initdb script creation +HAVE_IPV6= yes + +# The HP-UX port makefile, for one, needs access to this symbol +HAVE_POSIX_SIGNALS= + +# This is mainly for use on FreeBSD, where we have both a.out and elf +# systems now. May be applicable to other systems to? +ELF_SYSTEM= + +# Backend stack size limit has to be hard-wired on Windows (it's in bytes) +WIN32_STACK_RLIMIT=4194304 + +# Set if we have a working win32 crashdump header +have_win32_dbghelp = no + +# Pull in platform-specific magic +include Makefile.port + +# Set up rpath if enabled. By default it will point to our libdir, +# but individual Makefiles can force other rpath paths if needed. +rpathdir = $(libdir) + +ifeq ($(enable_rpath), yes) +LDFLAGS += $(rpath) +endif + + +########################################################################## +# +# Some variables needed to find some client interfaces + +ifdef PGXS +# some contribs assumes headers and libs are in the source tree... +libpq_srcdir = $(includedir) +libpq_builddir = $(libdir) +else +libpq_srcdir = $(top_srcdir)/src/interfaces/libpq +libpq_builddir = $(top_builddir)/src/interfaces/libpq +endif + +# This macro is for use by libraries linking to libpq. (Because libpgport +# isn't created with the same link flags as libpq, it can't be used.) +libpq = -L$(libpq_builddir) -lpq + +# If doing static linking, shared library dependency info isn't available, +# so add in the libraries that libpq depends on. +ifeq ($(enable_shared), no) +libpq += $(filter -lintl -lssl -lcrypto -lkrb5 -lcrypt, $(LIBS)) \ + $(LDAP_LIBS_FE) $(PTHREAD_LIBS) +endif + +# This macro is for use by client executables (not libraries) that use libpq. +# We force clients to pull symbols from the non-shared library libpgport +# rather than pulling some libpgport symbols from libpq just because +# libpq uses those functions too. This makes applications less +# dependent on changes in libpq's usage of pgport. To do this we link to +# pgport before libpq. This does cause duplicate -lpgport's to appear +# on client link lines. +ifdef PGXS +libpq_pgport = -L$(libdir) -lpgport $(libpq) +else +libpq_pgport = -L$(top_builddir)/src/port -lpgport $(libpq) +endif + + +submake-libpq: + $(MAKE) -C $(libpq_builddir) all + +submake-libpgport: + $(MAKE) -C $(top_builddir)/src/port all + +.PHONY: submake-libpq submake-libpgport + + +########################################################################## +# +# Testing support + +PL_TESTDB = pl_regression +CONTRIB_TESTDB = contrib_regression + +ifdef NO_LOCALE +NOLOCALE += --no-locale +endif + +pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) + +pg_regress_check = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) $(pg_regress_locale_flags) +pg_regress_installcheck = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir='$(PSQLDIR)' $(pg_regress_locale_flags) + +pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log/ + + +########################################################################## +# +# Customization +# +# This includes your local customizations if Makefile.custom exists +# in the source directory. This file doesn't exist in the original +# distribution so that it doesn't get overwritten when you upgrade. +# +# NOTE: Makefile.custom is from the pre-Autoconf days of PostgreSQL. +# You are liable to shoot yourself in the foot if you use it without +# knowing exactly what you're doing. The preferred (and more +# reliable) method is to communicate what you want to do to the +# configure script, and leave the makefiles alone. + +-include $(top_srcdir)/src/Makefile.custom + +ifneq ($(CUSTOM_INSTALL),) +INSTALL= $(CUSTOM_INSTALL) +endif + +ifneq ($(CUSTOM_CC),) + CC= $(CUSTOM_CC) +endif + +ifneq ($(CUSTOM_COPT),) + COPT= $(CUSTOM_COPT) +endif + +ifdef COPT + CFLAGS += $(COPT) + LDFLAGS += $(COPT) +endif + +ifdef PROFILE + CFLAGS += $(PROFILE) + LDFLAGS += $(PROFILE) +endif + + +########################################################################## +# +# substitute implementations of C library routines (see src/port/) +# note we already included -L.../src/port in LDFLAGS above + +LIBOBJS = ${LIBOBJDIR}fseeko$U.o ${LIBOBJDIR}crypt$U.o ${LIBOBJDIR}erand48$U.o ${LIBOBJDIR}getrusage$U.o ${LIBOBJDIR}inet_aton$U.o ${LIBOBJDIR}random$U.o ${LIBOBJDIR}srandom$U.o ${LIBOBJDIR}strlcat$U.o ${LIBOBJDIR}strlcpy$U.o ${LIBOBJDIR}getaddrinfo$U.o ${LIBOBJDIR}getopt$U.o ${LIBOBJDIR}getopt_long$U.o ${LIBOBJDIR}kill$U.o ${LIBOBJDIR}open$U.o ${LIBOBJDIR}win32env$U.o ${LIBOBJDIR}win32error$U.o ${LIBOBJDIR}win32setlocale$U.o ${LIBOBJDIR}snprintf$U.o + +LIBS := -lpgport $(LIBS) + +# to make ws2_32.lib the last library, and always link with shfolder, +# so SHGetFolderName isn't picked up from shell32.dll +ifeq ($(PORTNAME),win32) +LIBS += -lws2_32 -lshfolder +endif + +# Not really standard libc functions, used by the backend. +TAS = + + +########################################################################## +# +# Global targets and rules + +%.i: %.c + $(CPP) $(CPPFLAGS) -o $@ $< + +%.gz: % + $(GZIP) --best -c $< >$@ + +%.bz2: % + $(BZIP2) -c $< >$@ + +ifndef PGXS + +# Remake Makefile.global from Makefile.global.in if the latter +# changed. In order to trigger this rule, the including file must +# write `include $(top_builddir)/src/Makefile.global', not some +# shortcut thereof. +$(top_builddir)/src/Makefile.global: $(top_srcdir)/src/Makefile.global.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/Makefile.global + +# Remake pg_config.h from pg_config.h.in if the latter changed. +# config.status will not change the timestamp on pg_config.h if it +# doesn't change, so as to avoid recompiling the entire tree +# unnecessarily. Therefore we make config.status update a timestamp file +# stamp-h everytime it runs, so that we don't trigger this rule everytime. +# (We do trigger the null rule for stamp-h to pg_config.h everytime; so it's +# important for that rule to be null!) +# +# Of course you need to turn on dependency tracking to get any +# dependencies on pg_config.h. +$(top_builddir)/src/include/pg_config.h: $(top_builddir)/src/include/stamp-h + +$(top_builddir)/src/include/stamp-h: $(top_srcdir)/src/include/pg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/include/pg_config.h + +# Also remake ecpg_config.h from ecpg_config.h.in if the latter changed, same +# logic as above. +$(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h: $(top_builddir)/src/interfaces/ecpg/include/stamp-h + + $(top_builddir)/src/interfaces/ecpg/include/stamp-h: $(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/interfaces/ecpg/include/ecpg_config.h + +# When configure changes, rerun configure with the same options as +# last time. To change configure, you need to run autoconf manually. +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && ./config.status --recheck + +endif # not PGXS + + +install-strip: + @$(MAKE) INSTALL_PROGRAM_ENV="STRIPPROG='$(STRIP)'" \ + INSTALL_STLIB_ENV="STRIPPROG='$(STRIP_STATIC_LIB)'" \ + INSTALL_SHLIB_ENV="STRIPPROG='$(STRIP_SHARED_LIB)'" \ + INSTALL_STRIP_FLAG=-s \ + install + + +########################################################################## +# +# Automatic dependency generation +# ------------------------------- +# When we configure with --enable-depend then we override the default +# compilation rule with the magic below. While or after creating the +# actual output file we also create a dependency list for the .c file. +# Next time we invoke make we will have top-notch information about +# whether this file needs to be updated. The dependency files are kept +# in the .deps subdirectory of each directory. + +autodepend = + +ifeq ($(autodepend), yes) + +ifndef COMPILE.c +COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c +endif + +DEPDIR = .deps + +ifeq ($(GCC), yes) + +# GCC allows us to create object and dependency file in one invocation. +%.o : %.c + @if test ! -d $(DEPDIR); then mkdir -p $(DEPDIR); fi + $(COMPILE.c) -o $@ $< -MMD -MP -MF $(DEPDIR)/$(*F).Po + +endif # GCC + +# Include all the dependency files generated for the current +# directory. List /dev/null as dummy because if the wildcard expands +# to nothing then make would complain. +-include $(wildcard $(DEPDIR)/*.Po) /dev/null + +# hook for clean-up +clean distclean maintainer-clean: clean-deps + +.PHONY: clean-deps +clean-deps: + @rm -rf $(DEPDIR) + +# When in automatic dependency mode, never delete any intermediate +# files automatically. Otherwise, the following could happen: When +# starting from a clean source tree, the first build would delete the +# intermediate file, but also create the dependency file, which +# mentions the intermediate file, thus making it non-intermediate. +# The second build will then need to rebuild the now non-intermediate +# missing file. So the second build will do work even though nothing +# had changed. One place where this happens is the .c -> .o -> .so +# chain for some contrib modules. +.SECONDARY: + +endif # autodepend + + +########################################################################## +# +# Native language support + +ifeq ($(enable_nls), yes) +ifneq (,$(wildcard $(srcdir)/nls.mk)) + +include $(top_srcdir)/src/nls-global.mk + +endif # nls.mk +endif # enable_nls + + +########################################################################## +# +# Coverage + +# Explanation of involved files: +# foo.c source file +# foo.o object file +# foo.gcno gcov graph (a.k.a. "notes") file, created at compile time +# (by gcc -ftest-coverage) +# foo.gcda gcov data file, created when the program is run (for +# programs compiled with gcc -fprofile-arcs) +# foo.c.gcov gcov output file with coverage information, created by +# gcov from foo.gcda (by "make coverage") +# foo.c.gcov.out stdout captured when foo.c.gcov is created, mildly +# interesting +# lcov.info lcov tracefile, built from gcda files in one directory, +# later collected by "make coverage-html" + +ifeq ($(enable_coverage), yes) + +# There is a strange interaction between lcov and existing .gcov +# output files. Hence the rm command and the ordering dependency. + +gcda_files := $(wildcard *.gcda) + +lcov.info: $(gcda_files) + rm -f *.gcov + $(if $^,$(LCOV) -d . -c -o $@ $(LCOVFLAGS)) + +%.c.gcov: %.gcda | lcov.info + $(GCOV) -b -f -p -o . $(GCOVFLAGS) $*.c >$*.c.gcov.out + +coverage: $(gcda_files:.gcda=.c.gcov) lcov.info + +.PHONY: coverage-html +coverage-html: coverage + rm -rf coverage + mkdir coverage + $(GENHTML) --show-details --legend --output-directory=coverage --title=PostgreSQL --num-spaces=4 --prefix=$(abs_top_srcdir) `find . -name lcov.info -print` + + +# hook for clean-up +clean distclean maintainer-clean: clean-coverage + +.PHONY: clean-coverage +clean-coverage: + rm -rf coverage + rm -f *.gcda *.gcno lcov.info *.gcov *.gcov.out + + +# User-callable target to reset counts between test runs +coverage-clean: + rm -f `find . -name '*.gcda' -print` + +endif # enable_coverage diff --git a/src/libpq/Makefile.global.win64 b/src/libpq/Makefile.global.win64 new file mode 100644 index 00000000..03ebadc8 --- /dev/null +++ b/src/libpq/Makefile.global.win64 @@ -0,0 +1,717 @@ +# -*-makefile-*- +# src/Makefile.global.in + +#------------------------------------------------------------------------------ +# All PostgreSQL makefiles include this file and use the variables it sets, +# which in turn are put here by the configure script. There is no need for +# users to edit this file -- if it turns out to be necessary then that's a +# bug. +# +# A makefile that includes this file needs to set the variable `subdir' to +# the relative path from the top to itself and `top_builddir' to the relative +# path from itself to the top before including this file. (The "top" is the +# parent directory of the directory this file is in.) +#------------------------------------------------------------------------------ + + +########################################################################## +# +# Meta configuration + +standard_targets = all install installdirs uninstall distprep clean distclean maintainer-clean coverage check installcheck maintainer-check +# these targets should recurse even into subdirectories not being built: +standard_always_targets = distprep clean distclean maintainer-clean + +.PHONY: $(standard_targets) install-strip html man installcheck-parallel + +# make `all' the default target +all: + +# Delete target files if the command fails after it has +# started to update the file. +.DELETE_ON_ERROR: + +# PostgreSQL version number +VERSION = 9.1.1 +MAJORVERSION = 9.1 + +# Support for VPATH builds +vpath_build = no +abs_top_srcdir = /cygdrive/c/R64/postgresql-9.1.1 + +ifneq ($(vpath_build),yes) +top_srcdir = $(top_builddir) +srcdir = . +else # vpath_build = yes +top_srcdir = $(abs_top_srcdir) +srcdir = $(top_srcdir)/$(subdir) + +endif + +vpathsearch = `for f in $(addsuffix /$(1),$(subst :, ,. $(VPATH))); do test -r $$f && echo $$f && break; done` + +# Saved arguments from configure +# configure_args = '--host=x86_64-w64-mingw32' '--without-zlib' 'host_alias=x86_64-w64-mingw32' + + +########################################################################## +# +# Installation directories +# +# These are set by the equivalent --xxxdir configure options. We +# append "postgresql" to some of them, if the string does not already +# contain "pgsql" or "postgres", in order to avoid directory clutter. +# +# In a PGXS build, we cannot use the values inserted into Makefile.global +# by configure, since the installation tree may have been relocated. +# Instead get the path values from pg_config. + +ifndef PGXS + +# Note that prefix, exec_prefix, and datarootdir aren't defined in a PGXS build; +# makefiles may only use the derived variables such as bindir. + +prefix := /usr/local/pgsql +exec_prefix := ${prefix} +datarootdir := ${prefix}/share + +bindir := ${exec_prefix}/bin + +datadir := ${datarootdir} +ifeq "$(findstring pgsql, $(datadir))" "" +ifeq "$(findstring postgres, $(datadir))" "" +override datadir := $(datadir)/postgresql +endif +endif + +sysconfdir := ${prefix}/etc +ifeq "$(findstring pgsql, $(sysconfdir))" "" +ifeq "$(findstring postgres, $(sysconfdir))" "" +override sysconfdir := $(sysconfdir)/postgresql +endif +endif + +libdir := ${exec_prefix}/lib + +pkglibdir = $(libdir) +ifeq "$(findstring pgsql, $(pkglibdir))" "" +ifeq "$(findstring postgres, $(pkglibdir))" "" +override pkglibdir := $(pkglibdir)/postgresql +endif +endif + +includedir := ${prefix}/include + +pkgincludedir = $(includedir) +ifeq "$(findstring pgsql, $(pkgincludedir))" "" +ifeq "$(findstring postgres, $(pkgincludedir))" "" +override pkgincludedir := $(pkgincludedir)/postgresql +endif +endif + +mandir := ${datarootdir}/man + +docdir := ${datarootdir}/doc/${PACKAGE_TARNAME} +ifeq "$(findstring pgsql, $(docdir))" "" +ifeq "$(findstring postgres, $(docdir))" "" +override docdir := $(docdir)/postgresql +endif +endif + +htmldir := ${docdir} + +localedir := ${datarootdir}/locale + +else # PGXS case + +# Extension makefiles should set PG_CONFIG, but older ones might not +ifndef PG_CONFIG +PG_CONFIG = pg_config +endif + +bindir := $(shell $(PG_CONFIG) --bindir) +datadir := $(shell $(PG_CONFIG) --sharedir) +sysconfdir := $(shell $(PG_CONFIG) --sysconfdir) +libdir := $(shell $(PG_CONFIG) --libdir) +pkglibdir := $(shell $(PG_CONFIG) --pkglibdir) +includedir := $(shell $(PG_CONFIG) --includedir) +pkgincludedir := $(shell $(PG_CONFIG) --pkgincludedir) +mandir := $(shell $(PG_CONFIG) --mandir) +docdir := $(shell $(PG_CONFIG) --docdir) +localedir := $(shell $(PG_CONFIG) --localedir) + +endif # PGXS + +# These derived path variables aren't separately configurable. + +includedir_server = $(pkgincludedir)/server +includedir_internal = $(pkgincludedir)/internal +pgxsdir = $(pkglibdir)/pgxs + + +########################################################################## +# +# Features +# +# Records the choice of the various --enable-xxx and --with-xxx options. + +with_perl = no +with_python = no +with_tcl = no +with_openssl = no +with_ossp_uuid = no +with_selinux = no +with_libxml = no +with_libxslt = no +with_system_tzdata = +with_zlib = no +enable_shared = yes +enable_rpath = yes +enable_nls = no +enable_debug = no +enable_dtrace = no +enable_coverage = no +enable_thread_safety = yes + +python_includespec = +python_libdir = +python_libspec = +python_additional_libs = +python_configdir = +python_majorversion = +python_version = + +krb_srvtab = + +TCLSH = +TCL_LIB_FILE = +TCL_LIBS = +TCL_LIB_SPEC = +TCL_INCLUDE_SPEC = +TCL_SHARED_BUILD = +TCL_SHLIB_LD_LIBS = + +PTHREAD_CFLAGS = +PTHREAD_LIBS = + + +########################################################################## +# +# Programs and flags + +# Compilers + +#CPP = x86_64-w64-mingw32-gcc -E +# hope this is set appropriately by the system +CPPFLAGS = -I./src/include/port/win32 -DEXEC_BACKEND + +ifdef PGXS +override CPPFLAGS := -I$(includedir_server) -I$(includedir_internal) $(CPPFLAGS) +else # not PGXS +override CPPFLAGS := -I$(top_srcdir)/src/include $(CPPFLAGS) +ifdef VPATH +override CPPFLAGS := -I$(top_builddir)/src/include $(CPPFLAGS) +endif +endif # not PGXS + +#CC = x86_64-w64-mingw32-gcc +ifeq ($(CC),gcc) +CC=gcc -m64 +endif +ifeq "$(CC)" "x86_64-w64-mingw32-gcc " +CC=x86_64-w64-mingw32-gcc +endif +# hope this variable is set appropriately by the system +GCC = yes +SUN_STUDIO_CC = no +CFLAGS = -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv + +# Kind-of compilers + +BISON = /bin/bison +BISONFLAGS = $(YFLAGS) +FLEX = /bin/flex +FLEXFLAGS = $(LFLAGS) +DTRACE = +DTRACEFLAGS = +ZIC = + +# Linking + +ifeq "$(CC)" "x86_64-w64-mingw32-gcc" +AR = x86_64-w64-mingw32-ar +endif +# hope this variable is set appropriately by the system +ifeq "$(CC)" "x86_64-w64-mingw32-gcc" +DLLTOOL = x86_64-w64-mingw32-dlltool +DLLWRAP = x86_64-w64-mingw32-dllwrap +else +DLLTOOL = dlltool -m64 +DLLWRAP = dllwrap -m64 +endif +LIBS = -lwsock32 -lm +LDAP_LIBS_FE = +LDAP_LIBS_BE = +OSSP_UUID_LIBS = +#LD = c:/rtools/mingw64/x86_64-w64-mingw32/bin/ld.exe +# hope this variable is set appropriately by the system +with_gnu_ld = yes +ld_R_works = + +# We want -L for libpgport.a to be first in LDFLAGS. We also need LDFLAGS +# to be a "recursively expanded" variable, else adjustments to rpathdir +# don't work right. So we must NOT do LDFLAGS := something, meaning this has +# to be done first and elsewhere we must only do LDFLAGS += something. +ifdef PGXS + LDFLAGS = -L$(libdir) +else + LDFLAGS = -L$(top_builddir)/src/port +endif +LDFLAGS += -Wl,--allow-multiple-definition -Wl,--as-needed + +LDFLAGS_EX = +# LDFLAGS_SL might have already been assigned by calling makefile +LDFLAGS_SL += +LDREL = -r +LDOUT = -o + +ifeq "$(CC)" "x86_64-w64-mingw32-gcc" +RANLIB = x86_64-w64-mingw32-ranlib +WINDRES = x86_64-w64-mingw32-windres +else +RANLIB = ranlib +WINDRES = windres -F pe-x86-64 +endif +X = .exe + +# Perl + +ifneq (/bin/perl,) + # quoted to protect pathname with spaces + PERL = '/bin/perl' +else + PERL = $(missing) perl +endif +perl_archlibexp = +perl_privlibexp = +perl_useshrplib = +perl_embed_ldflags = + +# Miscellaneous + +AWK = gawk +LN_S = cp -p +MSGFMT = +MSGMERGE = +PYTHON = +TAR = /bin/tar +XGETTEXT = + +GZIP = gzip +BZIP2 = bzip2 + +# Installation. + +INSTALL = $(SHELL) $(top_srcdir)/config/install-sh -c + +INSTALL_SCRIPT_MODE = 755 +INSTALL_DATA_MODE = 644 +INSTALL_PROGRAM = $(INSTALL_PROGRAM_ENV) $(INSTALL) $(INSTALL_STRIP_FLAG) +INSTALL_SCRIPT = $(INSTALL) -m $(INSTALL_SCRIPT_MODE) +INSTALL_DATA = $(INSTALL) -m $(INSTALL_DATA_MODE) +INSTALL_STLIB = $(INSTALL_STLIB_ENV) $(INSTALL_DATA) $(INSTALL_STRIP_FLAG) +INSTALL_SHLIB = $(INSTALL_SHLIB_ENV) $(INSTALL) $(INSTALL_SHLIB_OPTS) $(INSTALL_STRIP_FLAG) +# Override in Makefile.port if necessary +INSTALL_SHLIB_OPTS = -m 755 + +MKDIR_P = /bin/mkdir -p + +missing = $(SHELL) $(top_srcdir)/config/missing + +ifeq "$(CC)" "x86_64-w64-mingw32-gcc" +STRIP = x86_64-w64-mingw32-strip +STRIP_STATIC_LIB = x86_64-w64-mingw32-strip -x +STRIP_SHARED_LIB = x86_64-w64-mingw32-strip --strip-unneeded +else +STRIP = strip +STRIP_STATIC_LIB = strip -x +STRIP_SHARED_LIB = strip --strip-unneeded +endif + +# Documentation + +have_docbook = no +COLLATEINDEX = +DOCBOOKSTYLE = +JADE = +NSGMLS = +OSX = +XSLTPROC = + +# Code coverage + +GCOV = +LCOV = +GENHTML = + +ifeq ($(enable_coverage),yes) +# ccache loses .gcno files +export CCACHE_DISABLE = 1 +endif + +# Feature settings + +DEF_PGPORT = 5432 +WANTED_LANGUAGES = + + +########################################################################## +# +# Additional platform-specific settings +# + +# Name of the "template" +PORTNAME= win32 + +build_os = mingw32 + +host_tuple = x86_64-w64-mingw32 +host_os = mingw32 +host_cpu = x86_64 + +# Make HAVE_IPV6 available for initdb script creation +HAVE_IPV6= yes + +# The HP-UX port makefile, for one, needs access to this symbol +HAVE_POSIX_SIGNALS= + +# This is mainly for use on FreeBSD, where we have both a.out and elf +# systems now. May be applicable to other systems to? +ELF_SYSTEM= + +# Backend stack size limit has to be hard-wired on Windows (it's in bytes) +WIN32_STACK_RLIMIT=4194304 + +# Set if we have a working win32 crashdump header +have_win32_dbghelp = yes + +# Pull in platform-specific magic +include Makefile.port + +# Set up rpath if enabled. By default it will point to our libdir, +# but individual Makefiles can force other rpath paths if needed. +rpathdir = $(libdir) + +ifeq ($(enable_rpath), yes) +LDFLAGS += $(rpath) +endif + + +########################################################################## +# +# Some variables needed to find some client interfaces + +ifdef PGXS +# some contribs assumes headers and libs are in the source tree... +libpq_srcdir = $(includedir) +libpq_builddir = $(libdir) +else +libpq_srcdir = $(top_srcdir)/src/interfaces/libpq +libpq_builddir = $(top_builddir)/src/interfaces/libpq +endif + +# This macro is for use by libraries linking to libpq. (Because libpgport +# isn't created with the same link flags as libpq, it can't be used.) +libpq = -L$(libpq_builddir) -lpq + +# If doing static linking, shared library dependency info isn't available, +# so add in the libraries that libpq depends on. +ifeq ($(enable_shared), no) +libpq += $(filter -lintl -lssl -lcrypto -lkrb5 -lcrypt, $(LIBS)) \ + $(LDAP_LIBS_FE) $(PTHREAD_LIBS) +endif + +# This macro is for use by client executables (not libraries) that use libpq. +# We force clients to pull symbols from the non-shared library libpgport +# rather than pulling some libpgport symbols from libpq just because +# libpq uses those functions too. This makes applications less +# dependent on changes in libpq's usage of pgport. To do this we link to +# pgport before libpq. This does cause duplicate -lpgport's to appear +# on client link lines. +ifdef PGXS +libpq_pgport = -L$(libdir) -lpgport $(libpq) +else +libpq_pgport = -L$(top_builddir)/src/port -lpgport $(libpq) +endif + + +submake-libpq: + $(MAKE) -C $(libpq_builddir) all + +submake-libpgport: + $(MAKE) -C $(top_builddir)/src/port all + +.PHONY: submake-libpq submake-libpgport + + +########################################################################## +# +# Testing support + +PL_TESTDB = pl_regression +CONTRIB_TESTDB = contrib_regression + +ifdef NO_LOCALE +NOLOCALE += --no-locale +endif + +pg_regress_locale_flags = $(if $(ENCODING),--encoding=$(ENCODING)) $(NOLOCALE) + +pg_regress_check = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) $(pg_regress_locale_flags) +pg_regress_installcheck = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir='$(PSQLDIR)' $(pg_regress_locale_flags) + +pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log/ + + +########################################################################## +# +# Customization +# +# This includes your local customizations if Makefile.custom exists +# in the source directory. This file doesn't exist in the original +# distribution so that it doesn't get overwritten when you upgrade. +# +# NOTE: Makefile.custom is from the pre-Autoconf days of PostgreSQL. +# You are liable to shoot yourself in the foot if you use it without +# knowing exactly what you're doing. The preferred (and more +# reliable) method is to communicate what you want to do to the +# configure script, and leave the makefiles alone. + +-include $(top_srcdir)/src/Makefile.custom + +ifneq ($(CUSTOM_INSTALL),) +INSTALL= $(CUSTOM_INSTALL) +endif + +ifneq ($(CUSTOM_CC),) + CC= $(CUSTOM_CC) +endif + +ifneq ($(CUSTOM_COPT),) + COPT= $(CUSTOM_COPT) +endif + +ifdef COPT + CFLAGS += $(COPT) + LDFLAGS += $(COPT) +endif + +ifdef PROFILE + CFLAGS += $(PROFILE) + LDFLAGS += $(PROFILE) +endif + + +########################################################################## +# +# substitute implementations of C library routines (see src/port/) +# note we already included -L.../src/port in LDFLAGS above + +LIBOBJS = ${LIBOBJDIR}fseeko$U.o ${LIBOBJDIR}crypt$U.o ${LIBOBJDIR}erand48$U.o ${LIBOBJDIR}getrusage$U.o ${LIBOBJDIR}inet_aton$U.o ${LIBOBJDIR}random$U.o ${LIBOBJDIR}srandom$U.o ${LIBOBJDIR}strlcat$U.o ${LIBOBJDIR}strlcpy$U.o ${LIBOBJDIR}getaddrinfo$U.o ${LIBOBJDIR}getopt$U.o ${LIBOBJDIR}getopt_long$U.o ${LIBOBJDIR}kill$U.o ${LIBOBJDIR}open$U.o ${LIBOBJDIR}win32env$U.o ${LIBOBJDIR}win32error$U.o ${LIBOBJDIR}win32setlocale$U.o ${LIBOBJDIR}snprintf$U.o + +LIBS := -lpgport $(LIBS) + +# to make ws2_32.lib the last library, and always link with shfolder, +# so SHGetFolderName isn't picked up from shell32.dll +ifeq ($(PORTNAME),win32) +LIBS += -lws2_32 -lshfolder +endif + +# Not really standard libc functions, used by the backend. +TAS = + + +########################################################################## +# +# Global targets and rules + +%.i: %.c + $(CPP) $(CPPFLAGS) -o $@ $< + +%.gz: % + $(GZIP) --best -c $< >$@ + +%.bz2: % + $(BZIP2) -c $< >$@ + +ifndef PGXS + +# Remake Makefile.global from Makefile.global.in if the latter +# changed. In order to trigger this rule, the including file must +# write `include $(top_builddir)/src/Makefile.global', not some +# shortcut thereof. +$(top_builddir)/src/Makefile.global: $(top_srcdir)/src/Makefile.global.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/Makefile.global + +# Remake pg_config.h from pg_config.h.in if the latter changed. +# config.status will not change the timestamp on pg_config.h if it +# doesn't change, so as to avoid recompiling the entire tree +# unnecessarily. Therefore we make config.status update a timestamp file +# stamp-h everytime it runs, so that we don't trigger this rule everytime. +# (We do trigger the null rule for stamp-h to pg_config.h everytime; so it's +# important for that rule to be null!) +# +# Of course you need to turn on dependency tracking to get any +# dependencies on pg_config.h. +$(top_builddir)/src/include/pg_config.h: $(top_builddir)/src/include/stamp-h + +$(top_builddir)/src/include/stamp-h: $(top_srcdir)/src/include/pg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/include/pg_config.h + +# Also remake ecpg_config.h from ecpg_config.h.in if the latter changed, same +# logic as above. +$(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h: $(top_builddir)/src/interfaces/ecpg/include/stamp-h + + $(top_builddir)/src/interfaces/ecpg/include/stamp-h: $(top_builddir)/src/interfaces/ecpg/include/ecpg_config.h.in $(top_builddir)/config.status + cd $(top_builddir) && ./config.status src/interfaces/ecpg/include/ecpg_config.h + +# When configure changes, rerun configure with the same options as +# last time. To change configure, you need to run autoconf manually. +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && ./config.status --recheck + +endif # not PGXS + + +install-strip: + @$(MAKE) INSTALL_PROGRAM_ENV="STRIPPROG='$(STRIP)'" \ + INSTALL_STLIB_ENV="STRIPPROG='$(STRIP_STATIC_LIB)'" \ + INSTALL_SHLIB_ENV="STRIPPROG='$(STRIP_SHARED_LIB)'" \ + INSTALL_STRIP_FLAG=-s \ + install + + +########################################################################## +# +# Automatic dependency generation +# ------------------------------- +# When we configure with --enable-depend then we override the default +# compilation rule with the magic below. While or after creating the +# actual output file we also create a dependency list for the .c file. +# Next time we invoke make we will have top-notch information about +# whether this file needs to be updated. The dependency files are kept +# in the .deps subdirectory of each directory. + +autodepend = + +ifeq ($(autodepend), yes) + +ifndef COMPILE.c +COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c +endif + +DEPDIR = .deps + +ifeq ($(GCC), yes) + +# GCC allows us to create object and dependency file in one invocation. +%.o : %.c + @if test ! -d $(DEPDIR); then mkdir -p $(DEPDIR); fi + $(COMPILE.c) -o $@ $< -MMD -MP -MF $(DEPDIR)/$(*F).Po + +endif # GCC + +# Include all the dependency files generated for the current +# directory. List /dev/null as dummy because if the wildcard expands +# to nothing then make would complain. +-include $(wildcard $(DEPDIR)/*.Po) /dev/null + +# hook for clean-up +clean distclean maintainer-clean: clean-deps + +.PHONY: clean-deps +clean-deps: + @rm -rf $(DEPDIR) + +# When in automatic dependency mode, never delete any intermediate +# files automatically. Otherwise, the following could happen: When +# starting from a clean source tree, the first build would delete the +# intermediate file, but also create the dependency file, which +# mentions the intermediate file, thus making it non-intermediate. +# The second build will then need to rebuild the now non-intermediate +# missing file. So the second build will do work even though nothing +# had changed. One place where this happens is the .c -> .o -> .so +# chain for some contrib modules. +.SECONDARY: + +endif # autodepend + + +########################################################################## +# +# Native language support + +ifeq ($(enable_nls), yes) +ifneq (,$(wildcard $(srcdir)/nls.mk)) + +include $(top_srcdir)/src/nls-global.mk + +endif # nls.mk +endif # enable_nls + + +########################################################################## +# +# Coverage + +# Explanation of involved files: +# foo.c source file +# foo.o object file +# foo.gcno gcov graph (a.k.a. "notes") file, created at compile time +# (by gcc -ftest-coverage) +# foo.gcda gcov data file, created when the program is run (for +# programs compiled with gcc -fprofile-arcs) +# foo.c.gcov gcov output file with coverage information, created by +# gcov from foo.gcda (by "make coverage") +# foo.c.gcov.out stdout captured when foo.c.gcov is created, mildly +# interesting +# lcov.info lcov tracefile, built from gcda files in one directory, +# later collected by "make coverage-html" + +ifeq ($(enable_coverage), yes) + +# There is a strange interaction between lcov and existing .gcov +# output files. Hence the rm command and the ordering dependency. + +gcda_files := $(wildcard *.gcda) + +lcov.info: $(gcda_files) + rm -f *.gcov + $(if $^,$(LCOV) -d . -c -o $@ $(LCOVFLAGS)) + +%.c.gcov: %.gcda | lcov.info + $(GCOV) -b -f -p -o . $(GCOVFLAGS) $*.c >$*.c.gcov.out + +coverage: $(gcda_files:.gcda=.c.gcov) lcov.info + +.PHONY: coverage-html +coverage-html: coverage + rm -rf coverage + mkdir coverage + $(GENHTML) --show-details --legend --output-directory=coverage --title=PostgreSQL --num-spaces=4 --prefix=$(abs_top_srcdir) `find . -name lcov.info -print` + + +# hook for clean-up +clean distclean maintainer-clean: clean-coverage + +.PHONY: clean-coverage +clean-coverage: + rm -rf coverage + rm -f *.gcda *.gcno lcov.info *.gcov *.gcov.out + + +# User-callable target to reset counts between test runs +coverage-clean: + rm -f `find . -name '*.gcda' -print` + +endif # enable_coverage diff --git a/src/libpq/Makefile.port b/src/libpq/Makefile.port new file mode 100644 index 00000000..dbeff298 --- /dev/null +++ b/src/libpq/Makefile.port @@ -0,0 +1,73 @@ +# src/makefiles/Makefile.win32 + +# Use replacement include files for those missing on Win32 +override CPPFLAGS+="-I$(top_srcdir)/src/include/port/win32" + +ifdef PGXS +BE_DLLLIBS= -L$(libdir) -lpostgres +else +BE_DLLLIBS= -L$(top_builddir)/src/backend -lpostgres +endif + +AROPT = crs +DLSUFFIX = .dll +CFLAGS_SL = + +ifneq (,$(findstring backend,$(subdir))) +ifeq (,$(findstring conversion_procs,$(subdir))) +ifeq (,$(findstring snowball,$(subdir))) +ifeq (,$(findstring libpqwalreceiver,$(subdir))) +override CPPFLAGS+= -DBUILDING_DLL +endif +endif +endif +endif + +ifneq (,$(findstring timezone,$(subdir))) +override CPPFLAGS+= -DBUILDING_DLL +endif + +ifneq (,$(findstring ecpg/ecpglib,$(subdir))) +override CPPFLAGS+= -DBUILDING_DLL +endif + +# required by Python headers +ifneq (,$(findstring src/pl/plpython,$(subdir))) +override CPPFLAGS+= -DUSE_DL_IMPORT +endif + +# special win32 headers are provided here +ifdef PGXS +override CPPFLAGS+= -I$(includedir_server)/port/win32 +endif + +# it is better to install shared-libraries anyway? +# may be overriden with make MAKE_DLL=false install +ifndef MAKE_DLL +MAKE_DLL = true +endif + + +# Build rules to add versioninfo resources to win32 binaries + +WIN32RES += win32ver.o +ifeq ($(PGFILESHLIB),1) +PGFTYPE = VFT_DLL +else +PGFTYPE = VFT_APP +endif +ifneq (,$(PGAPPICON)) +PGICOSTR = $(subst /,\/,IDI_ICON ICON \"$(top_builddir)/src/port/$(PGAPPICON).ico\") +endif + +win32ver.rc: $(top_srcdir)/src/port/win32ver.rc + sed -e 's;FILEDESC;$(PGFILEDESC);' -e 's;VFT_APP;$(PGFTYPE);' -e 's;_ICO_;$(PGICOSTR);' -e 's;\(VERSION.*\),0 *$$;\1,'`date '+%y%j' | sed 's/^0*//'`';' $< >$@ + +win32ver.o: win32ver.rc + $(WINDRES) -i $< -o $@ --include-dir=$(top_builddir)/src/include --include-dir=$(srcdir) + +# Rule for building a shared library from a single .o file +%.dll: %.o + $(DLLTOOL) --export-all --output-def $*.def $< + $(DLLWRAP) -o $@ --def $*.def $< $(LDFLAGS) $(LDFLAGS_SL) $(BE_DLLLIBS) + rm -f $*.def diff --git a/src/libpq/Makefile.port.darwin b/src/libpq/Makefile.port.darwin new file mode 100644 index 00000000..7a8ba3e5 --- /dev/null +++ b/src/libpq/Makefile.port.darwin @@ -0,0 +1,13 @@ +AROPT = crs + +DLSUFFIX = .so + +ifdef PGXS + BE_DLLLIBS = -bundle_loader $(bindir)/postgres +else + BE_DLLLIBS = -bundle_loader $(top_builddir)/src/backend/postgres +endif + +# Rule for building a shared library from a single .o file +%.so: %.o + $(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_SL) -bundle $(BE_DLLLIBS) -o $@ $< diff --git a/src/libpq/Makefile.shlib b/src/libpq/Makefile.shlib new file mode 100644 index 00000000..c2387527 --- /dev/null +++ b/src/libpq/Makefile.shlib @@ -0,0 +1,557 @@ +#------------------------------------------------------------------------- +# +# Makefile.shlib +# Common rules for building shared libraries +# +# Copyright (c) 1998, Regents of the University of California +# +# IDENTIFICATION +# src/Makefile.shlib +# +#------------------------------------------------------------------------- + +# This file should be included by any Postgres module Makefile that +# wants to build a shared library (if possible for the current +# platform). A static library is also built from the same object +# files. Only one library can be built per makefile. +# +# Before including this file, the module Makefile must define these +# variables: +# +# NAME Name of library to build (no suffix nor "lib" prefix) +# OBJS List of object files to include in library +# SHLIB_LINK If shared library relies on other libraries, +# additional stuff to put in its link command +# SHLIB_PREREQS Order-only prerequisites for library build target +# SHLIB_EXPORTS (optional) Name of file containing list of symbols to +# export, in the format "function_name number" +# +# When building a shared library, the following version information +# must also be set. It should be omitted when building a dynamically +# loadable module. +# +# SO_MAJOR_VERSION Major version number to use for shared library +# SO_MINOR_VERSION Minor version number to use for shared library +# (If you want a patchlevel, include it in SO_MINOR_VERSION, e.g., "6.2".) +# +# Optional flags when building DLL's (only applicable to win32 and cygwin +# platforms). +# DLLTOOL_DEFFLAGS Additional flags when creating the dll .def file +# DLLTOOL_LIBFLAGS Additional flags when creating the lib.a file +# DLLWRAP_FLAGS Additional flags to dllwrap +# +# The module Makefile must also include +# $(top_builddir)/src/Makefile.global before including this file. +# (Makefile.global sets PORTNAME and other needed symbols.) +# +# This makefile provides the following (phony) targets: +# +# all-lib build the static and shared (if applicable) libraries +# install-lib install the libraries into $(libdir) +# installdirs-lib create installation directory $(libdir) +# uninstall-lib remove the libraries from $(libdir) +# clean-lib delete the static and shared libraries from the build dir +# maintainer-clean-lib delete .def files built for win32 +# +# Since `all-lib' is the first rule in this file you probably want to +# have the `all' target before including this file. In the most simple +# case it would look like this: +# +# all: all-lib +# +# Similarly, the install rule might look like +# +# install: install-lib +# +# plus any additional things you want to install. Et cetera. +# +# Got that? Look at src/interfaces/libpq/Makefile for an example. +# +# While the linker allows creation of most shared libraries, +# -Bsymbolic requires resolution of all symbols, making the +# compiler a better choice for shared library creation on ELF platforms. +# With the linker, -Bsymbolic requires the crt1.o startup object file. +# bjm 2001-02-10 + + +COMPILER = $(CC) $(CFLAGS) +LINK.static = $(AR) $(AROPT) + + + +ifdef SO_MAJOR_VERSION +# Default library naming convention used by the majority of platforms +ifeq ($(enable_shared), yes) +shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) +shlib_major = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) +shlib_bare = lib$(NAME)$(DLSUFFIX) +endif +# Testing the soname variable is a reliable way to determine whether a +# linkable library is being built. +soname = $(shlib_major) +else +# Naming convention for dynamically loadable modules +ifeq ($(enable_shared), yes) +shlib = $(NAME)$(DLSUFFIX) +endif +endif +stlib = lib$(NAME).a + +ifndef soname +# additional flags for backend modules +SHLIB_LINK += $(BE_DLLLIBS) +endif + +# For each platform we support shared libraries on, set shlib to the +# name of the library (if default above is not right), set +# LINK.shared to the command to link the library, +# and adjust SHLIB_LINK if necessary. + +# Try to keep the sections in some kind of order, folks... + +override CFLAGS += $(CFLAGS_SL) +ifdef SO_MAJOR_VERSION +# libraries ought to use this to refer to versioned gettext domain names +override CPPFLAGS += -DSO_MAJOR_VERSION=$(SO_MAJOR_VERSION) +endif + +ifeq ($(PORTNAME), aix) + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) + endif + haslibarule = yes + exports_file = lib$(NAME).exp +endif + +ifeq ($(PORTNAME), darwin) + ifdef soname + # linkable library + DLSUFFIX = .dylib + ifneq ($(SO_MAJOR_VERSION), 0) + version_link = -compatibility_version $(SO_MAJOR_VERSION) -current_version $(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) + endif + LINK.shared = $(COMPILER) -dynamiclib -install_name '$(libdir)/lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX)' $(version_link) $(exported_symbols_list) -multiply_defined suppress + shlib = lib$(NAME).$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION)$(DLSUFFIX) + shlib_major = lib$(NAME).$(SO_MAJOR_VERSION)$(DLSUFFIX) + else + # loadable module + DLSUFFIX = .so + LINK.shared = $(COMPILER) -bundle -multiply_defined suppress + endif + BUILD.exports = $(AWK) '/^[^\#]/ {printf "_%s\n",$$1}' $< >$@ + exports_file = $(SHLIB_EXPORTS:%.txt=%.list) + ifneq (,$(exports_file)) + exported_symbols_list = -exported_symbols_list $(exports_file) + endif +endif + +ifeq ($(PORTNAME), openbsd) + ifdef ELF_SYSTEM + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + SHLIB_LINK += -lc + else + LINK.shared = $(LD) -x -Bshareable -Bforcearchive + endif +endif + +ifeq ($(PORTNAME), bsdi) + ifeq ($(DLSUFFIX), .so) + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + SHLIB_LINK += -lc + endif + ifeq ($(DLSUFFIX), .o) + LINK.shared = shlicc -O $(LDREL) + endif +endif + +ifeq ($(PORTNAME), freebsd) + ifdef ELF_SYSTEM + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) + endif + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + else + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) + endif + LINK.shared = $(LD) -x -Bshareable -Bforcearchive + endif +endif + +ifeq ($(PORTNAME), netbsd) + ifdef ELF_SYSTEM + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-x,-soname,$(soname) + endif + else + LINK.shared = $(LD) -x -Bshareable -Bforcearchive + endif +endif + +ifeq ($(PORTNAME), hpux) + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) + endif + ifeq ($(with_gnu_ld), yes) + LINK.shared = $(CC) -shared + ifdef soname + LINK.shared += -Wl,-h -Wl,$(soname) + endif + else + LINK.shared = $(LD) -b + ifdef soname + LINK.shared += +h $(soname) + endif + # can't use the CC-syntax rpath pattern here, so instead: + rpath = + ifeq ($(enable_rpath), yes) + LINK.shared += +b '$(rpathdir)' + endif + # On HPUX platforms, gcc is usually configured to search for libraries + # in /usr/local/lib, but ld won't do so. Add an explicit -L switch so + # ld can find the same libraries gcc does. Make sure it goes after any + # -L switches provided explicitly. + ifeq ($(GCC), yes) + SHLIB_LINK += -L/usr/local/lib + endif + endif + # And we need to link with libgcc, too + ifeq ($(GCC), yes) + SHLIB_LINK += `$(CC) $(LDFLAGS) -print-libgcc-file-name` + endif +endif + +ifeq ($(PORTNAME), irix) + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX).$(SO_MAJOR_VERSION) + endif + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-set_version,sgi$(SO_MAJOR_VERSION).$(SO_MINOR_VERSION) + endif +endif + +ifeq ($(PORTNAME), linux) + LINK.shared = $(COMPILER) -shared + ifdef soname + LINK.shared += -Wl,-soname,$(soname) + endif + BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ + exports_file = $(SHLIB_EXPORTS:%.txt=%.list) + ifneq (,$(exports_file)) + LINK.shared += -Wl,--version-script=$(exports_file) + endif +endif + +ifeq ($(PORTNAME), solaris) + ifeq ($(GCC), yes) + LINK.shared = $(COMPILER) -shared + else + LINK.shared = $(COMPILER) -G + endif + ifdef soname + ifeq ($(with_gnu_ld), yes) + LINK.shared += -Wl,-soname,$(soname) + else + LINK.shared += -h $(soname) + endif + endif +endif + +ifeq ($(PORTNAME), sunos4) + LINK.shared = $(LD) -assert pure-text -Bdynamic +endif + +ifeq ($(PORTNAME), osf) + LINK.shared = $(LD) -shared -expect_unresolved '*' +endif + +ifeq ($(PORTNAME), sco) + ifeq ($(GCC), yes) + LINK.shared = $(CC) -shared + else + LINK.shared = $(CC) -G + endif + LINK.shared += -Wl,-z,text + ifdef soname + LINK.shared += -Wl,-h,$(soname) + endif +endif + +ifeq ($(PORTNAME), svr4) + LINK.shared = $(LD) -G +endif + +ifeq ($(PORTNAME), univel) + LINK.shared = $(LD) -G -z text +endif + +ifeq ($(PORTNAME), unixware) + ifeq ($(GCC), yes) + LINK.shared = $(CC) -shared + else + LINK.shared = $(CC) -G + endif + LINK.shared += -Wl,-z,text + ifdef soname + LINK.shared += -Wl,-h,$(soname) + endif +endif + +ifeq ($(PORTNAME), cygwin) + ifdef SO_MAJOR_VERSION + shlib = cyg$(NAME)$(DLSUFFIX) + endif + haslibarule = yes +endif + +ifeq ($(PORTNAME), win32) + ifdef SO_MAJOR_VERSION + shlib = lib$(NAME)$(DLSUFFIX) + endif + haslibarule = yes +endif + + + +## +## BUILD +## + +.PHONY: all-lib all-static-lib all-shared-lib + +all-lib: all-shared-lib +ifdef soname +# no static library when building a dynamically loadable module +all-lib: all-static-lib +endif + +all-static-lib: $(stlib) + +all-shared-lib: $(shlib) + +ifndef haslibarule +$(stlib): $(OBJS) + $(LINK.static) $@ $^ + $(RANLIB) $@ +endif #haslibarule + +ifeq ($(enable_shared), yes) + +ifeq (,$(filter cygwin win32,$(PORTNAME))) +ifneq ($(PORTNAME), aix) + +# Normal case +$(shlib): $(OBJS) + $(LINK.shared) -o $@ $(OBJS) $(LDFLAGS) $(LDFLAGS_SL) $(SHLIB_LINK) +ifdef shlib_major +# If we're using major and minor versions, then make a symlink to major-version-only. +ifneq ($(shlib), $(shlib_major)) + rm -f $(shlib_major) + $(LN_S) $(shlib) $(shlib_major) +endif +# Make sure we have a link to a name without any version numbers +ifneq ($(shlib), $(shlib_bare)) + rm -f $(shlib_bare) + $(LN_S) $(shlib) $(shlib_bare) +endif +endif # shlib_major + +# Where possible, restrict the symbols exported by the library to just the +# official list, so as to avoid unintentional ABI changes. On recent Darwin +# this also quiets multiply-defined-symbol warnings in programs that use +# libpgport along with libpq. +ifneq (,$(SHLIB_EXPORTS)) +ifdef BUILD.exports +$(shlib): $(SHLIB_EXPORTS:%.txt=%.list) + +$(SHLIB_EXPORTS:%.txt=%.list): %.list: %.txt + $(BUILD.exports) +endif +endif + +else # PORTNAME == aix + +# AIX case +$(shlib) $(stlib): $(OBJS) + $(LINK.static) $(stlib) $^ + $(RANLIB) $(stlib) + $(MKLDEXPORT) $(stlib) >$(exports_file) + $(COMPILER) -o $(shlib) $(stlib) -Wl,-bE:$(exports_file) $(LDFLAGS) $(LDFLAGS_SL) $(SHLIB_LINK) + rm -f $(stlib) + $(AR) $(AROPT) $(stlib) $(shlib) + +endif # PORTNAME == aix + +else # PORTNAME == cygwin || PORTNAME == win32 + +# Cygwin or Win32 case + +# If SHLIB_EXPORTS is set, the rules below will build a .def file from +# that. Else we build a temporary one here. +ifeq (,$(SHLIB_EXPORTS)) +DLL_DEFFILE = lib$(NAME)dll.def +exports_file = $(DLL_DEFFILE) + +$(exports_file): $(OBJS) + $(DLLTOOL) --export-all $(DLLTOOL_DEFFLAGS) --output-def $@ $^ +else +DLL_DEFFILE = lib$(NAME)dll.def +endif + +ifeq "$(WIN)" "64" +$(shlib): $(OBJS) $(DLL_DEFFILE) + $(LD) -shared --dll -o $@ $(OBJS) $(DLL_DEFFILE) +ifeq "$(AR)" "x86_64-w64-mingw32-ar" +$(stlib): $(OBJS) + $(AR) rcs $@ $(OBJS) +else +$(stlib): $(OBJS) + $(AR) rcs --target pe-x86-64 $@ $(OBJS) +endif +else +$(shlib): $(OBJS) $(DLL_DEFFILE) + $(DLLWRAP) -o $@ --dllname $(shlib) $(DLLWRAP_FLAGS) --def $(DLL_DEFFILE) $(OBJS) $(LDFLAGS) $(LDFLAGS_SL) $(SHLIB_LINK) +$(stlib): $(OBJS) + $(AR) rcs $@ $(OBJS) +endif + +endif # PORTNAME == cygwin || PORTNAME == win32 + +endif # enable_shared + + +# We need several not-quite-identical variants of .DEF files to build +# DLLs for Windows. These are made from the single source file +# exports.txt. Since we can't assume that Windows boxes will have +# sed, the .DEF files are always built and included in distribution +# tarballs. + +ifneq (,$(SHLIB_EXPORTS)) +distprep: lib$(NAME)dll.def lib$(NAME)ddll.def blib$(NAME)dll.def + +UC_NAME = $(shell echo $(NAME) | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + +lib$(NAME)dll.def: $(SHLIB_EXPORTS) + echo '; DEF file for MS VC++' >$@ + echo 'LIBRARY LIB$(UC_NAME)' >>$@ + echo 'EXPORTS' >>$@ + sed -e '/^#/d' -e 's/^\(.*[ ]\)\([0-9][0-9]*\)/ \1@ \2/' $< >>$@ + +lib$(NAME)ddll.def: $(SHLIB_EXPORTS) + echo '; DEF file for MS VC++' >$@ + echo 'LIBRARY LIB$(UC_NAME)D' >>$@ + echo 'EXPORTS' >>$@ + sed -e '/^#/d' -e 's/^\(.*[ ]\)\([0-9][0-9]*\)/ \1@ \2/' $< >>$@ + +blib$(NAME)dll.def: $(SHLIB_EXPORTS) + echo '; DEF file for Borland C++ Builder' >$@ + echo 'LIBRARY BLIB$(UC_NAME)' >>$@ + echo 'EXPORTS' >>$@ + sed -e '/^#/d' -e 's/^\(.*[ ]\)\([0-9][0-9]*\)/ _\1@ \2/' $< >>$@ + echo >>$@ + echo '; Aliases for MS compatible names' >> $@ + sed -e '/^#/d' -e 's/^\(.*[ ]\)\([0-9][0-9]*\)/ \1= _\1/' $< | sed 's/ *$$//' >>$@ +endif # SHLIB_EXPORTS + + +## +## INSTALL +## + +.PHONY: install-lib install-lib-static install-lib-shared installdirs-lib +install-lib: install-lib-shared +ifdef soname +install-lib: install-lib-static +endif + +install-lib-static: $(stlib) installdirs-lib + $(INSTALL_STLIB) $< '$(DESTDIR)$(libdir)/$(stlib)' +ifeq ($(PORTNAME), darwin) + cd '$(DESTDIR)$(libdir)' && \ + ranlib $(stlib) +endif + +ifeq ($(enable_shared), yes) +install-lib-shared: $(shlib) installdirs-lib +ifdef soname +# we don't install $(shlib) on AIX +# (see http://archives.postgresql.org/message-id/52EF20B2E3209443BC37736D00C3C1380A6E79FE@EXADV1.host.magwien.gv.at) +ifneq ($(PORTNAME), aix) + $(INSTALL_SHLIB) $< '$(DESTDIR)$(libdir)/$(shlib)' +ifneq ($(PORTNAME), cygwin) +ifneq ($(PORTNAME), win32) +ifneq ($(shlib), $(shlib_major)) + cd '$(DESTDIR)$(libdir)' && \ + rm -f $(shlib_major) && \ + $(LN_S) $(shlib) $(shlib_major) +endif +ifneq ($(shlib), $(shlib_bare)) + cd '$(DESTDIR)$(libdir)' && \ + rm -f $(shlib_bare) && \ + $(LN_S) $(shlib) $(shlib_bare) +endif +endif # not win32 +endif # not cygwin +endif # not aix +else # no soname + $(INSTALL_SHLIB) $< '$(DESTDIR)$(pkglibdir)/$(shlib)' +endif +else # not enable_shared +ifndef soname +install-lib-shared: + @echo "*****"; \ + echo "* Module $(NAME) was not installed due to lack of shared library support."; \ + echo "*****" +endif +endif # enable_shared + + +installdirs-lib: +ifdef soname + $(MKDIR_P) '$(DESTDIR)$(libdir)' +else + $(MKDIR_P) '$(DESTDIR)$(pkglibdir)' +endif + + +## +## UNINSTALL +## + +.PHONY: uninstall-lib +uninstall-lib: +ifdef soname + rm -f '$(DESTDIR)$(libdir)/$(stlib)' +ifeq ($(enable_shared), yes) + rm -f '$(DESTDIR)$(libdir)/$(shlib_bare)' \ + '$(DESTDIR)$(libdir)/$(shlib_major)' \ + '$(DESTDIR)$(libdir)/$(shlib)' +endif # enable_shared +else # no soname + rm -f '$(DESTDIR)$(pkglibdir)/$(shlib)' +endif # no soname + + +## +## CLEAN +## + +.PHONY: clean-lib +clean-lib: + rm -f $(shlib) $(shlib_bare) $(shlib_major) $(stlib) $(exports_file) + +ifneq (,$(SHLIB_EXPORTS)) +maintainer-clean-lib: + rm -f lib$(NAME)dll.def lib$(NAME)ddll.def blib$(NAME)dll.def +endif diff --git a/src/libpq/Makefile.win b/src/libpq/Makefile.win new file mode 100644 index 00000000..0111a252 --- /dev/null +++ b/src/libpq/Makefile.win @@ -0,0 +1,135 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/interfaces/libpq library +# +# Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/interfaces/libpq/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/interfaces/libpq +top_builddir = .. + +ifeq "$(WIN)" "64" +include Makefile.global.win64 +else +include Makefile.global.win32 +endif + +all: pg_config.h pg_config_os.h +pg_config.h: pg_config.h.win + cp pg_config.h.win pg_config.h +pg_config_os.h: pg_config_os.h.win + cp pg_config_os.h.win pg_config_os.h +pg_config_paths.h: pg_config_paths.h.win + cp pg_config_paths.h.win pg_config_paths.h + + + +# shared library parameters +NAME= pq +SO_MAJOR_VERSION= 5 +SO_MINOR_VERSION= 4 + +override CPPFLAGS := -DFRONTEND -DUNSAFE_STAT_OK -I$(srcdir) $(CPPFLAGS) -Iwininclude +ifneq ($(PORTNAME), win32) +override CFLAGS += $(PTHREAD_CFLAGS) +endif + +# Need to recompile any external C files because we need +# all object files to use the same compile flags as libpq; some +# platforms require special flags. +LIBS := $(LIBS:-lpgport=) + +# We can't use Makefile variables here because the MSVC build system scrapes +# OBJS from this file. +OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ + fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \ + libpq-events.o +# libpgport C files we always use +OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o thread.o +# libpgport C files that are needed if identified by configure +OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o win32setlocale.o, $(LIBOBJS)) +# backend/libpq +OBJS += ip.o md5.o +# utils/mb +OBJS += encnames.o wchar.o + +ifeq ($(PORTNAME), cygwin) +override shlib = cyg$(NAME)$(DLSUFFIX) +endif + +ifeq ($(PORTNAME), win32) +# pgsleep.o is from libpgport +OBJS += pgsleep.o win32.o libpqrc.o + +libpqrc.o: libpq.rc + $(WINDRES) -i $< -o $@ + +ifeq ($(enable_thread_safety), yes) +OBJS += pthread-win32.o +endif +endif + + +# Add libraries that libpq depends (or might depend) on into the +# shared library link. (The order in which you list them here doesn't +# matter.) +ifneq ($(PORTNAME), win32) +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS) +else +SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE) +endif +ifeq ($(PORTNAME), win32) +SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS)) +endif + +SHLIB_EXPORTS = exports.txt + +all: all-static-lib + +# Shared library stuff +include Makefile.shlib + + +distprep: libpq-dist.rc + +libpq.rc libpq-dist.rc: libpq.rc.in + sed -e 's/\(VERSION.*\),0 *$$/\1,'`date '+%y%j' | sed 's/^0*//'`'/' $< >$@ + +# Depend on Makefile.global to force rebuild on re-run of configure. +# (But libpq-dist.rc is shipped in the distribution for shell-less +# installations and is only updated by distprep.) +# libpq.rc: Makefile.global +#not for static port + +fe-connect.o: fe-connect.c pg_config_paths.h + +install: all installdirs install-lib + $(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)' + $(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)' + $(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)' + $(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)' + $(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample' + +installdirs: installdirs-lib + $(MKDIR_P) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)' + +uninstall: uninstall-lib + rm -f '$(DESTDIR)$(includedir)/libpq-fe.h' + rm -f '$(DESTDIR)$(includedir)/libpq-events.h' + rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h' + rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h' + rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample' + +clean distclean: clean-lib + rm -f $(OBJS) pthread.h libpq.rc +# Might be left over from a Win32 client-only build + rm -f pg_config.h + rm -f pg_config_os.h + rm -f pg_config_paths.h + +maintainer-clean: distclean maintainer-clean-lib + rm -f libpq-dist.rc diff --git a/src/libpq/README b/src/libpq/README new file mode 100644 index 00000000..0dcef75a --- /dev/null +++ b/src/libpq/README @@ -0,0 +1,3 @@ +src/interfaces/libpq/README + +This directory contains the C version of Libpq, the POSTGRES frontend library. diff --git a/src/libpq/bcc32.mak b/src/libpq/bcc32.mak new file mode 100644 index 00000000..fbef737e --- /dev/null +++ b/src/libpq/bcc32.mak @@ -0,0 +1,297 @@ +# Makefile for Borland C++ 5.5 + +# Will build a Win32 static library libpq.lib +# and a Win32 dynamic library libpq.dll with import library libpqdll.lib + +# Borland C++ base install directory goes here +# BCB=c:\Borland\Bcc55 + +!IF "$(BCB)" == "" +!MESSAGE You must edit bcc32.mak and define BCB at the top +!ERROR misssing BCB +!ENDIF + +!IF "$(__NMAKE__)" == "" +!MESSAGE You must use the -N compatibility flag, e.g. make -N -f bcc32.make +!ERROR missing -N +!ENDIF + +!MESSAGE Building the Win32 DLL and Static Library... +!MESSAGE +!IF "$(CFG)" == "" +CFG=Release +!MESSAGE No configuration specified. Defaulting to Release. +!MESSAGE +!ELSE +!MESSAGE Configuration "$(CFG)" +!MESSAGE +!ENDIF + +!IF "$(CFG)" != "Release" && "$(CFG)" != "Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running MAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE make -N -DCFG=[Release | Debug] -f bcc32.mak +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Release" (Win32 Release DLL and Static Library) +!MESSAGE "Debug" (Win32 Debug DLL and Static Library) +!MESSAGE +!ERROR An invalid configuration was specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "Debug" +DEBUG=1 +OUTDIR=.\Debug +INTDIR=.\Debug +!ELSE +OUTDIR=.\Release +INTDIR=.\Release +!ENDIF + +OUTFILENAME=blibpq + +USERDEFINES=FRONTEND;NDEBUG;WIN32;_WINDOWS + +CPP=bcc32.exe +CPP_PROJ = -I..\..\include\port\win32_msvc;$(BCB)\include;..\..\include;..\..\include\port\win32;..\..\port -n"$(INTDIR)" -WD -c -D$(USERDEFINES) -tWM \ + -a8 -X -w-use -w-par -w-pia -w-csu -w-aus -w-ccc + +!IFDEF DEBUG +CPP_PROJ = $(CPP_PROJ) -Od -r- -k -v -y -vi- -D_DEBUG +!else +CPP_PROJ = $(CPP_PROJ) -O -Oi -OS -DNDEBUG +!endif + +ALL : config "$(OUTDIR)" "$(OUTDIR)\blibpq.dll" "$(OUTDIR)\blibpq.lib" + +CLEAN : + -@erase "$(INTDIR)\getaddrinfo.obj" + -@erase "$(INTDIR)\pgstrcasecmp.obj" + -@erase "$(INTDIR)\thread.obj" + -@erase "$(INTDIR)\inet_aton.obj" + -@erase "$(INTDIR)\crypt.obj" + -@erase "$(INTDIR)\noblock.obj" + -@erase "$(INTDIR)\chklocale.obj" + -@erase "$(INTDIR)\inet_net_ntop.obj" + -@erase "$(INTDIR)\md5.obj" + -@erase "$(INTDIR)\ip.obj" + -@erase "$(INTDIR)\fe-auth.obj" + -@erase "$(INTDIR)\fe-protocol2.obj" + -@erase "$(INTDIR)\fe-protocol3.obj" + -@erase "$(INTDIR)\fe-connect.obj" + -@erase "$(INTDIR)\fe-exec.obj" + -@erase "$(INTDIR)\fe-lobj.obj" + -@erase "$(INTDIR)\fe-misc.obj" + -@erase "$(INTDIR)\fe-print.obj" + -@erase "$(INTDIR)\fe-secure.obj" + -@erase "$(INTDIR)\libpq-events.obj" + -@erase "$(INTDIR)\pqexpbuffer.obj" + -@erase "$(INTDIR)\pqsignal.obj" + -@erase "$(INTDIR)\win32.obj" + -@erase "$(INTDIR)\wchar.obj" + -@erase "$(INTDIR)\encnames.obj" + -@erase "$(INTDIR)\pthread-win32.obj" + -@erase "$(INTDIR)\snprintf.obj" + -@erase "$(INTDIR)\strlcpy.obj" + -@erase "$(INTDIR)\dirent.obj" + -@erase "$(INTDIR)\dirmod.obj" + -@erase "$(INTDIR)\pgsleep.obj" + -@erase "$(INTDIR)\open.obj" + -@erase "$(INTDIR)\win32error.obj" + -@erase "$(OUTDIR)\$(OUTFILENAME).lib" + -@erase "$(OUTDIR)\$(OUTFILENAME)dll.lib" + -@erase "$(OUTDIR)\libpq.res" + -@erase "$(OUTDIR)\$(OUTFILENAME).dll" + -@erase "$(OUTDIR)\$(OUTFILENAME).tds" + -@erase "$(INTDIR)\pg_config_paths.h" + + +LIB32=tlib.exe +LIB32_FLAGS= +LIB32_OBJS= \ + "$(INTDIR)\win32.obj" \ + "$(INTDIR)\getaddrinfo.obj" \ + "$(INTDIR)\pgstrcasecmp.obj" \ + "$(INTDIR)\thread.obj" \ + "$(INTDIR)\inet_aton.obj" \ + "$(INTDIR)\crypt.obj" \ + "$(INTDIR)\noblock.obj" \ + "$(INTDIR)\chklocale.obj" \ + "$(INTDIR)\inet_net_ntop.obj" \ + "$(INTDIR)\md5.obj" \ + "$(INTDIR)\ip.obj" \ + "$(INTDIR)\fe-auth.obj" \ + "$(INTDIR)\fe-protocol2.obj" \ + "$(INTDIR)\fe-protocol3.obj" \ + "$(INTDIR)\fe-connect.obj" \ + "$(INTDIR)\fe-exec.obj" \ + "$(INTDIR)\fe-lobj.obj" \ + "$(INTDIR)\fe-misc.obj" \ + "$(INTDIR)\fe-print.obj" \ + "$(INTDIR)\fe-secure.obj" \ + "$(INTDIR)\libpq-events.obj" \ + "$(INTDIR)\pqexpbuffer.obj" \ + "$(INTDIR)\pqsignal.obj" \ + "$(INTDIR)\wchar.obj" \ + "$(INTDIR)\encnames.obj" \ + "$(INTDIR)\snprintf.obj" \ + "$(INTDIR)\strlcpy.obj" \ + "$(INTDIR)\dirent.obj" \ + "$(INTDIR)\dirmod.obj" \ + "$(INTDIR)\pgsleep.obj" \ + "$(INTDIR)\open.obj" \ + "$(INTDIR)\win32error.obj" \ + "$(INTDIR)\pthread-win32.obj" + + +config: ..\..\include\pg_config.h ..\..\include\pg_config_os.h pg_config_paths.h + +..\..\include\pg_config.h: ..\..\include\pg_config.h.win32 + copy ..\..\include\pg_config.h.win32 ..\..\include\pg_config.h + +..\..\include\pg_config_os.h: ..\..\include\port\win32.h + copy ..\..\include\port\win32.h ..\..\include\pg_config_os.h + +# Have to use \# so # isn't treated as a comment, but MSVC doesn't like this +pg_config_paths.h: bcc32.mak + echo \#define SYSCONFDIR "" > pg_config_paths.h + +"$(OUTDIR)" : + @if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +RSC=brcc32.exe +RSC_PROJ=-l 0x409 -i$(BCB)\include -fo"$(INTDIR)\libpq.res" + +LINK32=ilink32.exe +LINK32_FLAGS = -Gn -L$(BCB)\lib;$(INTDIR); -x -Tpd -v + +# @<< is a Response file, http://www.opussoftware.com/tutorial/TutMakefile.htm + +"$(OUTDIR)\blibpq.dll": "$(OUTDIR)\blibpq.lib" "$(INTDIR)\libpq.res" blibpqdll.def + $(LINK32) @<< + $(LINK32_FLAGS) + + c0d32.obj , + + $@,, + + "$(OUTDIR)\blibpq.lib" import32.lib cw32mt.lib, + + blibpqdll.def,"$(INTDIR)\libpq.res" +<< + implib -w "$(OUTDIR)\blibpqdll.lib" blibpqdll.def $@ + +"$(INTDIR)\libpq.res" : "$(INTDIR)" libpq-dist.rc + $(RSC) $(RSC_PROJ) libpq-dist.rc + +"$(OUTDIR)\blibpq.lib": $(LIB32_OBJS) + $(LIB32) $@ @<< ++-"$(**: =" &^ ++-")" +<< + + +"$(INTDIR)\getaddrinfo.obj" : ..\..\port\getaddrinfo.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\getaddrinfo.c +<< + +"$(INTDIR)\pgstrcasecmp.obj" : ..\..\port\pgstrcasecmp.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\pgstrcasecmp.c +<< + +"$(INTDIR)\thread.obj" : ..\..\port\thread.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\thread.c +<< + +"$(INTDIR)\inet_aton.obj" : ..\..\port\inet_aton.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\inet_aton.c +<< + +"$(INTDIR)\crypt.obj" : ..\..\port\crypt.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\crypt.c +<< + +"$(INTDIR)\noblock.obj" : ..\..\port\noblock.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\noblock.c +<< + +"$(INTDIR)\chklocale.obj" : ..\..\port\chklocale.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\chklocale.c +<< + +"$(INTDIR)\inet_net_ntop.obj" : ..\..\port\inet_net_ntop.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\inet_net_ntop.c +<< + +"$(INTDIR)\md5.obj" : ..\..\backend\libpq\md5.c + $(CPP) @<< + $(CPP_PROJ) ..\..\backend\libpq\md5.c +<< + +"$(INTDIR)\ip.obj" : ..\..\backend\libpq\ip.c + $(CPP) @<< + $(CPP_PROJ) ..\..\backend\libpq\ip.c +<< + +"$(INTDIR)\wchar.obj" : ..\..\backend\utils\mb\wchar.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\backend\utils\mb\wchar.c +<< + + +"$(INTDIR)\encnames.obj" : ..\..\backend\utils\mb\encnames.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\backend\utils\mb\encnames.c +<< + +"$(INTDIR)\snprintf.obj" : ..\..\port\snprintf.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\snprintf.c +<< + +"$(INTDIR)\strlcpy.obj" : ..\..\port\strlcpy.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\strlcpy.c +<< + +"$(INTDIR)\dirent.obj" : ..\..\port\dirent.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\dirent.c +<< + +"$(INTDIR)\dirmod.obj" : ..\..\port\dirmod.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\dirmod.c +<< + +"$(INTDIR)\pgsleep.obj" : ..\..\port\pgsleep.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\pgsleep.c +<< + +"$(INTDIR)\open.obj" : ..\..\port\open.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\open.c +<< + +"$(INTDIR)\win32error.obj" : ..\..\port\win32error.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\win32error.c +<< + + +.c.obj: + $(CPP) $(CPP_PROJ) $< diff --git a/src/libpq/blibpqdll.def b/src/libpq/blibpqdll.def new file mode 100644 index 00000000..c7c00ea4 --- /dev/null +++ b/src/libpq/blibpqdll.def @@ -0,0 +1,325 @@ +; DEF file for Borland C++ Builder +LIBRARY BLIBPQ +EXPORTS + _PQconnectdb @ 1 + _PQsetdbLogin @ 2 + _PQconndefaults @ 3 + _PQfinish @ 4 + _PQreset @ 5 + _PQrequestCancel @ 6 + _PQdb @ 7 + _PQuser @ 8 + _PQpass @ 9 + _PQhost @ 10 + _PQport @ 11 + _PQtty @ 12 + _PQoptions @ 13 + _PQstatus @ 14 + _PQerrorMessage @ 15 + _PQsocket @ 16 + _PQbackendPID @ 17 + _PQtrace @ 18 + _PQuntrace @ 19 + _PQsetNoticeProcessor @ 20 + _PQexec @ 21 + _PQnotifies @ 22 + _PQsendQuery @ 23 + _PQgetResult @ 24 + _PQisBusy @ 25 + _PQconsumeInput @ 26 + _PQgetline @ 27 + _PQputline @ 28 + _PQgetlineAsync @ 29 + _PQputnbytes @ 30 + _PQendcopy @ 31 + _PQfn @ 32 + _PQresultStatus @ 33 + _PQntuples @ 34 + _PQnfields @ 35 + _PQbinaryTuples @ 36 + _PQfname @ 37 + _PQfnumber @ 38 + _PQftype @ 39 + _PQfsize @ 40 + _PQfmod @ 41 + _PQcmdStatus @ 42 + _PQoidStatus @ 43 + _PQcmdTuples @ 44 + _PQgetvalue @ 45 + _PQgetlength @ 46 + _PQgetisnull @ 47 + _PQclear @ 48 + _PQmakeEmptyPGresult @ 49 + _PQprint @ 50 + _PQdisplayTuples @ 51 + _PQprintTuples @ 52 + _lo_open @ 53 + _lo_close @ 54 + _lo_read @ 55 + _lo_write @ 56 + _lo_lseek @ 57 + _lo_creat @ 58 + _lo_tell @ 59 + _lo_unlink @ 60 + _lo_import @ 61 + _lo_export @ 62 + _pgresStatus @ 63 + _PQmblen @ 64 + _PQresultErrorMessage @ 65 + _PQresStatus @ 66 + _termPQExpBuffer @ 67 + _appendPQExpBufferChar @ 68 + _initPQExpBuffer @ 69 + _resetPQExpBuffer @ 70 + _PQoidValue @ 71 + _PQclientEncoding @ 72 + _PQenv2encoding @ 73 + _appendBinaryPQExpBuffer @ 74 + _appendPQExpBufferStr @ 75 + _destroyPQExpBuffer @ 76 + _createPQExpBuffer @ 77 + _PQconninfoFree @ 78 + _PQconnectPoll @ 79 + _PQconnectStart @ 80 + _PQflush @ 81 + _PQisnonblocking @ 82 + _PQresetPoll @ 83 + _PQresetStart @ 84 + _PQsetClientEncoding @ 85 + _PQsetnonblocking @ 86 + _PQfreeNotify @ 87 + _PQescapeString @ 88 + _PQescapeBytea @ 89 + _printfPQExpBuffer @ 90 + _appendPQExpBuffer @ 91 + _pg_encoding_to_char @ 92 + _pg_utf_mblen @ 93 + _PQunescapeBytea @ 94 + _PQfreemem @ 95 + _PQtransactionStatus @ 96 + _PQparameterStatus @ 97 + _PQprotocolVersion @ 98 + _PQsetErrorVerbosity @ 99 + _PQsetNoticeReceiver @ 100 + _PQexecParams @ 101 + _PQsendQueryParams @ 102 + _PQputCopyData @ 103 + _PQputCopyEnd @ 104 + _PQgetCopyData @ 105 + _PQresultErrorField @ 106 + _PQftable @ 107 + _PQftablecol @ 108 + _PQfformat @ 109 + _PQexecPrepared @ 110 + _PQsendQueryPrepared @ 111 + _PQdsplen @ 112 + _PQserverVersion @ 113 + _PQgetssl @ 114 + _pg_char_to_encoding @ 115 + _pg_valid_server_encoding @ 116 + _pqsignal @ 117 + _PQprepare @ 118 + _PQsendPrepare @ 119 + _PQgetCancel @ 120 + _PQfreeCancel @ 121 + _PQcancel @ 122 + _lo_create @ 123 + _PQinitSSL @ 124 + _PQregisterThreadLock @ 125 + _PQescapeStringConn @ 126 + _PQescapeByteaConn @ 127 + _PQencryptPassword @ 128 + _PQisthreadsafe @ 129 + _enlargePQExpBuffer @ 130 + _PQnparams @ 131 + _PQparamtype @ 132 + _PQdescribePrepared @ 133 + _PQdescribePortal @ 134 + _PQsendDescribePrepared @ 135 + _PQsendDescribePortal @ 136 + _lo_truncate @ 137 + _PQconnectionUsedPassword @ 138 + _pg_valid_server_encoding_id @ 139 + _PQconnectionNeedsPassword @ 140 + _lo_import_with_oid @ 141 + _PQcopyResult @ 142 + _PQsetResultAttrs @ 143 + _PQsetvalue @ 144 + _PQresultAlloc @ 145 + _PQregisterEventProc @ 146 + _PQinstanceData @ 147 + _PQsetInstanceData @ 148 + _PQresultInstanceData @ 149 + _PQresultSetInstanceData @ 150 + _PQfireResultCreateEvents @ 151 + _PQconninfoParse @ 152 + _PQinitOpenSSL @ 153 + _PQescapeLiteral @ 154 + _PQescapeIdentifier @ 155 + _PQconnectdbParams @ 156 + _PQconnectStartParams @ 157 + _PQping @ 158 + _PQpingParams @ 159 + _PQlibVersion @ 160 + +; Aliases for MS compatible names + PQconnectdb = _PQconnectdb + PQsetdbLogin = _PQsetdbLogin + PQconndefaults = _PQconndefaults + PQfinish = _PQfinish + PQreset = _PQreset + PQrequestCancel = _PQrequestCancel + PQdb = _PQdb + PQuser = _PQuser + PQpass = _PQpass + PQhost = _PQhost + PQport = _PQport + PQtty = _PQtty + PQoptions = _PQoptions + PQstatus = _PQstatus + PQerrorMessage = _PQerrorMessage + PQsocket = _PQsocket + PQbackendPID = _PQbackendPID + PQtrace = _PQtrace + PQuntrace = _PQuntrace + PQsetNoticeProcessor = _PQsetNoticeProcessor + PQexec = _PQexec + PQnotifies = _PQnotifies + PQsendQuery = _PQsendQuery + PQgetResult = _PQgetResult + PQisBusy = _PQisBusy + PQconsumeInput = _PQconsumeInput + PQgetline = _PQgetline + PQputline = _PQputline + PQgetlineAsync = _PQgetlineAsync + PQputnbytes = _PQputnbytes + PQendcopy = _PQendcopy + PQfn = _PQfn + PQresultStatus = _PQresultStatus + PQntuples = _PQntuples + PQnfields = _PQnfields + PQbinaryTuples = _PQbinaryTuples + PQfname = _PQfname + PQfnumber = _PQfnumber + PQftype = _PQftype + PQfsize = _PQfsize + PQfmod = _PQfmod + PQcmdStatus = _PQcmdStatus + PQoidStatus = _PQoidStatus + PQcmdTuples = _PQcmdTuples + PQgetvalue = _PQgetvalue + PQgetlength = _PQgetlength + PQgetisnull = _PQgetisnull + PQclear = _PQclear + PQmakeEmptyPGresult = _PQmakeEmptyPGresult + PQprint = _PQprint + PQdisplayTuples = _PQdisplayTuples + PQprintTuples = _PQprintTuples + lo_open = _lo_open + lo_close = _lo_close + lo_read = _lo_read + lo_write = _lo_write + lo_lseek = _lo_lseek + lo_creat = _lo_creat + lo_tell = _lo_tell + lo_unlink = _lo_unlink + lo_import = _lo_import + lo_export = _lo_export + pgresStatus = _pgresStatus + PQmblen = _PQmblen + PQresultErrorMessage = _PQresultErrorMessage + PQresStatus = _PQresStatus + termPQExpBuffer = _termPQExpBuffer + appendPQExpBufferChar = _appendPQExpBufferChar + initPQExpBuffer = _initPQExpBuffer + resetPQExpBuffer = _resetPQExpBuffer + PQoidValue = _PQoidValue + PQclientEncoding = _PQclientEncoding + PQenv2encoding = _PQenv2encoding + appendBinaryPQExpBuffer = _appendBinaryPQExpBuffer + appendPQExpBufferStr = _appendPQExpBufferStr + destroyPQExpBuffer = _destroyPQExpBuffer + createPQExpBuffer = _createPQExpBuffer + PQconninfoFree = _PQconninfoFree + PQconnectPoll = _PQconnectPoll + PQconnectStart = _PQconnectStart + PQflush = _PQflush + PQisnonblocking = _PQisnonblocking + PQresetPoll = _PQresetPoll + PQresetStart = _PQresetStart + PQsetClientEncoding = _PQsetClientEncoding + PQsetnonblocking = _PQsetnonblocking + PQfreeNotify = _PQfreeNotify + PQescapeString = _PQescapeString + PQescapeBytea = _PQescapeBytea + printfPQExpBuffer = _printfPQExpBuffer + appendPQExpBuffer = _appendPQExpBuffer + pg_encoding_to_char = _pg_encoding_to_char + pg_utf_mblen = _pg_utf_mblen + PQunescapeBytea = _PQunescapeBytea + PQfreemem = _PQfreemem + PQtransactionStatus = _PQtransactionStatus + PQparameterStatus = _PQparameterStatus + PQprotocolVersion = _PQprotocolVersion + PQsetErrorVerbosity = _PQsetErrorVerbosity + PQsetNoticeReceiver = _PQsetNoticeReceiver + PQexecParams = _PQexecParams + PQsendQueryParams = _PQsendQueryParams + PQputCopyData = _PQputCopyData + PQputCopyEnd = _PQputCopyEnd + PQgetCopyData = _PQgetCopyData + PQresultErrorField = _PQresultErrorField + PQftable = _PQftable + PQftablecol = _PQftablecol + PQfformat = _PQfformat + PQexecPrepared = _PQexecPrepared + PQsendQueryPrepared = _PQsendQueryPrepared + PQdsplen = _PQdsplen + PQserverVersion = _PQserverVersion + PQgetssl = _PQgetssl + pg_char_to_encoding = _pg_char_to_encoding + pg_valid_server_encoding = _pg_valid_server_encoding + pqsignal = _pqsignal + PQprepare = _PQprepare + PQsendPrepare = _PQsendPrepare + PQgetCancel = _PQgetCancel + PQfreeCancel = _PQfreeCancel + PQcancel = _PQcancel + lo_create = _lo_create + PQinitSSL = _PQinitSSL + PQregisterThreadLock = _PQregisterThreadLock + PQescapeStringConn = _PQescapeStringConn + PQescapeByteaConn = _PQescapeByteaConn + PQencryptPassword = _PQencryptPassword + PQisthreadsafe = _PQisthreadsafe + enlargePQExpBuffer = _enlargePQExpBuffer + PQnparams = _PQnparams + PQparamtype = _PQparamtype + PQdescribePrepared = _PQdescribePrepared + PQdescribePortal = _PQdescribePortal + PQsendDescribePrepared = _PQsendDescribePrepared + PQsendDescribePortal = _PQsendDescribePortal + lo_truncate = _lo_truncate + PQconnectionUsedPassword = _PQconnectionUsedPassword + pg_valid_server_encoding_id = _pg_valid_server_encoding_id + PQconnectionNeedsPassword = _PQconnectionNeedsPassword + lo_import_with_oid = _lo_import_with_oid + PQcopyResult = _PQcopyResult + PQsetResultAttrs = _PQsetResultAttrs + PQsetvalue = _PQsetvalue + PQresultAlloc = _PQresultAlloc + PQregisterEventProc = _PQregisterEventProc + PQinstanceData = _PQinstanceData + PQsetInstanceData = _PQsetInstanceData + PQresultInstanceData = _PQresultInstanceData + PQresultSetInstanceData = _PQresultSetInstanceData + PQfireResultCreateEvents = _PQfireResultCreateEvents + PQconninfoParse = _PQconninfoParse + PQinitOpenSSL = _PQinitOpenSSL + PQescapeLiteral = _PQescapeLiteral + PQescapeIdentifier = _PQescapeIdentifier + PQconnectdbParams = _PQconnectdbParams + PQconnectStartParams = _PQconnectStartParams + PQping = _PQping + PQpingParams = _PQpingParams + PQlibVersion = _PQlibVersion diff --git a/src/libpq/c.h b/src/libpq/c.h new file mode 100644 index 00000000..03918608 --- /dev/null +++ b/src/libpq/c.h @@ -0,0 +1,853 @@ +/*------------------------------------------------------------------------- + * + * c.h + * Fundamental C definitions. This is included by every .c file in + * PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate). + * + * Note that the definitions here are not intended to be exposed to clients + * of the frontend interface libraries --- so we don't worry much about + * polluting the namespace with lots of stuff... + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/c.h + * + *------------------------------------------------------------------------- + */ +/* + *---------------------------------------------------------------- + * TABLE OF CONTENTS + * + * When adding stuff to this file, please try to put stuff + * into the relevant section, or add new sections as appropriate. + * + * section description + * ------- ------------------------------------------------ + * 0) pg_config.h and standard system headers + * 1) hacks to cope with non-ANSI C compilers + * 2) bool, true, false, TRUE, FALSE, NULL + * 3) standard system types + * 4) IsValid macros for system types + * 5) offsetof, lengthof, endof, alignment + * 6) widely useful macros + * 7) random stuff + * 8) system-specific hacks + * + * NOTE: since this file is included by both frontend and backend modules, it's + * almost certainly wrong to put an "extern" declaration here. typedefs and + * macros are the kind of thing that might go here. + * + *---------------------------------------------------------------- + */ +#ifndef C_H +#define C_H + +/* + * We have to include stdlib.h here because it defines many of these macros + * on some platforms, and we only want our definitions used if stdlib.h doesn't + * have its own. The same goes for stddef and stdarg if present. + */ + +#include "pg_config.h" +#include "pg_config_manual.h" /* must be after pg_config.h */ +#if !defined(WIN32) && !defined(__CYGWIN__) /* win32 will include further + * down */ +#include "pg_config_os.h" /* must be before any system header files */ +#endif +#include "postgres_ext.h" + +#if _MSC_VER >= 1400 || defined(WIN64) +#define errcode __msvc_errcode +#include +#undef errcode +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include + +#include +#if defined(WIN32) || defined(__CYGWIN__) +#include /* ensure O_BINARY is available */ +#endif +#ifdef HAVE_SUPPORTDEFS_H +#include +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* We have to redefine some system functions after they are included above. */ +#include "pg_config_os.h" +#endif + +/* Must be before gettext() games below */ +#include + +#define _(x) gettext(x) + +#ifdef ENABLE_NLS +#include +#else +#define gettext(x) (x) +#define dgettext(d,x) (x) +#define ngettext(s,p,n) ((n) == 1 ? (s) : (p)) +#define dngettext(d,s,p,n) ((n) == 1 ? (s) : (p)) +#endif + +/* + * Use this to mark string constants as needing translation at some later + * time, rather than immediately. This is useful for cases where you need + * access to the original string and translated string, and for cases where + * immediate translation is not possible, like when initializing global + * variables. + * http://www.gnu.org/software/autoconf/manual/gettext/Special-cases.html + */ +#define gettext_noop(x) (x) + + +/* ---------------------------------------------------------------- + * Section 1: hacks to cope with non-ANSI C compilers + * + * type prefixes (const, signed, volatile, inline) are handled in pg_config.h. + * ---------------------------------------------------------------- + */ + +/* + * CppAsString + * Convert the argument to a string, using the C preprocessor. + * CppConcat + * Concatenate two arguments together, using the C preprocessor. + * + * Note: the standard Autoconf macro AC_C_STRINGIZE actually only checks + * whether #identifier works, but if we have that we likely have ## too. + */ +#if defined(HAVE_STRINGIZE) + +#define CppAsString(identifier) #identifier +#define CppConcat(x, y) x##y +#else /* !HAVE_STRINGIZE */ + +#define CppAsString(identifier) "identifier" + +/* + * CppIdentity -- On Reiser based cpp's this is used to concatenate + * two tokens. That is + * CppIdentity(A)B ==> AB + * We renamed it to _private_CppIdentity because it should not + * be referenced outside this file. On other cpp's it + * produces A B. + */ +#define _priv_CppIdentity(x)x +#define CppConcat(x, y) _priv_CppIdentity(x)y +#endif /* !HAVE_STRINGIZE */ + +/* + * dummyret is used to set return values in macros that use ?: to make + * assignments. gcc wants these to be void, other compilers like char + */ +#ifdef __GNUC__ /* GNU cc */ +#define dummyret void +#else +#define dummyret char +#endif + +#ifndef __GNUC__ +#define __attribute__(_arg_) +#endif + +/* ---------------------------------------------------------------- + * Section 2: bool, true, false, TRUE, FALSE, NULL + * ---------------------------------------------------------------- + */ + +/* + * bool + * Boolean value, either true or false. + * + * XXX for C++ compilers, we assume the compiler has a compatible + * built-in definition of bool. + */ + +#ifndef __cplusplus + +#ifndef bool +typedef char bool; +#endif + +#ifndef true +#define true ((bool) 1) +#endif + +#ifndef false +#define false ((bool) 0) +#endif +#endif /* not C++ */ + +typedef bool *BoolPtr; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * NULL + * Null pointer. + */ +#ifndef NULL +#define NULL ((void *) 0) +#endif + + +/* ---------------------------------------------------------------- + * Section 3: standard system types + * ---------------------------------------------------------------- + */ + +/* + * Pointer + * Variable holding address of any memory resident object. + * + * XXX Pointer arithmetic is done with this, so it can't be void * + * under "true" ANSI compilers. + */ +typedef char *Pointer; + +/* + * intN + * Signed integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_INT8 +typedef signed char int8; /* == 8 bits */ +typedef signed short int16; /* == 16 bits */ +typedef signed int int32; /* == 32 bits */ +#endif /* not HAVE_INT8 */ + +/* + * uintN + * Unsigned integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_UINT8 +typedef unsigned char uint8; /* == 8 bits */ +typedef unsigned short uint16; /* == 16 bits */ +typedef unsigned int uint32; /* == 32 bits */ +#endif /* not HAVE_UINT8 */ + +/* + * bitsN + * Unit of bitwise operation, AT LEAST N BITS IN SIZE. + */ +typedef uint8 bits8; /* >= 8 bits */ +typedef uint16 bits16; /* >= 16 bits */ +typedef uint32 bits32; /* >= 32 bits */ + +/* + * 64-bit integers + */ +#ifdef HAVE_LONG_INT_64 +/* Plain "long int" fits, use it */ + +#ifndef HAVE_INT64 +typedef long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long int uint64; +#endif +#elif defined(HAVE_LONG_LONG_INT_64) +/* We have working support for "long long int", use that */ + +#ifndef HAVE_INT64 +typedef long long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long long int uint64; +#endif +#else +/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */ +#error must have a working 64-bit integer datatype +#endif + +/* Decide if we need to decorate 64-bit constants */ +#ifdef HAVE_LL_CONSTANTS +#define INT64CONST(x) ((int64) x##LL) +#define UINT64CONST(x) ((uint64) x##ULL) +#else +#define INT64CONST(x) ((int64) x) +#define UINT64CONST(x) ((uint64) x) +#endif + + +/* Select timestamp representation (float8 or int64) */ +#ifdef USE_INTEGER_DATETIMES +#define HAVE_INT64_TIMESTAMP +#endif + +/* sig_atomic_t is required by ANSI C, but may be missing on old platforms */ +#ifndef HAVE_SIG_ATOMIC_T +typedef int sig_atomic_t; +#endif + +/* + * Size + * Size of any memory resident object, as returned by sizeof. + */ +typedef size_t Size; + +/* + * Index + * Index into any memory resident array. + * + * Note: + * Indices are non negative. + */ +typedef unsigned int Index; + +/* + * Offset + * Offset into any memory resident array. + * + * Note: + * This differs from an Index in that an Index is always + * non negative, whereas Offset may be negative. + */ +typedef signed int Offset; + +/* + * Common Postgres datatype names (as used in the catalogs) + */ +typedef int16 int2; +typedef int32 int4; +typedef float float4; +typedef double float8; + +/* + * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId, + * CommandId + */ + +/* typedef Oid is in postgres_ext.h */ + +/* + * regproc is the type name used in the include/catalog headers, but + * RegProcedure is the preferred name in C code. + */ +typedef Oid regproc; +typedef regproc RegProcedure; + +typedef uint32 TransactionId; + +typedef uint32 LocalTransactionId; + +typedef uint32 SubTransactionId; + +#define InvalidSubTransactionId ((SubTransactionId) 0) +#define TopSubTransactionId ((SubTransactionId) 1) + +/* MultiXactId must be equivalent to TransactionId, to fit in t_xmax */ +typedef TransactionId MultiXactId; + +typedef uint32 MultiXactOffset; + +typedef uint32 CommandId; + +#define FirstCommandId ((CommandId) 0) + +/* + * Array indexing support + */ +#define MAXDIM 6 +typedef struct +{ + int indx[MAXDIM]; +} IntArray; + +/* ---------------- + * Variable-length datatypes all share the 'struct varlena' header. + * + * NOTE: for TOASTable types, this is an oversimplification, since the value + * may be compressed or moved out-of-line. However datatype-specific routines + * are mostly content to deal with de-TOASTed values only, and of course + * client-side routines should never see a TOASTed value. But even in a + * de-TOASTed value, beware of touching vl_len_ directly, as its representation + * is no longer convenient. It's recommended that code always use the VARDATA, + * VARSIZE, and SET_VARSIZE macros instead of relying on direct mentions of + * the struct fields. See postgres.h for details of the TOASTed form. + * ---------------- + */ +struct varlena +{ + char vl_len_[4]; /* Do not touch this field directly! */ + char vl_dat[1]; +}; + +#define VARHDRSZ ((int32) sizeof(int32)) + +/* + * These widely-used datatypes are just a varlena header and the data bytes. + * There is no terminating null or anything like that --- the data length is + * always VARSIZE(ptr) - VARHDRSZ. + */ +typedef struct varlena bytea; +typedef struct varlena text; +typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ +typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ + +/* + * Specialized array types. These are physically laid out just the same + * as regular arrays (so that the regular array subscripting code works + * with them). They exist as distinct types mostly for historical reasons: + * they have nonstandard I/O behavior which we don't want to change for fear + * of breaking applications that look at the system catalogs. There is also + * an implementation issue for oidvector: it's part of the primary key for + * pg_proc, and we can't use the normal btree array support routines for that + * without circularity. + */ +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for int2vector */ + int32 dataoffset; /* always 0 for int2vector */ + Oid elemtype; + int dim1; + int lbound1; + int2 values[1]; /* VARIABLE LENGTH ARRAY */ +} int2vector; /* VARIABLE LENGTH STRUCT */ + +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for oidvector */ + int32 dataoffset; /* always 0 for oidvector */ + Oid elemtype; + int dim1; + int lbound1; + Oid values[1]; /* VARIABLE LENGTH ARRAY */ +} oidvector; /* VARIABLE LENGTH STRUCT */ + +/* + * Representation of a Name: effectively just a C string, but null-padded to + * exactly NAMEDATALEN bytes. The use of a struct is historical. + */ +typedef struct nameData +{ + char data[NAMEDATALEN]; +} NameData; +typedef NameData *Name; + +#define NameStr(name) ((name).data) + +/* + * Support macros for escaping strings. escape_backslash should be TRUE + * if generating a non-standard-conforming string. Prefixing a string + * with ESCAPE_STRING_SYNTAX guarantees it is non-standard-conforming. + * Beware of multiple evaluation of the "ch" argument! + */ +#define SQL_STR_DOUBLE(ch, escape_backslash) \ + ((ch) == '\'' || ((ch) == '\\' && (escape_backslash))) + +#define ESCAPE_STRING_SYNTAX 'E' + +/* ---------------------------------------------------------------- + * Section 4: IsValid macros for system types + * ---------------------------------------------------------------- + */ +/* + * BoolIsValid + * True iff bool is valid. + */ +#define BoolIsValid(boolean) ((boolean) == false || (boolean) == true) + +/* + * PointerIsValid + * True iff pointer is valid. + */ +#define PointerIsValid(pointer) ((void*)(pointer) != NULL) + +/* + * PointerIsAligned + * True iff pointer is properly aligned to point to the given type. + */ +#define PointerIsAligned(pointer, type) \ + (((intptr_t)(pointer) % (sizeof (type))) == 0) + +#define OidIsValid(objectId) ((bool) ((objectId) != InvalidOid)) + +#define RegProcedureIsValid(p) OidIsValid(p) + + +/* ---------------------------------------------------------------- + * Section 5: offsetof, lengthof, endof, alignment + * ---------------------------------------------------------------- + */ +/* + * offsetof + * Offset of a structure/union field within that structure/union. + * + * XXX This is supposed to be part of stddef.h, but isn't on + * some systems (like SunOS 4). + */ +#ifndef offsetof +#define offsetof(type, field) ((long) &((type *)0)->field) +#endif /* offsetof */ + +/* + * lengthof + * Number of elements in an array. + */ +#define lengthof(array) (sizeof (array) / sizeof ((array)[0])) + +/* + * endof + * Address of the element one past the last in an array. + */ +#define endof(array) (&(array)[lengthof(array)]) + +/* ---------------- + * Alignment macros: align a length or address appropriately for a given type. + * The fooALIGN() macros round up to a multiple of the required alignment, + * while the fooALIGN_DOWN() macros round down. The latter are more useful + * for problems like "how many X-sized structures will fit in a page?". + * + * NOTE: TYPEALIGN[_DOWN] will not work if ALIGNVAL is not a power of 2. + * That case seems extremely unlikely to be needed in practice, however. + * ---------------- + */ + +#define TYPEALIGN(ALIGNVAL,LEN) \ + (((intptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((intptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN(LEN) TYPEALIGN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN(LEN) TYPEALIGN(ALIGNOF_INT, (LEN)) +#define LONGALIGN(LEN) TYPEALIGN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN(LEN) TYPEALIGN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN)) +/* MAXALIGN covers only built-in types, not buffers */ +#define BUFFERALIGN(LEN) TYPEALIGN(ALIGNOF_BUFFER, (LEN)) + +#define TYPEALIGN_DOWN(ALIGNVAL,LEN) \ + (((intptr_t) (LEN)) & ~((intptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_INT, (LEN)) +#define LONGALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN_DOWN(LEN) TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN)) + +/* ---------------------------------------------------------------- + * Section 6: widely useful macros + * ---------------------------------------------------------------- + */ +/* + * Max + * Return the maximum of two numbers. + */ +#define Max(x, y) ((x) > (y) ? (x) : (y)) + +/* + * Min + * Return the minimum of two numbers. + */ +#define Min(x, y) ((x) < (y) ? (x) : (y)) + +/* + * Abs + * Return the absolute value of the argument. + */ +#define Abs(x) ((x) >= 0 ? (x) : -(x)) + +/* + * StrNCpy + * Like standard library function strncpy(), except that result string + * is guaranteed to be null-terminated --- that is, at most N-1 bytes + * of the source string will be kept. + * Also, the macro returns no result (too hard to do that without + * evaluating the arguments multiple times, which seems worse). + * + * BTW: when you need to copy a non-null-terminated string (like a text + * datum) and add a null, do not do it with StrNCpy(..., len+1). That + * might seem to work, but it fetches one byte more than there is in the + * text object. One fine day you'll have a SIGSEGV because there isn't + * another byte before the end of memory. Don't laugh, we've had real + * live bug reports from real live users over exactly this mistake. + * Do it honestly with "memcpy(dst,src,len); dst[len] = '\0';", instead. + */ +#define StrNCpy(dst,src,len) \ + do \ + { \ + char * _dst = (dst); \ + Size _len = (len); \ +\ + if (_len > 0) \ + { \ + strncpy(_dst, (src), _len); \ + _dst[_len-1] = '\0'; \ + } \ + } while (0) + + +/* Get a bit mask of the bits set in non-long aligned addresses */ +#define LONG_ALIGN_MASK (sizeof(long) - 1) + +/* + * MemSet + * Exactly the same as standard library function memset(), but considerably + * faster for zeroing small word-aligned structures (such as parsetree nodes). + * This has to be a macro because the main point is to avoid function-call + * overhead. However, we have also found that the loop is faster than + * native libc memset() on some platforms, even those with assembler + * memset() functions. More research needs to be done, perhaps with + * MEMSET_LOOP_LIMIT tests in configure. + */ +#define MemSet(start, val, len) \ + do \ + { \ + /* must be void* because we don't know if it is integer aligned yet */ \ + void *_vstart = (void *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((((intptr_t) _vstart) & LONG_ALIGN_MASK) == 0 && \ + (_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + /* \ + * If MEMSET_LOOP_LIMIT == 0, optimizer should find \ + * the whole "if" false at compile time. \ + */ \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_start = (long *) _vstart; \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_vstart, _val, _len); \ + } while (0) + +/* + * MemSetAligned is the same as MemSet except it omits the test to see if + * "start" is word-aligned. This is okay to use if the caller knows a-priori + * that the pointer is suitably aligned (typically, because he just got it + * from palloc(), which always delivers a max-aligned pointer). + */ +#define MemSetAligned(start, val, len) \ + do \ + { \ + long *_start = (long *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_start, _val, _len); \ + } while (0) + + +/* + * MemSetTest/MemSetLoop are a variant version that allow all the tests in + * MemSet to be done at compile time in cases where "val" and "len" are + * constants *and* we know the "start" pointer must be word-aligned. + * If MemSetTest succeeds, then it is okay to use MemSetLoop, otherwise use + * MemSetAligned. Beware of multiple evaluations of the arguments when using + * this approach. + */ +#define MemSetTest(val, len) \ + ( ((len) & LONG_ALIGN_MASK) == 0 && \ + (len) <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0 && \ + (val) == 0 ) + +#define MemSetLoop(start, val, len) \ + do \ + { \ + long * _start = (long *) (start); \ + long * _stop = (long *) ((char *) _start + (Size) (len)); \ + \ + while (_start < _stop) \ + *_start++ = 0; \ + } while (0) + + +/* ---------------------------------------------------------------- + * Section 7: random stuff + * ---------------------------------------------------------------- + */ + +/* msb for char */ +#define HIGHBIT (0x80) +#define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_EOF (-2) +#define STATUS_FOUND (1) +#define STATUS_WAITING (2) + + +/* gettext domain name mangling */ + +/* + * To better support parallel installations of major PostgeSQL + * versions as well as parallel installations of major library soname + * versions, we mangle the gettext domain name by appending those + * version numbers. The coding rule ought to be that whereever the + * domain name is mentioned as a literal, it must be wrapped into + * PG_TEXTDOMAIN(). The macros below do not work on non-literals; but + * that is somewhat intentional because it avoids having to worry + * about multiple states of premangling and postmangling as the values + * are being passed around. + * + * Make sure this matches the installation rules in nls-global.mk. + */ + +/* need a second indirection because we want to stringize the macro value, not the name */ +#define CppAsString2(x) CppAsString(x) + +#ifdef SO_MAJOR_VERSION +#define PG_TEXTDOMAIN(domain) (domain CppAsString2(SO_MAJOR_VERSION) "-" PG_MAJORVERSION) +#else +#define PG_TEXTDOMAIN(domain) (domain "-" PG_MAJORVERSION) +#endif + + +/* ---------------------------------------------------------------- + * Section 8: system-specific hacks + * + * This should be limited to things that absolutely have to be + * included in every source file. The port-specific header file + * is usually a better place for this sort of thing. + * ---------------------------------------------------------------- + */ + +/* + * NOTE: this is also used for opening text files. + * WIN32 treats Control-Z as EOF in files opened in text mode. + * Therefore, we open files in binary mode on Win32 so we can read + * literal control-Z. The other affect is that we see CRLF, but + * that is OK because we can already handle those cleanly. + */ +#if defined(WIN32) || defined(__CYGWIN__) +#define PG_BINARY O_BINARY +#define PG_BINARY_A "ab" +#define PG_BINARY_R "rb" +#define PG_BINARY_W "wb" +#else +#define PG_BINARY 0 +#define PG_BINARY_A "a" +#define PG_BINARY_R "r" +#define PG_BINARY_W "w" +#endif + +/* + * Provide prototypes for routines not present in a particular machine's + * standard C library. + */ + +#if !HAVE_DECL_SNPRINTF +extern int +snprintf(char *str, size_t count, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4))); +#endif + +#if !HAVE_DECL_VSNPRINTF +extern int vsnprintf(char *str, size_t count, const char *fmt, va_list args); +#endif + +#if !defined(HAVE_MEMMOVE) && !defined(memmove) +#define memmove(d, s, c) bcopy(s, d, c) +#endif + +/* no special DLL markers on most ports */ +#ifndef PGDLLIMPORT +#define PGDLLIMPORT +#endif +#ifndef PGDLLEXPORT +#define PGDLLEXPORT +#endif + +/* + * The following is used as the arg list for signal handlers. Any ports + * that take something other than an int argument should override this in + * their pg_config_os.h file. Note that variable names are required + * because it is used in both the prototypes as well as the definitions. + * Note also the long name. We expect that this won't collide with + * other names causing compiler warnings. + */ + +#ifndef SIGNAL_ARGS +#define SIGNAL_ARGS int postgres_signal_arg +#endif + +/* + * When there is no sigsetjmp, its functionality is provided by plain + * setjmp. Incidentally, nothing provides setjmp's functionality in + * that case. + */ +#ifndef HAVE_SIGSETJMP +#define sigjmp_buf jmp_buf +#define sigsetjmp(x,y) setjmp(x) +#define siglongjmp longjmp +#endif + +#if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC +extern int fdatasync(int fildes); +#endif + +/* If strtoq() exists, rename it to the more standard strtoll() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOLL) && defined(HAVE_STRTOQ) +#define strtoll strtoq +#define HAVE_STRTOLL 1 +#endif + +/* If strtouq() exists, rename it to the more standard strtoull() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOULL) && defined(HAVE_STRTOUQ) +#define strtoull strtouq +#define HAVE_STRTOULL 1 +#endif + +/* + * We assume if we have these two functions, we have their friends too, and + * can use the wide-character functions. + */ +#if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) +#define USE_WIDE_UPPER_LOWER +#endif + +/* EXEC_BACKEND defines */ +#ifdef EXEC_BACKEND +#define NON_EXEC_STATIC +#else +#define NON_EXEC_STATIC static +#endif + +/* /port compatibility functions */ +#include "port.h" + +#endif /* C_H */ diff --git a/src/libpq/chklocale.c b/src/libpq/chklocale.c new file mode 100644 index 00000000..e4f3dc99 --- /dev/null +++ b/src/libpq/chklocale.c @@ -0,0 +1,358 @@ +/*------------------------------------------------------------------------- + * + * chklocale.c + * Functions for handling locale-related info + * + * + * Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/chklocale.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#ifdef HAVE_LANGINFO_H +#include +#endif + +#include "mb/pg_wchar.h" + + +/* + * This table needs to recognize all the CODESET spellings for supported + * backend encodings, as well as frontend-only encodings where possible + * (the latter case is currently only needed for initdb to recognize + * error situations). On Windows, we rely on entries for codepage + * numbers (CPnnn). + * + * Note that we search the table with pg_strcasecmp(), so variant + * capitalizations don't need their own entries. + */ +struct encoding_match +{ + enum pg_enc pg_enc_code; + const char *system_enc_name; +}; + +static const struct encoding_match encoding_match_list[] = { + {PG_EUC_JP, "EUC-JP"}, + {PG_EUC_JP, "eucJP"}, + {PG_EUC_JP, "IBM-eucJP"}, + {PG_EUC_JP, "sdeckanji"}, + {PG_EUC_JP, "CP20932"}, + + {PG_EUC_CN, "EUC-CN"}, + {PG_EUC_CN, "eucCN"}, + {PG_EUC_CN, "IBM-eucCN"}, + {PG_EUC_CN, "GB2312"}, + {PG_EUC_CN, "dechanzi"}, + {PG_EUC_CN, "CP20936"}, + + {PG_EUC_KR, "EUC-KR"}, + {PG_EUC_KR, "eucKR"}, + {PG_EUC_KR, "IBM-eucKR"}, + {PG_EUC_KR, "deckorean"}, + {PG_EUC_KR, "5601"}, + {PG_EUC_KR, "CP51949"}, + + {PG_EUC_TW, "EUC-TW"}, + {PG_EUC_TW, "eucTW"}, + {PG_EUC_TW, "IBM-eucTW"}, + {PG_EUC_TW, "cns11643"}, + /* No codepage for EUC-TW ? */ + + {PG_UTF8, "UTF-8"}, + {PG_UTF8, "utf8"}, + {PG_UTF8, "CP65001"}, + + {PG_LATIN1, "ISO-8859-1"}, + {PG_LATIN1, "ISO8859-1"}, + {PG_LATIN1, "iso88591"}, + {PG_LATIN1, "CP28591"}, + + {PG_LATIN2, "ISO-8859-2"}, + {PG_LATIN2, "ISO8859-2"}, + {PG_LATIN2, "iso88592"}, + {PG_LATIN2, "CP28592"}, + + {PG_LATIN3, "ISO-8859-3"}, + {PG_LATIN3, "ISO8859-3"}, + {PG_LATIN3, "iso88593"}, + {PG_LATIN3, "CP28593"}, + + {PG_LATIN4, "ISO-8859-4"}, + {PG_LATIN4, "ISO8859-4"}, + {PG_LATIN4, "iso88594"}, + {PG_LATIN4, "CP28594"}, + + {PG_LATIN5, "ISO-8859-9"}, + {PG_LATIN5, "ISO8859-9"}, + {PG_LATIN5, "iso88599"}, + {PG_LATIN5, "CP28599"}, + + {PG_LATIN6, "ISO-8859-10"}, + {PG_LATIN6, "ISO8859-10"}, + {PG_LATIN6, "iso885910"}, + + {PG_LATIN7, "ISO-8859-13"}, + {PG_LATIN7, "ISO8859-13"}, + {PG_LATIN7, "iso885913"}, + + {PG_LATIN8, "ISO-8859-14"}, + {PG_LATIN8, "ISO8859-14"}, + {PG_LATIN8, "iso885914"}, + + {PG_LATIN9, "ISO-8859-15"}, + {PG_LATIN9, "ISO8859-15"}, + {PG_LATIN9, "iso885915"}, + {PG_LATIN9, "CP28605"}, + + {PG_LATIN10, "ISO-8859-16"}, + {PG_LATIN10, "ISO8859-16"}, + {PG_LATIN10, "iso885916"}, + + {PG_KOI8R, "KOI8-R"}, + {PG_KOI8R, "CP20866"}, + + {PG_KOI8U, "KOI8-U"}, + {PG_KOI8U, "CP21866"}, + + {PG_WIN866, "CP866"}, + {PG_WIN874, "CP874"}, + {PG_WIN1250, "CP1250"}, + {PG_WIN1251, "CP1251"}, + {PG_WIN1251, "ansi-1251"}, + {PG_WIN1252, "CP1252"}, + {PG_WIN1253, "CP1253"}, + {PG_WIN1254, "CP1254"}, + {PG_WIN1255, "CP1255"}, + {PG_WIN1256, "CP1256"}, + {PG_WIN1257, "CP1257"}, + {PG_WIN1258, "CP1258"}, + + {PG_ISO_8859_5, "ISO-8859-5"}, + {PG_ISO_8859_5, "ISO8859-5"}, + {PG_ISO_8859_5, "iso88595"}, + {PG_ISO_8859_5, "CP28595"}, + + {PG_ISO_8859_6, "ISO-8859-6"}, + {PG_ISO_8859_6, "ISO8859-6"}, + {PG_ISO_8859_6, "iso88596"}, + {PG_ISO_8859_6, "CP28596"}, + + {PG_ISO_8859_7, "ISO-8859-7"}, + {PG_ISO_8859_7, "ISO8859-7"}, + {PG_ISO_8859_7, "iso88597"}, + {PG_ISO_8859_7, "CP28597"}, + + {PG_ISO_8859_8, "ISO-8859-8"}, + {PG_ISO_8859_8, "ISO8859-8"}, + {PG_ISO_8859_8, "iso88598"}, + {PG_ISO_8859_8, "CP28598"}, + + {PG_SJIS, "SJIS"}, + {PG_SJIS, "PCK"}, + {PG_SJIS, "CP932"}, + + {PG_BIG5, "BIG5"}, + {PG_BIG5, "BIG5HKSCS"}, + {PG_BIG5, "Big5-HKSCS"}, + {PG_BIG5, "CP950"}, + + {PG_GBK, "GBK"}, + {PG_GBK, "CP936"}, + + {PG_UHC, "UHC"}, + {PG_UHC, "CP949"}, + + {PG_JOHAB, "JOHAB"}, + {PG_JOHAB, "CP1361"}, + + {PG_GB18030, "GB18030"}, + {PG_GB18030, "CP54936"}, + + {PG_SHIFT_JIS_2004, "SJIS_2004"}, + + {PG_SQL_ASCII, "US-ASCII"}, + + {PG_SQL_ASCII, NULL} /* end marker */ +}; + +#ifdef WIN32 +/* + * On Windows, use CP instead of the nl_langinfo() result + */ +static char * +win32_langinfo(const char *ctype) +{ + char *r; + char *codepage; + int ln; + + /* + * Locale format on Win32 is _. . For + * example, English_USA.1252. + */ + codepage = strrchr(ctype, '.'); + if (!codepage) + return NULL; + codepage++; + ln = strlen(codepage); + r = malloc(ln + 3); + sprintf(r, "CP%s", codepage); + + return r; +} +#endif /* WIN32 */ + +#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) || defined(WIN32) + +/* + * Given a setting for LC_CTYPE, return the Postgres ID of the associated + * encoding, if we can determine it. Return -1 if we can't determine it. + * + * Pass in NULL to get the encoding for the current locale setting. + * Pass "" to get the encoding selected by the server's environment. + * + * If the result is PG_SQL_ASCII, callers should treat it as being compatible + * with any desired encoding. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + char *sys; + int i; + + /* Get the CODESET property, and also LC_CTYPE if not passed in */ + if (ctype) + { + char *save; + char *name; + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + + save = setlocale(LC_CTYPE, NULL); + if (!save) + return -1; /* setlocale() broken? */ + /* must copy result, or it might change after setlocale */ + save = strdup(save); + if (!save) + return -1; /* out of memory; unlikely */ + + name = setlocale(LC_CTYPE, ctype); + if (!name) + { + free(save); + return -1; /* bogus ctype passed in? */ + } + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(name); +#endif + + setlocale(LC_CTYPE, save); + free(save); + } + else + { + /* much easier... */ + ctype = setlocale(LC_CTYPE, NULL); + if (!ctype) + return -1; /* setlocale() broken? */ + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(ctype); +#endif + } + + if (!sys) + return -1; /* out of memory; unlikely */ + + /* Check the table */ + for (i = 0; encoding_match_list[i].system_enc_name; i++) + { + if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) + { + free(sys); + return encoding_match_list[i].pg_enc_code; + } + } + + /* Special-case kluges for particular platforms go here */ + +#ifdef __darwin__ + + /* + * Current OS X has many locales that report an empty string for CODESET, + * but they all seem to actually use UTF-8. + */ + if (strlen(sys) == 0) + { + free(sys); + return PG_UTF8; + } +#endif + + /* + * We print a warning if we got a CODESET string but couldn't recognize + * it. This means we need another entry in the table. + */ + if (write_message) + { +#ifdef FRONTEND + fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), + ctype, sys); + /* keep newline separate so there's only one translatable string */ + fputc('\n', stderr); +#else + ereport(WARNING, + (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", + ctype, sys), + errdetail("Please report this to ."))); +#endif + } + + free(sys); + return -1; +} +#else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ + +/* + * stub if no multi-language platform support + * + * Note: we could return -1 here, but that would have the effect of + * forcing users to specify an encoding to initdb on such platforms. + * It seems better to silently default to SQL_ASCII. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + return PG_SQL_ASCII; +} + +#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ diff --git a/src/libpq/crypt.c b/src/libpq/crypt.c new file mode 100644 index 00000000..9347d3b4 --- /dev/null +++ b/src/libpq/crypt.c @@ -0,0 +1,1085 @@ +/* src/port/crypt.c */ +/* $NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tom Truscott. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)crypt.c 8.1.1.1 (Berkeley) 8/18/93"; +#else +__RCSID("$NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $"); +#endif +#endif /* not lint */ + +#include "c.h" + +#include + +#ifndef WIN32 +#include +#endif + +static int des_setkey(const char *key); +static int des_cipher(const char *in, char *out, long salt, int num_iter); + +/* + * UNIX password, and DES, encryption. + * By Tom Truscott, trt@rti.rti.org, + * from algorithms by Robert W. Baldwin and James Gillogly. + * + * References: + * "Mathematical Cryptology for Computer Scientists and Mathematicians," + * by Wayne Patterson, 1987, ISBN 0-8476-7438-X. + * + * "Password Security: A Case History," R. Morris and Ken Thompson, + * Communications of the ACM, vol. 22, pp. 594-597, Nov. 1979. + * + * "DES will be Totally Insecure within Ten Years," M.E. Hellman, + * IEEE Spectrum, vol. 16, pp. 32-39, July 1979. + */ + +/* ===== Configuration ==================== */ + +/* + * define "MUST_ALIGN" if your compiler cannot load/store + * long integers at arbitrary (e.g. odd) memory locations. + * (Either that or never pass unaligned addresses to des_cipher!) + */ +/* #define MUST_ALIGN */ + +#ifdef CHAR_BITS +#if CHAR_BITS != 8 +#error C_block structure assumes 8 bit characters +#endif +#endif + +/* + * define "B64" to be the declaration for a 64 bit integer. + * XXX this feature is currently unused, see "endian" comment below. + */ +#define B64 __int64 + +/* + * define "LARGEDATA" to get faster permutations, by using about 72 kilobytes + * of lookup tables. This speeds up des_setkey() and des_cipher(), but has + * little effect on crypt(). + */ +/* #define LARGEDATA */ + +/* compile with "-DSTATIC=void" when profiling */ +#ifndef STATIC +#define STATIC static void +#endif + +/* + * Define the "int32_t" type for integral type with a width of at least + * 32 bits. + */ +typedef int int32_t; + +/* ==================================== */ + +#define _PASSWORD_EFMT1 '_' /* extended encryption format */ + +/* + * Cipher-block representation (Bob Baldwin): + * + * DES operates on groups of 64 bits, numbered 1..64 (sigh). One + * representation is to store one bit per byte in an array of bytes. Bit N of + * the NBS spec is stored as the LSB of the Nth byte (index N-1) in the array. + * Another representation stores the 64 bits in 8 bytes, with bits 1..8 in the + * first byte, 9..16 in the second, and so on. The DES spec apparently has + * bit 1 in the MSB of the first byte, but that is particularly noxious so we + * bit-reverse each byte so that bit 1 is the LSB of the first byte, bit 8 is + * the MSB of the first byte. Specifically, the 64-bit input data and key are + * converted to LSB format, and the output 64-bit block is converted back into + * MSB format. + * + * DES operates internally on groups of 32 bits which are expanded to 48 bits + * by permutation E and shrunk back to 32 bits by the S boxes. To speed up + * the computation, the expansion is applied only once, the expanded + * representation is maintained during the encryption, and a compression + * permutation is applied only at the end. To speed up the S-box lookups, + * the 48 bits are maintained as eight 6 bit groups, one per byte, which + * directly feed the eight S-boxes. Within each byte, the 6 bits are the + * most significant ones. The low two bits of each byte are zero. (Thus, + * bit 1 of the 48 bit E expansion is stored as the "4"-valued bit of the + * first byte in the eight byte representation, bit 2 of the 48 bit value is + * the "8"-valued bit, and so on.) In fact, a combined "SPE"-box lookup is + * used, in which the output is the 64 bit result of an S-box lookup which + * has been permuted by P and expanded by E, and is ready for use in the next + * iteration. Two 32-bit wide tables, SPE[0] and SPE[1], are used for this + * lookup. Since each byte in the 48 bit path is a multiple of four, indexed + * lookup of SPE[0] and SPE[1] is simple and fast. The key schedule and + * "salt" are also converted to this 8*(6+2) format. The SPE table size is + * 8*64*8 = 4K bytes. + * + * To speed up bit-parallel operations (such as XOR), the 8 byte + * representation is "union"ed with 32 bit values "i0" and "i1", and, on + * machines which support it, a 64 bit value "b64". This data structure, + * "C_block", has two problems. First, alignment restrictions must be + * honored. Second, the byte-order (e.g. little-endian or big-endian) of + * the architecture becomes visible. + * + * The byte-order problem is unfortunate, since on the one hand it is good + * to have a machine-independent C_block representation (bits 1..8 in the + * first byte, etc.), and on the other hand it is good for the LSB of the + * first byte to be the LSB of i0. We cannot have both these things, so we + * currently use the "little-endian" representation and avoid any multi-byte + * operations that depend on byte order. This largely precludes use of the + * 64-bit datatype since the relative order of i0 and i1 are unknown. It + * also inhibits grouping the SPE table to look up 12 bits at a time. (The + * 12 bits can be stored in a 16-bit field with 3 low-order zeroes and 1 + * high-order zero, providing fast indexing into a 64-bit wide SPE.) On the + * other hand, 64-bit datatypes are currently rare, and a 12-bit SPE lookup + * requires a 128 kilobyte table, so perhaps this is not a big loss. + * + * Permutation representation (Jim Gillogly): + * + * A transformation is defined by its effect on each of the 8 bytes of the + * 64-bit input. For each byte we give a 64-bit output that has the bits in + * the input distributed appropriately. The transformation is then the OR + * of the 8 sets of 64-bits. This uses 8*256*8 = 16K bytes of storage for + * each transformation. Unless LARGEDATA is defined, however, a more compact + * table is used which looks up 16 4-bit "chunks" rather than 8 8-bit chunks. + * The smaller table uses 16*16*8 = 2K bytes for each transformation. This + * is slower but tolerable, particularly for password encryption in which + * the SPE transformation is iterated many times. The small tables total 9K + * bytes, the large tables total 72K bytes. + * + * The transformations used are: + * IE3264: MSB->LSB conversion, initial permutation, and expansion. + * This is done by collecting the 32 even-numbered bits and applying + * a 32->64 bit transformation, and then collecting the 32 odd-numbered + * bits and applying the same transformation. Since there are only + * 32 input bits, the IE3264 transformation table is half the size of + * the usual table. + * CF6464: Compression, final permutation, and LSB->MSB conversion. + * This is done by two trivial 48->32 bit compressions to obtain + * a 64-bit block (the bit numbering is given in the "CIFP" table) + * followed by a 64->64 bit "cleanup" transformation. (It would + * be possible to group the bits in the 64-bit block so that 2 + * identical 32->32 bit transformations could be used instead, + * saving a factor of 4 in space and possibly 2 in time, but + * byte-ordering and other complications rear their ugly head. + * Similar opportunities/problems arise in the key schedule + * transforms.) + * PC1ROT: MSB->LSB, PC1 permutation, rotate, and PC2 permutation. + * This admittedly baroque 64->64 bit transformation is used to + * produce the first code (in 8*(6+2) format) of the key schedule. + * PC2ROT[0]: Inverse PC2 permutation, rotate, and PC2 permutation. + * It would be possible to define 15 more transformations, each + * with a different rotation, to generate the entire key schedule. + * To save space, however, we instead permute each code into the + * next by using a transformation that "undoes" the PC2 permutation, + * rotates the code, and then applies PC2. Unfortunately, PC2 + * transforms 56 bits into 48 bits, dropping 8 bits, so PC2 is not + * invertible. We get around that problem by using a modified PC2 + * which retains the 8 otherwise-lost bits in the unused low-order + * bits of each byte. The low-order bits are cleared when the + * codes are stored into the key schedule. + * PC2ROT[1]: Same as PC2ROT[0], but with two rotations. + * This is faster than applying PC2ROT[0] twice, + * + * The Bell Labs "salt" (Bob Baldwin): + * + * The salting is a simple permutation applied to the 48-bit result of E. + * Specifically, if bit i (1 <= i <= 24) of the salt is set then bits i and + * i+24 of the result are swapped. The salt is thus a 24 bit number, with + * 16777216 possible values. (The original salt was 12 bits and could not + * swap bits 13..24 with 36..48.) + * + * It is possible, but ugly, to warp the SPE table to account for the salt + * permutation. Fortunately, the conditional bit swapping requires only + * about four machine instructions and can be done on-the-fly with about an + * 8% performance penalty. + */ + +typedef union +{ + unsigned char b[8]; + struct + { + int32_t i0; + int32_t i1; + } b32; +#if defined(B64) + B64 b64; +#endif +} C_block; + +/* + * Convert twenty-four-bit long in host-order + * to six bits (and 2 low-order zeroes) per char little-endian format. + */ +#define TO_SIX_BIT(rslt, src) { \ + C_block cvt; \ + cvt.b[0] = src; src >>= 6; \ + cvt.b[1] = src; src >>= 6; \ + cvt.b[2] = src; src >>= 6; \ + cvt.b[3] = src; \ + rslt = (cvt.b32.i0 & 0x3f3f3f3fL) << 2; \ + } + +/* + * These macros may someday permit efficient use of 64-bit integers. + */ +#define ZERO(d,d0,d1) d0 = 0, d1 = 0 +#define LOAD(d,d0,d1,bl) d0 = (bl).b32.i0, d1 = (bl).b32.i1 +#define LOADREG(d,d0,d1,s,s0,s1) d0 = s0, d1 = s1 +#define OR(d,d0,d1,bl) d0 |= (bl).b32.i0, d1 |= (bl).b32.i1 +#define STORE(s,s0,s1,bl) (bl).b32.i0 = s0, (bl).b32.i1 = s1 +#define DCL_BLOCK(d,d0,d1) int32_t d0, d1 + +#if defined(LARGEDATA) + /* Waste memory like crazy. Also, do permutations in line */ +#define LGCHUNKBITS 3 +#define CHUNKBITS (1<> 4]; + OR(D, D0, D1, *tp); + p += (1 << CHUNKBITS); + } while (--chars_in > 0); + STORE(D, D0, D1, *out); +} +#endif /* LARGEDATA */ + + +/* ===== (mostly) Standard DES Tables ==================== */ + +static const unsigned char IP[] = { /* initial permutation */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, +}; + +/* The final permutation is the inverse of IP - no table is necessary */ + +static const unsigned char ExpandTr[] = { /* expansion operation */ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1, +}; + +static const unsigned char PC1[] = { /* permuted choice table 1 */ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4, +}; + +static const unsigned char Rotates[] = { /* PC1 rotation schedule */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +/* note: each "row" of PC2 is left-padded with bits that make it invertible */ +static const unsigned char PC2[] = { /* permuted choice table 2 */ + 9, 18, 14, 17, 11, 24, 1, 5, + 22, 25, 3, 28, 15, 6, 21, 10, + 35, 38, 23, 19, 12, 4, 26, 8, + 43, 54, 16, 7, 27, 20, 13, 2, + + 0, 0, 41, 52, 31, 37, 47, 55, + 0, 0, 30, 40, 51, 45, 33, 48, + 0, 0, 44, 49, 39, 56, 34, 53, + 0, 0, 46, 42, 50, 36, 29, 32, +}; + +static const unsigned char S[8][64] = { /* 48->32 bit substitution tables */ + /* S[1] */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + /* S[2] */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + /* S[3] */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + /* S[4] */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + /* S[5] */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + /* S[6] */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + /* S[7] */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + /* S[8] */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11} +}; + +static const unsigned char P32Tr[] = { /* 32-bit permutation function */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25, +}; + +static const unsigned char CIFP[] = { /* compressed/interleaved permutation */ + 1, 2, 3, 4, 17, 18, 19, 20, + 5, 6, 7, 8, 21, 22, 23, 24, + 9, 10, 11, 12, 25, 26, 27, 28, + 13, 14, 15, 16, 29, 30, 31, 32, + + 33, 34, 35, 36, 49, 50, 51, 52, + 37, 38, 39, 40, 53, 54, 55, 56, + 41, 42, 43, 44, 57, 58, 59, 60, + 45, 46, 47, 48, 61, 62, 63, 64, +}; + +static const unsigned char itoa64[] = /* 0..63 => ascii-64 */ +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +/* ===== Tables that are initialized at run time ==================== */ + + +static unsigned char a64toi[128]; /* ascii-64 => 0..63 */ + +/* Initial key schedule permutation */ +static C_block PC1ROT[64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Subsequent key schedule rotation permutations */ +static C_block PC2ROT[2][64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Initial permutation/expansion table */ +static C_block IE3264[32 / CHUNKBITS][1 << CHUNKBITS]; + +/* Table that combines the S, P, and E operations. */ +static int32_t SPE[2][8][64]; + +/* compressed/interleaved => final permutation table */ +static C_block CF6464[64 / CHUNKBITS][1 << CHUNKBITS]; + + +/* ==================================== */ + + +static C_block constdatablock; /* encryption constant */ +static char cryptresult[1 + 4 + 4 + 11 + 1]; /* encrypted result */ + +extern char *__md5crypt(const char *, const char *); /* XXX */ +extern char *__bcrypt(const char *, const char *); /* XXX */ + + +/* + * Return a pointer to static data consisting of the "setting" + * followed by an encryption produced by the "key" and "setting". + */ +char * +crypt(key, setting) +const char *key; +const char *setting; +{ + char *encp; + int32_t i; + int t; + int32_t salt; + int num_iter, + salt_size; + C_block keyblock, + rsltblock; + +#if 0 + /* Non-DES encryption schemes hook in here. */ + if (setting[0] == _PASSWORD_NONDES) + { + switch (setting[1]) + { + case '2': + return (__bcrypt(key, setting)); + case '1': + default: + return (__md5crypt(key, setting)); + } + } +#endif + + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] = t; + } + if (des_setkey((char *) keyblock.b)) /* also initializes "a64toi" */ + return (NULL); + + encp = &cryptresult[0]; + switch (*setting) + { + case _PASSWORD_EFMT1: + + /* + * Involve the rest of the password 8 characters at a time. + */ + while (*key) + { + if (des_cipher((char *) (void *) &keyblock, + (char *) (void *) &keyblock, 0L, 1)) + return (NULL); + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] ^= t; + } + if (des_setkey((char *) keyblock.b)) + return (NULL); + } + + *encp++ = *setting++; + + /* get iteration count */ + num_iter = 0; + for (i = 4; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + num_iter = (num_iter << 6) | a64toi[t]; + } + setting += 4; + encp += 4; + salt_size = 4; + break; + default: + num_iter = 25; + salt_size = 2; + } + + salt = 0; + for (i = salt_size; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + salt = (salt << 6) | a64toi[t]; + } + encp += salt_size; + if (des_cipher((char *) (void *) &constdatablock, + (char *) (void *) &rsltblock, salt, num_iter)) + return (NULL); + + /* + * Encode the 64 cipher bits as 11 ascii characters. + */ + i = ((int32_t) ((rsltblock.b[0] << 8) | rsltblock.b[1]) << 8) | + rsltblock.b[2]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[3] << 8) | rsltblock.b[4]) << 8) | + rsltblock.b[5]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[6]) << 8) | rsltblock.b[7]) << 2; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + + encp[3] = 0; + + return (cryptresult); +} + + +/* + * The Key Schedule, filled in by des_setkey() or setkey(). + */ +#define KS_SIZE 16 +static C_block KS[KS_SIZE]; + +static volatile int des_ready = 0; + +/* + * Set up the key schedule from the key. + */ +static int +des_setkey(key) +const char *key; +{ + DCL_BLOCK(K, K0, K1); + C_block *ptabp; + int i; + + if (!des_ready) + init_des(); + + PERM6464(K, K0, K1, (unsigned char *) key, (C_block *) PC1ROT); + key = (char *) &KS[0]; + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + for (i = 1; i < 16; i++) + { + key += sizeof(C_block); + STORE(K, K0, K1, *(C_block *) key); + ptabp = (C_block *) PC2ROT[Rotates[i] - 1]; + PERM6464(K, K0, K1, (unsigned char *) key, ptabp); + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + } + return (0); +} + +/* + * Encrypt (or decrypt if num_iter < 0) the 8 chars at "in" with abs(num_iter) + * iterations of DES, using the given 24-bit salt and the pre-computed key + * schedule, and store the resulting 8 chars at "out" (in == out is permitted). + * + * NOTE: the performance of this routine is critically dependent on your + * compiler and machine architecture. + */ +static int +des_cipher(in, out, salt, num_iter) +const char *in; +char *out; +long salt; +int num_iter; +{ + /* variables that we want in registers, most important first */ +#if defined(pdp11) + int j; +#endif + int32_t L0, + L1, + R0, + R1, + k; + C_block *kp; + int ks_inc, + loop_count; + C_block B; + + L0 = salt; + TO_SIX_BIT(salt, L0); /* convert to 4*(6+2) format */ + +#if defined(__vax__) || defined(pdp11) + salt = ~salt; /* "x &~ y" is faster than "x & y". */ +#define SALT (~salt) +#else +#define SALT salt +#endif + +#if defined(MUST_ALIGN) + B.b[0] = in[0]; + B.b[1] = in[1]; + B.b[2] = in[2]; + B.b[3] = in[3]; + B.b[4] = in[4]; + B.b[5] = in[5]; + B.b[6] = in[6]; + B.b[7] = in[7]; + LOAD(L, L0, L1, B); +#else + LOAD(L, L0, L1, *(C_block *) in); +#endif + LOADREG(R, R0, R1, L, L0, L1); + L0 &= 0x55555555L; + L1 &= 0x55555555L; + L0 = (L0 << 1) | L1; /* L0 is the even-numbered input bits */ + R0 &= 0xaaaaaaaaL; + R1 = (R1 >> 1) & 0x55555555L; + L1 = R0 | R1; /* L1 is the odd-numbered input bits */ + STORE(L, L0, L1, B); + PERM3264(L, L0, L1, B.b, (C_block *) IE3264); /* even bits */ + PERM3264(R, R0, R1, B.b + 4, (C_block *) IE3264); /* odd bits */ + + if (num_iter >= 0) + { /* encryption */ + kp = &KS[0]; + ks_inc = sizeof(*kp); + } + else + { /* decryption */ + num_iter = -num_iter; + kp = &KS[KS_SIZE - 1]; + ks_inc = -(long) sizeof(*kp); + } + + while (--num_iter >= 0) + { + loop_count = 8; + do + { + +#define SPTAB(t, i) \ + (*(int32_t *)((unsigned char *)(t) + (i)*(sizeof(int32_t)/4))) +#if defined(gould) + /* use this if B.b[i] is evaluated just once ... */ +#define DOXOR(x,y,i) x^=SPTAB(SPE[0][i],B.b[i]); y^=SPTAB(SPE[1][i],B.b[i]); +#else +#if defined(pdp11) + /* use this if your "long" int indexing is slow */ +#define DOXOR(x,y,i) j=B.b[i]; x^=SPTAB(SPE[0][i],j); y^=SPTAB(SPE[1][i],j); +#else + /* use this if "k" is allocated to a register ... */ +#define DOXOR(x,y,i) k=B.b[i]; x^=SPTAB(SPE[0][i],k); y^=SPTAB(SPE[1][i],k); +#endif +#endif + +#define CRUNCH(p0, p1, q0, q1) \ + k = ((q0) ^ (q1)) & SALT; \ + B.b32.i0 = k ^ (q0) ^ kp->b32.i0; \ + B.b32.i1 = k ^ (q1) ^ kp->b32.i1; \ + kp = (C_block *)((char *)kp+ks_inc); \ + \ + DOXOR(p0, p1, 0); \ + DOXOR(p0, p1, 1); \ + DOXOR(p0, p1, 2); \ + DOXOR(p0, p1, 3); \ + DOXOR(p0, p1, 4); \ + DOXOR(p0, p1, 5); \ + DOXOR(p0, p1, 6); \ + DOXOR(p0, p1, 7); + + CRUNCH(L0, L1, R0, R1); + CRUNCH(R0, R1, L0, L1); + } while (--loop_count != 0); + kp = (C_block *) ((char *) kp - (ks_inc * KS_SIZE)); + + + /* swap L and R */ + L0 ^= R0; + L1 ^= R1; + R0 ^= L0; + R1 ^= L1; + L0 ^= R0; + L1 ^= R1; + } + + /* store the encrypted (or decrypted) result */ + L0 = ((L0 >> 3) & 0x0f0f0f0fL) | ((L1 << 1) & 0xf0f0f0f0L); + L1 = ((R0 >> 3) & 0x0f0f0f0fL) | ((R1 << 1) & 0xf0f0f0f0L); + STORE(L, L0, L1, B); + PERM6464(L, L0, L1, B.b, (C_block *) CF6464); +#if defined(MUST_ALIGN) + STORE(L, L0, L1, B); + out[0] = B.b[0]; + out[1] = B.b[1]; + out[2] = B.b[2]; + out[3] = B.b[3]; + out[4] = B.b[4]; + out[5] = B.b[5]; + out[6] = B.b[6]; + out[7] = B.b[7]; +#else + STORE(L, L0, L1, *(C_block *) out); +#endif + return (0); +} + + +/* + * Initialize various tables. This need only be done once. It could even be + * done at compile time, if the compiler were capable of that sort of thing. + */ +STATIC +init_des() +{ + int i, + j; + int32_t k; + int tableno; + static unsigned char perm[64], + tmp32[32]; /* "static" for speed */ + +/* static volatile long init_start = 0; not used */ + + /* + * table that converts chars "./0-9A-Za-z"to integers 0-63. + */ + for (i = 0; i < 64; i++) + a64toi[itoa64[i]] = i; + + /* + * PC1ROT - bit reverse, then PC1, then Rotate, then PC2. + */ + for (i = 0; i < 64; i++) + perm[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += Rotates[0] - 1; + if ((k % 28) < Rotates[0]) + k -= 28; + k = PC1[k]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i] = k; + } +#ifdef DEBUG + prtab("pc1tab", perm, 8); +#endif + init_perm(PC1ROT, perm, 8, 8); + + /* + * PC2ROT - PC2 inverse, then Rotate (once or twice), then PC2. + */ + for (j = 0; j < 2; j++) + { + unsigned char pc2inv[64]; + + for (i = 0; i < 64; i++) + perm[i] = pc2inv[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + pc2inv[k - 1] = i + 1; + } + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += j; + if ((k % 28) <= j) + k -= 28; + perm[i] = pc2inv[k]; + } +#ifdef DEBUG + prtab("pc2tab", perm, 8); +#endif + init_perm(PC2ROT[j], perm, 8, 8); + } + + /* + * Bit reverse, then initial permutation, then expansion. + */ + for (i = 0; i < 8; i++) + { + for (j = 0; j < 8; j++) + { + k = (j < 2) ? 0 : IP[ExpandTr[i * 6 + j - 2] - 1]; + if (k > 32) + k -= 32; + else if (k > 0) + k--; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i * 8 + j] = k; + } + } +#ifdef DEBUG + prtab("ietab", perm, 8); +#endif + init_perm(IE3264, perm, 4, 8); + + /* + * Compression, then final permutation, then bit reverse. + */ + for (i = 0; i < 64; i++) + { + k = IP[CIFP[i] - 1]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[k - 1] = i + 1; + } +#ifdef DEBUG + prtab("cftab", perm, 8); +#endif + init_perm(CF6464, perm, 8, 8); + + /* + * SPE table + */ + for (i = 0; i < 48; i++) + perm[i] = P32Tr[ExpandTr[i] - 1]; + for (tableno = 0; tableno < 8; tableno++) + { + for (j = 0; j < 64; j++) + { + k = (((j >> 0) & 01) << 5) | + (((j >> 1) & 01) << 3) | + (((j >> 2) & 01) << 2) | + (((j >> 3) & 01) << 1) | + (((j >> 4) & 01) << 0) | + (((j >> 5) & 01) << 4); + k = S[tableno][k]; + k = (((k >> 3) & 01) << 0) | + (((k >> 2) & 01) << 1) | + (((k >> 1) & 01) << 2) | + (((k >> 0) & 01) << 3); + for (i = 0; i < 32; i++) + tmp32[i] = 0; + for (i = 0; i < 4; i++) + tmp32[4 * tableno + i] = (k >> i) & 01; + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i] - 1]; + TO_SIX_BIT(SPE[0][tableno][j], k); + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i + 24] - 1]; + TO_SIX_BIT(SPE[1][tableno][j], k); + } + } + + des_ready = 1; +} + +/* + * Initialize "perm" to represent transformation "p", which rearranges + * (perhaps with expansion and/or contraction) one packed array of bits + * (of size "chars_in" characters) into another array (of size "chars_out" + * characters). + * + * "perm" must be all-zeroes on entry to this routine. + */ +STATIC +init_perm(perm, p, chars_in, chars_out) +C_block perm[64 / CHUNKBITS][1 << CHUNKBITS]; +unsigned char p[64]; +int chars_in, + chars_out; +{ + int i, + j, + k, + l; + + for (k = 0; k < chars_out * 8; k++) + { /* each output bit position */ + l = p[k] - 1; /* where this bit comes from */ + if (l < 0) + continue; /* output bit is always 0 */ + i = l >> LGCHUNKBITS; /* which chunk this bit comes from */ + l = 1 << (l & (CHUNKBITS - 1)); /* mask for this bit */ + for (j = 0; j < (1 << CHUNKBITS); j++) + { /* each chunk value */ + if ((j & l) != 0) + perm[i][j].b[k >> 3] |= 1 << (k & 07); + } + } +} + +/* + * "setkey" routine (for backwards compatibility) + */ +#ifdef NOT_USED +int +setkey(key) +const char *key; +{ + int i, + j, + k; + C_block keyblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *key++; + } + keyblock.b[i] = k; + } + return (des_setkey((char *) keyblock.b)); +} + +/* + * "encrypt" routine (for backwards compatibility) + */ +static int +encrypt(block, flag) +char *block; +int flag; +{ + int i, + j, + k; + C_block cblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *block++; + } + cblock.b[i] = k; + } + if (des_cipher((char *) &cblock, (char *) &cblock, 0L, (flag ? -1 : 1))) + return (1); + for (i = 7; i >= 0; i--) + { + k = cblock.b[i]; + for (j = 7; j >= 0; j--) + { + *--block = k & 01; + k >>= 1; + } + } + return (0); +} +#endif + +#ifdef DEBUG +STATIC +prtab(s, t, num_rows) +char *s; +unsigned char *t; +int num_rows; +{ + int i, + j; + + (void) printf("%s:\n", s); + for (i = 0; i < num_rows; i++) + { + for (j = 0; j < 8; j++) + (void) printf("%3d", t[i * 8 + j]); + (void) printf("\n"); + } + (void) printf("\n"); +} + +#endif diff --git a/src/libpq/encnames.c b/src/libpq/encnames.c new file mode 100644 index 00000000..7a371575 --- /dev/null +++ b/src/libpq/encnames.c @@ -0,0 +1,557 @@ +/* + * Encoding names and routines for work with it. All + * in this file is shared bedween FE and BE. + * + * src/backend/utils/mb/encnames.c + */ +#ifdef FRONTEND +#include "postgres_fe.h" +#define Assert(condition) +#else +#include "postgres.h" +#include "utils/builtins.h" +#endif + +#include +#include + +#include "mb/pg_wchar.h" + + +/* ---------- + * All encoding names, sorted: *** A L P H A B E T I C *** + * + * All names must be without irrelevant chars, search routines use + * isalnum() chars only. It means ISO-8859-1, iso_8859-1 and Iso8859_1 + * are always converted to 'iso88591'. All must be lower case. + * + * The table doesn't contain 'cs' aliases (like csISOLatin1). It's needed? + * + * Karel Zak, Aug 2001 + * ---------- + */ +pg_encname pg_encname_tbl[] = +{ + { + "abc", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "alt", PG_WIN866 + }, /* IBM866 */ + { + "big5", PG_BIG5 + }, /* Big5; Chinese for Taiwan multibyte set */ + { + "euccn", PG_EUC_CN + }, /* EUC-CN; Extended Unix Code for simplified + * Chinese */ + { + "eucjis2004", PG_EUC_JIS_2004 + }, /* EUC-JIS-2004; Extended UNIX Code fixed + * Width for Japanese, standard JIS X 0213 */ + { + "eucjp", PG_EUC_JP + }, /* EUC-JP; Extended UNIX Code fixed Width for + * Japanese, standard OSF */ + { + "euckr", PG_EUC_KR + }, /* EUC-KR; Extended Unix Code for Korean , KS + * X 1001 standard */ + { + "euctw", PG_EUC_TW + }, /* EUC-TW; Extended Unix Code for + * + * traditional Chinese */ + { + "gb18030", PG_GB18030 + }, /* GB18030;GB18030 */ + { + "gbk", PG_GBK + }, /* GBK; Chinese Windows CodePage 936 + * simplified Chinese */ + { + "iso88591", PG_LATIN1 + }, /* ISO-8859-1; RFC1345,KXS2 */ + { + "iso885910", PG_LATIN6 + }, /* ISO-8859-10; RFC1345,KXS2 */ + { + "iso885913", PG_LATIN7 + }, /* ISO-8859-13; RFC1345,KXS2 */ + { + "iso885914", PG_LATIN8 + }, /* ISO-8859-14; RFC1345,KXS2 */ + { + "iso885915", PG_LATIN9 + }, /* ISO-8859-15; RFC1345,KXS2 */ + { + "iso885916", PG_LATIN10 + }, /* ISO-8859-16; RFC1345,KXS2 */ + { + "iso88592", PG_LATIN2 + }, /* ISO-8859-2; RFC1345,KXS2 */ + { + "iso88593", PG_LATIN3 + }, /* ISO-8859-3; RFC1345,KXS2 */ + { + "iso88594", PG_LATIN4 + }, /* ISO-8859-4; RFC1345,KXS2 */ + { + "iso88595", PG_ISO_8859_5 + }, /* ISO-8859-5; RFC1345,KXS2 */ + { + "iso88596", PG_ISO_8859_6 + }, /* ISO-8859-6; RFC1345,KXS2 */ + { + "iso88597", PG_ISO_8859_7 + }, /* ISO-8859-7; RFC1345,KXS2 */ + { + "iso88598", PG_ISO_8859_8 + }, /* ISO-8859-8; RFC1345,KXS2 */ + { + "iso88599", PG_LATIN5 + }, /* ISO-8859-9; RFC1345,KXS2 */ + { + "johab", PG_JOHAB + }, /* JOHAB; Extended Unix Code for simplified + * Chinese */ + { + "koi8", PG_KOI8R + }, /* _dirty_ alias for KOI8-R (backward + * compatibility) */ + { + "koi8r", PG_KOI8R + }, /* KOI8-R; RFC1489 */ + { + "koi8u", PG_KOI8U + }, /* KOI8-U; RFC2319 */ + { + "latin1", PG_LATIN1 + }, /* alias for ISO-8859-1 */ + { + "latin10", PG_LATIN10 + }, /* alias for ISO-8859-16 */ + { + "latin2", PG_LATIN2 + }, /* alias for ISO-8859-2 */ + { + "latin3", PG_LATIN3 + }, /* alias for ISO-8859-3 */ + { + "latin4", PG_LATIN4 + }, /* alias for ISO-8859-4 */ + { + "latin5", PG_LATIN5 + }, /* alias for ISO-8859-9 */ + { + "latin6", PG_LATIN6 + }, /* alias for ISO-8859-10 */ + { + "latin7", PG_LATIN7 + }, /* alias for ISO-8859-13 */ + { + "latin8", PG_LATIN8 + }, /* alias for ISO-8859-14 */ + { + "latin9", PG_LATIN9 + }, /* alias for ISO-8859-15 */ + { + "mskanji", PG_SJIS + }, /* alias for Shift_JIS */ + { + "muleinternal", PG_MULE_INTERNAL + }, + { + "shiftjis", PG_SJIS + }, /* Shift_JIS; JIS X 0202-1991 */ + + { + "shiftjis2004", PG_SHIFT_JIS_2004 + }, /* SHIFT-JIS-2004; Shift JIS for Japanese, + * standard JIS X 0213 */ + { + "sjis", PG_SJIS + }, /* alias for Shift_JIS */ + { + "sqlascii", PG_SQL_ASCII + }, + { + "tcvn", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "tcvn5712", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "uhc", PG_UHC + }, /* UHC; Korean Windows CodePage 949 */ + { + "unicode", PG_UTF8 + }, /* alias for UTF8 */ + { + "utf8", PG_UTF8 + }, /* alias for UTF8 */ + { + "vscii", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "win", PG_WIN1251 + }, /* _dirty_ alias for windows-1251 (backward + * compatibility) */ + { + "win1250", PG_WIN1250 + }, /* alias for Windows-1250 */ + { + "win1251", PG_WIN1251 + }, /* alias for Windows-1251 */ + { + "win1252", PG_WIN1252 + }, /* alias for Windows-1252 */ + { + "win1253", PG_WIN1253 + }, /* alias for Windows-1253 */ + { + "win1254", PG_WIN1254 + }, /* alias for Windows-1254 */ + { + "win1255", PG_WIN1255 + }, /* alias for Windows-1255 */ + { + "win1256", PG_WIN1256 + }, /* alias for Windows-1256 */ + { + "win1257", PG_WIN1257 + }, /* alias for Windows-1257 */ + { + "win1258", PG_WIN1258 + }, /* alias for Windows-1258 */ + { + "win866", PG_WIN866 + }, /* IBM866 */ + { + "win874", PG_WIN874 + }, /* alias for Windows-874 */ + { + "win932", PG_SJIS + }, /* alias for Shift_JIS */ + { + "win936", PG_GBK + }, /* alias for GBK */ + { + "win949", PG_UHC + }, /* alias for UHC */ + { + "win950", PG_BIG5 + }, /* alias for BIG5 */ + { + "windows1250", PG_WIN1250 + }, /* Windows-1251; Microsoft */ + { + "windows1251", PG_WIN1251 + }, /* Windows-1251; Microsoft */ + { + "windows1252", PG_WIN1252 + }, /* Windows-1252; Microsoft */ + { + "windows1253", PG_WIN1253 + }, /* Windows-1253; Microsoft */ + { + "windows1254", PG_WIN1254 + }, /* Windows-1254; Microsoft */ + { + "windows1255", PG_WIN1255 + }, /* Windows-1255; Microsoft */ + { + "windows1256", PG_WIN1256 + }, /* Windows-1256; Microsoft */ + { + "windows1257", PG_WIN1257 + }, /* Windows-1257; Microsoft */ + { + "windows1258", PG_WIN1258 + }, /* Windows-1258; Microsoft */ + { + "windows866", PG_WIN866 + }, /* IBM866 */ + { + "windows874", PG_WIN874 + }, /* Windows-874; Microsoft */ + { + "windows932", PG_SJIS + }, /* alias for Shift_JIS */ + { + "windows936", PG_GBK + }, /* alias for GBK */ + { + "windows949", PG_UHC + }, /* alias for UHC */ + { + "windows950", PG_BIG5 + }, /* alias for BIG5 */ + { + NULL, 0 + } /* last */ +}; + +unsigned int pg_encname_tbl_sz = \ +sizeof(pg_encname_tbl) / sizeof(pg_encname_tbl[0]) - 1; + +/* ---------- + * These are "official" encoding names. + * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) + * ---------- + */ +#ifndef WIN32 +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name } +#else +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage } +#endif +pg_enc2name pg_enc2name_tbl[] = +{ + DEF_ENC2NAME(SQL_ASCII, 0), + DEF_ENC2NAME(EUC_JP, 20932), + DEF_ENC2NAME(EUC_CN, 20936), + DEF_ENC2NAME(EUC_KR, 51949), + DEF_ENC2NAME(EUC_TW, 0), + DEF_ENC2NAME(EUC_JIS_2004, 20932), + DEF_ENC2NAME(UTF8, 65001), + DEF_ENC2NAME(MULE_INTERNAL, 0), + DEF_ENC2NAME(LATIN1, 28591), + DEF_ENC2NAME(LATIN2, 28592), + DEF_ENC2NAME(LATIN3, 28593), + DEF_ENC2NAME(LATIN4, 28594), + DEF_ENC2NAME(LATIN5, 28599), + DEF_ENC2NAME(LATIN6, 0), + DEF_ENC2NAME(LATIN7, 0), + DEF_ENC2NAME(LATIN8, 0), + DEF_ENC2NAME(LATIN9, 28605), + DEF_ENC2NAME(LATIN10, 0), + DEF_ENC2NAME(WIN1256, 1256), + DEF_ENC2NAME(WIN1258, 1258), + DEF_ENC2NAME(WIN866, 866), + DEF_ENC2NAME(WIN874, 874), + DEF_ENC2NAME(KOI8R, 20866), + DEF_ENC2NAME(WIN1251, 1251), + DEF_ENC2NAME(WIN1252, 1252), + DEF_ENC2NAME(ISO_8859_5, 28595), + DEF_ENC2NAME(ISO_8859_6, 28596), + DEF_ENC2NAME(ISO_8859_7, 28597), + DEF_ENC2NAME(ISO_8859_8, 28598), + DEF_ENC2NAME(WIN1250, 1250), + DEF_ENC2NAME(WIN1253, 1253), + DEF_ENC2NAME(WIN1254, 1254), + DEF_ENC2NAME(WIN1255, 1255), + DEF_ENC2NAME(WIN1257, 1257), + DEF_ENC2NAME(KOI8U, 21866), + DEF_ENC2NAME(SJIS, 932), + DEF_ENC2NAME(BIG5, 950), + DEF_ENC2NAME(GBK, 936), + DEF_ENC2NAME(UHC, 0), + DEF_ENC2NAME(GB18030, 54936), + DEF_ENC2NAME(JOHAB, 0), + DEF_ENC2NAME(SHIFT_JIS_2004, 932) +}; + +/* ---------- + * These are encoding names for gettext. + * ---------- + */ +pg_enc2gettext pg_enc2gettext_tbl[] = +{ + {PG_UTF8, "UTF-8"}, + {PG_LATIN1, "LATIN1"}, + {PG_LATIN2, "LATIN2"}, + {PG_LATIN3, "LATIN3"}, + {PG_LATIN4, "LATIN4"}, + {PG_ISO_8859_5, "ISO-8859-5"}, + {PG_ISO_8859_6, "ISO_8859-6"}, + {PG_ISO_8859_7, "ISO-8859-7"}, + {PG_ISO_8859_8, "ISO-8859-8"}, + {PG_LATIN5, "LATIN5"}, + {PG_LATIN6, "LATIN6"}, + {PG_LATIN7, "LATIN7"}, + {PG_LATIN8, "LATIN8"}, + {PG_LATIN9, "LATIN-9"}, + {PG_LATIN10, "LATIN10"}, + {PG_KOI8R, "KOI8-R"}, + {PG_KOI8U, "KOI8-U"}, + {PG_WIN1250, "CP1250"}, + {PG_WIN1251, "CP1251"}, + {PG_WIN1252, "CP1252"}, + {PG_WIN1253, "CP1253"}, + {PG_WIN1254, "CP1254"}, + {PG_WIN1255, "CP1255"}, + {PG_WIN1256, "CP1256"}, + {PG_WIN1257, "CP1257"}, + {PG_WIN1258, "CP1258"}, + {PG_WIN866, "CP866"}, + {PG_WIN874, "CP874"}, + {PG_EUC_CN, "EUC-CN"}, + {PG_EUC_JP, "EUC-JP"}, + {PG_EUC_KR, "EUC-KR"}, + {PG_EUC_TW, "EUC-TW"}, + {PG_EUC_JIS_2004, "EUC-JP"}, + {0, NULL} +}; + + +/* ---------- + * Encoding checks, for error returns -1 else encoding id + * ---------- + */ +int +pg_valid_client_encoding(const char *name) +{ + int enc; + + if ((enc = pg_char_to_encoding(name)) < 0) + return -1; + + if (!PG_VALID_FE_ENCODING(enc)) + return -1; + + return enc; +} + +int +pg_valid_server_encoding(const char *name) +{ + int enc; + + if ((enc = pg_char_to_encoding(name)) < 0) + return -1; + + if (!PG_VALID_BE_ENCODING(enc)) + return -1; + + return enc; +} + +int +pg_valid_server_encoding_id(int encoding) +{ + return PG_VALID_BE_ENCODING(encoding); +} + +/* ---------- + * Remove irrelevant chars from encoding name + * ---------- + */ +static char * +clean_encoding_name(const char *key, char *newkey) +{ + const char *p; + char *np; + + for (p = key, np = newkey; *p != '\0'; p++) + { + if (isalnum((unsigned char) *p)) + { + if (*p >= 'A' && *p <= 'Z') + *np++ = *p + 'a' - 'A'; + else + *np++ = *p; + } + } + *np = '\0'; + return newkey; +} + +/* ---------- + * Search encoding by encoding name + * ---------- + */ +pg_encname * +pg_char_to_encname_struct(const char *name) +{ + unsigned int nel = pg_encname_tbl_sz; + pg_encname *base = pg_encname_tbl, + *last = base + nel - 1, + *position; + int result; + char buff[NAMEDATALEN], + *key; + + if (name == NULL || *name == '\0') + return NULL; + + if (strlen(name) >= NAMEDATALEN) + { +#ifdef FRONTEND + fprintf(stderr, "encoding name too long\n"); + return NULL; +#else + ereport(ERROR, + (errcode(ERRCODE_NAME_TOO_LONG), + errmsg("encoding name too long"))); +#endif + } + key = clean_encoding_name(name, buff); + + while (last >= base) + { + position = base + ((last - base) >> 1); + result = key[0] - position->name[0]; + + if (result == 0) + { + result = strcmp(key, position->name); + if (result == 0) + return position; + } + if (result < 0) + last = position - 1; + else + base = position + 1; + } + return NULL; +} + +/* + * Returns encoding or -1 for error + */ +int +pg_char_to_encoding(const char *name) +{ + pg_encname *p; + + if (!name) + return -1; + + p = pg_char_to_encname_struct(name); + return p ? p->encoding : -1; +} + +#ifndef FRONTEND +Datum +PG_char_to_encoding(PG_FUNCTION_ARGS) +{ + Name s = PG_GETARG_NAME(0); + + PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s))); +} +#endif + +const char * +pg_encoding_to_char(int encoding) +{ + if (PG_VALID_ENCODING(encoding)) + { + pg_enc2name *p = &pg_enc2name_tbl[encoding]; + + Assert(encoding == p->encoding); + return p->name; + } + return ""; +} + +#ifndef FRONTEND +Datum +PG_encoding_to_char(PG_FUNCTION_ARGS) +{ + int32 encoding = PG_GETARG_INT32(0); + const char *encoding_name = pg_encoding_to_char(encoding); + + return DirectFunctionCall1(namein, CStringGetDatum(encoding_name)); +} + +#endif diff --git a/src/libpq/exports.txt b/src/libpq/exports.txt new file mode 100644 index 00000000..1af8df69 --- /dev/null +++ b/src/libpq/exports.txt @@ -0,0 +1,162 @@ +# src/interfaces/libpq/exports.txt +# Functions to be exported by libpq DLLs +PQconnectdb 1 +PQsetdbLogin 2 +PQconndefaults 3 +PQfinish 4 +PQreset 5 +PQrequestCancel 6 +PQdb 7 +PQuser 8 +PQpass 9 +PQhost 10 +PQport 11 +PQtty 12 +PQoptions 13 +PQstatus 14 +PQerrorMessage 15 +PQsocket 16 +PQbackendPID 17 +PQtrace 18 +PQuntrace 19 +PQsetNoticeProcessor 20 +PQexec 21 +PQnotifies 22 +PQsendQuery 23 +PQgetResult 24 +PQisBusy 25 +PQconsumeInput 26 +PQgetline 27 +PQputline 28 +PQgetlineAsync 29 +PQputnbytes 30 +PQendcopy 31 +PQfn 32 +PQresultStatus 33 +PQntuples 34 +PQnfields 35 +PQbinaryTuples 36 +PQfname 37 +PQfnumber 38 +PQftype 39 +PQfsize 40 +PQfmod 41 +PQcmdStatus 42 +PQoidStatus 43 +PQcmdTuples 44 +PQgetvalue 45 +PQgetlength 46 +PQgetisnull 47 +PQclear 48 +PQmakeEmptyPGresult 49 +PQprint 50 +PQdisplayTuples 51 +PQprintTuples 52 +lo_open 53 +lo_close 54 +lo_read 55 +lo_write 56 +lo_lseek 57 +lo_creat 58 +lo_tell 59 +lo_unlink 60 +lo_import 61 +lo_export 62 +pgresStatus 63 +PQmblen 64 +PQresultErrorMessage 65 +PQresStatus 66 +termPQExpBuffer 67 +appendPQExpBufferChar 68 +initPQExpBuffer 69 +resetPQExpBuffer 70 +PQoidValue 71 +PQclientEncoding 72 +PQenv2encoding 73 +appendBinaryPQExpBuffer 74 +appendPQExpBufferStr 75 +destroyPQExpBuffer 76 +createPQExpBuffer 77 +PQconninfoFree 78 +PQconnectPoll 79 +PQconnectStart 80 +PQflush 81 +PQisnonblocking 82 +PQresetPoll 83 +PQresetStart 84 +PQsetClientEncoding 85 +PQsetnonblocking 86 +PQfreeNotify 87 +PQescapeString 88 +PQescapeBytea 89 +printfPQExpBuffer 90 +appendPQExpBuffer 91 +pg_encoding_to_char 92 +pg_utf_mblen 93 +PQunescapeBytea 94 +PQfreemem 95 +PQtransactionStatus 96 +PQparameterStatus 97 +PQprotocolVersion 98 +PQsetErrorVerbosity 99 +PQsetNoticeReceiver 100 +PQexecParams 101 +PQsendQueryParams 102 +PQputCopyData 103 +PQputCopyEnd 104 +PQgetCopyData 105 +PQresultErrorField 106 +PQftable 107 +PQftablecol 108 +PQfformat 109 +PQexecPrepared 110 +PQsendQueryPrepared 111 +PQdsplen 112 +PQserverVersion 113 +PQgetssl 114 +pg_char_to_encoding 115 +pg_valid_server_encoding 116 +pqsignal 117 +PQprepare 118 +PQsendPrepare 119 +PQgetCancel 120 +PQfreeCancel 121 +PQcancel 122 +lo_create 123 +PQinitSSL 124 +PQregisterThreadLock 125 +PQescapeStringConn 126 +PQescapeByteaConn 127 +PQencryptPassword 128 +PQisthreadsafe 129 +enlargePQExpBuffer 130 +PQnparams 131 +PQparamtype 132 +PQdescribePrepared 133 +PQdescribePortal 134 +PQsendDescribePrepared 135 +PQsendDescribePortal 136 +lo_truncate 137 +PQconnectionUsedPassword 138 +pg_valid_server_encoding_id 139 +PQconnectionNeedsPassword 140 +lo_import_with_oid 141 +PQcopyResult 142 +PQsetResultAttrs 143 +PQsetvalue 144 +PQresultAlloc 145 +PQregisterEventProc 146 +PQinstanceData 147 +PQsetInstanceData 148 +PQresultInstanceData 149 +PQresultSetInstanceData 150 +PQfireResultCreateEvents 151 +PQconninfoParse 152 +PQinitOpenSSL 153 +PQescapeLiteral 154 +PQescapeIdentifier 155 +PQconnectdbParams 156 +PQconnectStartParams 157 +PQping 158 +PQpingParams 159 +PQlibVersion 160 diff --git a/src/libpq/fe-auth.c b/src/libpq/fe-auth.c new file mode 100644 index 00000000..e72826cd --- /dev/null +++ b/src/libpq/fe-auth.c @@ -0,0 +1,1053 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.c + * The front-end (client) authorization routines + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/interfaces/libpq/fe-auth.c + * + *------------------------------------------------------------------------- + */ + +/* + * INTERFACE ROUTINES + * frontend (client) routines: + * pg_fe_sendauth send authentication information + * pg_fe_getauthname get user's name according to the client side + * of the authentication system + */ + +#include "postgres_fe.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#include +#ifdef HAVE_SYS_UCRED_H +#include +#endif +#ifndef MAXHOSTNAMELEN +#include /* for MAXHOSTNAMELEN on some */ +#endif +#include +#endif + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "libpq/md5.h" + + +#ifdef KRB5 +/* + * MIT Kerberos authentication system - protocol version 5 + */ + +#include +/* Some old versions of Kerberos do not include in */ +#if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__) +#include +#endif + +/* + * Heimdal doesn't have a free function for unparsed names. Just pass it to + * standard free() which should work in these cases. + */ +#ifndef HAVE_KRB5_FREE_UNPARSED_NAME +static void +krb5_free_unparsed_name(krb5_context context, char *val) +{ + free(val); +} +#endif + +/* + * pg_an_to_ln -- return the local name corresponding to an authentication + * name + * + * XXX Assumes that the first aname component is the user name. This is NOT + * necessarily so, since an aname can actually be something out of your + * worst X.400 nightmare, like + * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU + * Note that the MIT an_to_ln code does the same thing if you don't + * provide an aname mapping database...it may be a better idea to use + * krb5_an_to_ln, except that it punts if multiple components are found, + * and we can't afford to punt. + * + * For WIN32, convert username to lowercase because the Win32 kerberos library + * generates tickets with the username as the user entered it instead of as + * it is entered in the directory. + */ +static char * +pg_an_to_ln(char *aname) +{ + char *p; + + if ((p = strchr(aname, '/')) || (p = strchr(aname, '@'))) + *p = '\0'; +#ifdef WIN32 + for (p = aname; *p; p++) + *p = pg_tolower((unsigned char) *p); +#endif + + return aname; +} + + +/* + * Various krb5 state which is not connection specific, and a flag to + * indicate whether we have initialised it yet. + */ +/* +static int pg_krb5_initialised; +static krb5_context pg_krb5_context; +static krb5_ccache pg_krb5_ccache; +static krb5_principal pg_krb5_client; +static char *pg_krb5_name; +*/ + +struct krb5_info +{ + int pg_krb5_initialised; + krb5_context pg_krb5_context; + krb5_ccache pg_krb5_ccache; + krb5_principal pg_krb5_client; + char *pg_krb5_name; +}; + + +static int +pg_krb5_init(PQExpBuffer errorMessage, struct krb5_info * info) +{ + krb5_error_code retval; + + if (info->pg_krb5_initialised) + return STATUS_OK; + + retval = krb5_init_context(&(info->pg_krb5_context)); + if (retval) + { + printfPQExpBuffer(errorMessage, + "pg_krb5_init: krb5_init_context: %s\n", + error_message(retval)); + return STATUS_ERROR; + } + + retval = krb5_cc_default(info->pg_krb5_context, &(info->pg_krb5_ccache)); + if (retval) + { + printfPQExpBuffer(errorMessage, + "pg_krb5_init: krb5_cc_default: %s\n", + error_message(retval)); + krb5_free_context(info->pg_krb5_context); + return STATUS_ERROR; + } + + retval = krb5_cc_get_principal(info->pg_krb5_context, info->pg_krb5_ccache, + &(info->pg_krb5_client)); + if (retval) + { + printfPQExpBuffer(errorMessage, + "pg_krb5_init: krb5_cc_get_principal: %s\n", + error_message(retval)); + krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache); + krb5_free_context(info->pg_krb5_context); + return STATUS_ERROR; + } + + retval = krb5_unparse_name(info->pg_krb5_context, info->pg_krb5_client, &(info->pg_krb5_name)); + if (retval) + { + printfPQExpBuffer(errorMessage, + "pg_krb5_init: krb5_unparse_name: %s\n", + error_message(retval)); + krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client); + krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache); + krb5_free_context(info->pg_krb5_context); + return STATUS_ERROR; + } + + info->pg_krb5_name = pg_an_to_ln(info->pg_krb5_name); + + info->pg_krb5_initialised = 1; + return STATUS_OK; +} + +static void +pg_krb5_destroy(struct krb5_info * info) +{ + krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client); + krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache); + krb5_free_unparsed_name(info->pg_krb5_context, info->pg_krb5_name); + krb5_free_context(info->pg_krb5_context); +} + + +/* + * pg_krb5_sendauth -- client routine to send authentication information to + * the server + */ +static int +pg_krb5_sendauth(PGconn *conn) +{ + krb5_error_code retval; + int ret; + krb5_principal server; + krb5_auth_context auth_context = NULL; + krb5_error *err_ret = NULL; + struct krb5_info info; + + info.pg_krb5_initialised = 0; + + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + + ret = pg_krb5_init(&conn->errorMessage, &info); + if (ret != STATUS_OK) + return ret; + + retval = krb5_sname_to_principal(info.pg_krb5_context, conn->pghost, + conn->krbsrvname, + KRB5_NT_SRV_HST, &server); + if (retval) + { + printfPQExpBuffer(&conn->errorMessage, + "pg_krb5_sendauth: krb5_sname_to_principal: %s\n", + error_message(retval)); + pg_krb5_destroy(&info); + return STATUS_ERROR; + } + + /* + * libpq uses a non-blocking socket. But kerberos needs a blocking socket, + * and we have to block somehow to do mutual authentication anyway. So we + * temporarily make it blocking. + */ + if (!pg_set_block(conn->sock)) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); + krb5_free_principal(info.pg_krb5_context, server); + pg_krb5_destroy(&info); + return STATUS_ERROR; + } + + retval = krb5_sendauth(info.pg_krb5_context, &auth_context, + (krb5_pointer) &conn->sock, (char *) conn->krbsrvname, + info.pg_krb5_client, server, + AP_OPTS_MUTUAL_REQUIRED, + NULL, 0, /* no creds, use ccache instead */ + info.pg_krb5_ccache, &err_ret, NULL, NULL); + if (retval) + { + if (retval == KRB5_SENDAUTH_REJECTED && err_ret) + { +#if defined(HAVE_KRB5_ERROR_TEXT_DATA) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), + (int) err_ret->text.length, err_ret->text.data); +#elif defined(HAVE_KRB5_ERROR_E_DATA) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), + (int) err_ret->e_data->length, + (const char *) err_ret->e_data->data); +#else +#error "bogus configuration" +#endif + } + else + { + printfPQExpBuffer(&conn->errorMessage, + "krb5_sendauth: %s\n", error_message(retval)); + } + + if (err_ret) + krb5_free_error(info.pg_krb5_context, err_ret); + + ret = STATUS_ERROR; + } + + krb5_free_principal(info.pg_krb5_context, server); + + if (!pg_set_noblock(conn->sock)) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not restore non-blocking mode on socket: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + ret = STATUS_ERROR; + } + pg_krb5_destroy(&info); + + return ret; +} +#endif /* KRB5 */ + +#ifdef ENABLE_GSS +/* + * GSSAPI authentication system. + */ + +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +/* + * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW + * that contain the OIDs required. Redefine here, values copied + * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c + */ +static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc = +{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}; +static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc; +#endif + +/* + * Fetch all errors of a specific type and append to "str". + */ +static void +pg_GSS_error_int(PQExpBuffer str, const char *mprefix, + OM_uint32 stat, int type) +{ + OM_uint32 lmin_s; + gss_buffer_desc lmsg; + OM_uint32 msg_ctx = 0; + + do + { + gss_display_status(&lmin_s, stat, type, + GSS_C_NO_OID, &msg_ctx, &lmsg); + appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value); + gss_release_buffer(&lmin_s, &lmsg); + } while (msg_ctx); +} + +/* + * GSSAPI errors contain two parts; put both into conn->errorMessage. + */ +static void +pg_GSS_error(const char *mprefix, PGconn *conn, + OM_uint32 maj_stat, OM_uint32 min_stat) +{ + resetPQExpBuffer(&conn->errorMessage); + + /* Fetch major error codes */ + pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE); + + /* Add the minor codes as well */ + pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE); +} + +/* + * Continue GSS authentication with next token as needed. + */ +static int +pg_GSS_continue(PGconn *conn) +{ + OM_uint32 maj_stat, + min_stat, + lmin_s; + + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &conn->gctx, + conn->gtarg_nam, + GSS_C_NO_OID, + GSS_C_MUTUAL_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf, + NULL, + &conn->goutbuf, + NULL, + NULL); + + if (conn->gctx != GSS_C_NO_CONTEXT) + { + free(conn->ginbuf.value); + conn->ginbuf.value = NULL; + conn->ginbuf.length = 0; + } + + if (conn->goutbuf.length != 0) + { + /* + * GSS generated data to send to the server. We don't care if it's the + * first or subsequent packet, just send the same kind of password + * packet. + */ + if (pqPacketSend(conn, 'p', + conn->goutbuf.value, conn->goutbuf.length) + != STATUS_OK) + { + gss_release_buffer(&lmin_s, &conn->goutbuf); + return STATUS_ERROR; + } + } + gss_release_buffer(&lmin_s, &conn->goutbuf); + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) + { + pg_GSS_error(libpq_gettext("GSSAPI continuation error"), + conn, + maj_stat, min_stat); + gss_release_name(&lmin_s, &conn->gtarg_nam); + if (conn->gctx) + gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); + return STATUS_ERROR; + } + + if (maj_stat == GSS_S_COMPLETE) + gss_release_name(&lmin_s, &conn->gtarg_nam); + + return STATUS_OK; +} + +/* + * Send initial GSS authentication token + */ +static int +pg_GSS_startup(PGconn *conn) +{ + OM_uint32 maj_stat, + min_stat; + int maxlen; + gss_buffer_desc temp_gbuf; + + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + + if (conn->gctx) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("duplicate GSS authentication request\n")); + return STATUS_ERROR; + } + + /* + * Import service principal name so the proper ticket can be acquired by + * the GSSAPI system. + */ + maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2; + temp_gbuf.value = (char *) malloc(maxlen); + snprintf(temp_gbuf.value, maxlen, "%s@%s", + conn->krbsrvname, conn->pghost); + temp_gbuf.length = strlen(temp_gbuf.value); + + maj_stat = gss_import_name(&min_stat, &temp_gbuf, + GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam); + free(temp_gbuf.value); + + if (maj_stat != GSS_S_COMPLETE) + { + pg_GSS_error(libpq_gettext("GSSAPI name import error"), + conn, + maj_stat, min_stat); + return STATUS_ERROR; + } + + /* + * Initial packet is the same as a continuation packet with no initial + * context. + */ + conn->gctx = GSS_C_NO_CONTEXT; + + return pg_GSS_continue(conn); +} +#endif /* ENABLE_GSS */ + + +#ifdef ENABLE_SSPI +/* + * SSPI authentication system (Windows only) + */ + +static void +pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r) +{ + char sysmsg[256]; + + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, + sysmsg, sizeof(sysmsg), NULL) == 0) + printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x", + mprefix, (unsigned int) r); + else + printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)", + mprefix, sysmsg, (unsigned int) r); +} + +/* + * Continue SSPI authentication with next token as needed. + */ +static int +pg_SSPI_continue(PGconn *conn) +{ + SECURITY_STATUS r; + CtxtHandle newContext; + ULONG contextAttr; + SecBufferDesc inbuf; + SecBufferDesc outbuf; + SecBuffer OutBuffers[1]; + SecBuffer InBuffers[1]; + + if (conn->sspictx != NULL) + { + /* + * On runs other than the first we have some data to send. Put this + * data in a SecBuffer type structure. + */ + inbuf.ulVersion = SECBUFFER_VERSION; + inbuf.cBuffers = 1; + inbuf.pBuffers = InBuffers; + InBuffers[0].pvBuffer = conn->ginbuf.value; + InBuffers[0].cbBuffer = conn->ginbuf.length; + InBuffers[0].BufferType = SECBUFFER_TOKEN; + } + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + outbuf.cBuffers = 1; + outbuf.pBuffers = OutBuffers; + outbuf.ulVersion = SECBUFFER_VERSION; + + r = InitializeSecurityContext(conn->sspicred, + conn->sspictx, + conn->sspitarget, + ISC_REQ_ALLOCATE_MEMORY, + 0, + SECURITY_NETWORK_DREP, + (conn->sspictx == NULL) ? NULL : &inbuf, + 0, + &newContext, + &outbuf, + &contextAttr, + NULL); + + if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) + { + pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r); + + return STATUS_ERROR; + } + + if (conn->sspictx == NULL) + { + /* On first run, transfer retreived context handle */ + conn->sspictx = malloc(sizeof(CtxtHandle)); + if (conn->sspictx == NULL) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); + } + else + { + /* + * On subsequent runs when we had data to send, free buffers that + * contained this data. + */ + free(conn->ginbuf.value); + conn->ginbuf.value = NULL; + conn->ginbuf.length = 0; + } + + /* + * If SSPI returned any data to be sent to the server (as it normally + * would), send this data as a password packet. + */ + if (outbuf.cBuffers > 0) + { + if (outbuf.cBuffers != 1) + { + /* + * This should never happen, at least not for Kerberos + * authentication. Keep check in case it shows up with other + * authentication methods later. + */ + printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); + return STATUS_ERROR; + } + + /* + * If the negotiation is complete, there may be zero bytes to send. + * The server is at this point not expecting any more data, so don't + * send it. + */ + if (outbuf.pBuffers[0].cbBuffer > 0) + { + if (pqPacketSend(conn, 'p', + outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) + { + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + return STATUS_ERROR; + } + } + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + } + + /* Cleanup is handled by the code in freePGconn() */ + return STATUS_OK; +} + +/* + * Send initial SSPI authentication token. + * If use_negotiate is 0, use kerberos authentication package which is + * compatible with Unix. If use_negotiate is 1, use the negotiate package + * which supports both kerberos and NTLM, but is not compatible with Unix. + */ +static int +pg_SSPI_startup(PGconn *conn, int use_negotiate) +{ + SECURITY_STATUS r; + TimeStamp expire; + + conn->sspictx = NULL; + + /* + * Retreive credentials handle + */ + conn->sspicred = malloc(sizeof(CredHandle)); + if (conn->sspicred == NULL) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + + r = AcquireCredentialsHandle(NULL, + use_negotiate ? "negotiate" : "kerberos", + SECPKG_CRED_OUTBOUND, + NULL, + NULL, + NULL, + NULL, + conn->sspicred, + &expire); + if (r != SEC_E_OK) + { + pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r); + free(conn->sspicred); + conn->sspicred = NULL; + return STATUS_ERROR; + } + + /* + * Compute target principal name. SSPI has a different format from GSSAPI, + * but not more complex. We can skip the @REALM part, because Windows will + * fill that in for us automatically. + */ + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2); + if (!conn->sspitarget) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost); + + /* + * Indicate that we're in SSPI authentication mode to make sure that + * pg_SSPI_continue is called next time in the negotiation. + */ + conn->usesspi = 1; + + return pg_SSPI_continue(conn); +} +#endif /* ENABLE_SSPI */ + +/* + * Respond to AUTH_REQ_SCM_CREDS challenge. + * + * Note: this is dead code as of Postgres 9.1, because current backends will + * never send this challenge. But we must keep it as long as libpq needs to + * interoperate with pre-9.1 servers. It is believed to be needed only on + * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the + * getpeereid() function isn't provided by libc). + */ +static int +pg_local_sendauth(PGconn *conn) +{ +#ifdef HAVE_STRUCT_CMSGCRED + char buf; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + union + { + struct cmsghdr hdr; + unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; + } cmsgbuf; + + /* + * The backend doesn't care what we send here, but it wants exactly one + * character to force recvmsg() to block and wait for us. + */ + buf = '\0'; + iov.iov_base = &buf; + iov.iov_len = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* We must set up a message that will be filled in by kernel */ + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDS; + + if (sendmsg(conn->sock, &msg, 0) == -1) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + "pg_local_sendauth: sendmsg: %s\n", + pqStrerror(errno, sebuf, sizeof(sebuf))); + return STATUS_ERROR; + } + return STATUS_OK; +#else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SCM_CRED authentication method not supported\n")); + return STATUS_ERROR; +#endif +} + +static int +pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) +{ + int ret; + char *crypt_pwd; + + /* Encrypt the password if needed. */ + + switch (areq) + { + case AUTH_REQ_MD5: + { + char *crypt_pwd2; + + /* Allocate enough space for two MD5 hashes */ + crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); + if (!crypt_pwd) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + + crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; + if (!pg_md5_encrypt(password, conn->pguser, + strlen(conn->pguser), crypt_pwd2)) + { + free(crypt_pwd); + return STATUS_ERROR; + } + if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, + sizeof(conn->md5Salt), crypt_pwd)) + { + free(crypt_pwd); + return STATUS_ERROR; + } + break; + } + case AUTH_REQ_PASSWORD: + /* discard const so we can assign it */ + crypt_pwd = (char *) password; + break; + default: + return STATUS_ERROR; + } + /* Packet has a message type as of protocol 3.0 */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1); + else + ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1); + if (areq == AUTH_REQ_MD5) + free(crypt_pwd); + return ret; +} + +/* + * pg_fe_sendauth + * client demux routine for outgoing authentication information + */ +int +pg_fe_sendauth(AuthRequest areq, PGconn *conn) +{ + switch (areq) + { + case AUTH_REQ_OK: + break; + + case AUTH_REQ_KRB4: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 4 authentication not supported\n")); + return STATUS_ERROR; + + case AUTH_REQ_KRB5: +#ifdef KRB5 + pglock_thread(); + if (pg_krb5_sendauth(conn) != STATUS_OK) + { + /* Error message already filled in */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + break; +#else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 5 authentication not supported\n")); + return STATUS_ERROR; +#endif + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + case AUTH_REQ_GSS: +#if !defined(ENABLE_SSPI) + /* no native SSPI, so use GSSAPI library for it */ + case AUTH_REQ_SSPI: +#endif + { + int r; + + pglock_thread(); + + /* + * If we have both GSS and SSPI support compiled in, use SSPI + * support by default. This is overridable by a connection + * string parameter. Note that when using SSPI we still leave + * the negotiate parameter off, since we want SSPI to use the + * GSSAPI kerberos protocol. For actual SSPI negotiate + * protocol, we use AUTH_REQ_SSPI. + */ +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0)) + r = pg_GSS_startup(conn); + else + r = pg_SSPI_startup(conn, 0); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_startup(conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_startup(conn, 0); +#endif + if (r != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + } + break; + + case AUTH_REQ_GSS_CONT: + { + int r; + + pglock_thread(); +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->usesspi) + r = pg_SSPI_continue(conn); + else + r = pg_GSS_continue(conn); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_continue(conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_continue(conn); +#endif + if (r != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + } + break; +#else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ + /* No GSSAPI *or* SSPI support */ + case AUTH_REQ_GSS: + case AUTH_REQ_GSS_CONT: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("GSSAPI authentication not supported\n")); + return STATUS_ERROR; +#endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ + +#ifdef ENABLE_SSPI + case AUTH_REQ_SSPI: + + /* + * SSPI has it's own startup message so libpq can decide which + * method to use. Indicate to pg_SSPI_startup that we want SSPI + * negotiation instead of Kerberos. + */ + pglock_thread(); + if (pg_SSPI_startup(conn, 1) != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + break; +#else + + /* + * No SSPI support. However, if we have GSSAPI but not SSPI + * support, AUTH_REQ_SSPI will have been handled in the codepath + * for AUTH_REQ_GSSAPI above, so don't duplicate the case label in + * that case. + */ +#if !defined(ENABLE_GSS) + case AUTH_REQ_SSPI: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSPI authentication not supported\n")); + return STATUS_ERROR; +#endif /* !define(ENABLE_GSSAPI) */ +#endif /* ENABLE_SSPI */ + + + case AUTH_REQ_CRYPT: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Crypt authentication not supported\n")); + return STATUS_ERROR; + + case AUTH_REQ_MD5: + case AUTH_REQ_PASSWORD: + conn->password_needed = true; + if (conn->pgpass == NULL || conn->pgpass[0] == '\0') + { + printfPQExpBuffer(&conn->errorMessage, + PQnoPasswordSupplied); + return STATUS_ERROR; + } + if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK) + { + printfPQExpBuffer(&conn->errorMessage, + "fe_sendauth: error sending password authentication\n"); + return STATUS_ERROR; + } + break; + + case AUTH_REQ_SCM_CREDS: + if (pg_local_sendauth(conn) != STATUS_OK) + return STATUS_ERROR; + break; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("authentication method %u not supported\n"), areq); + return STATUS_ERROR; + } + + return STATUS_OK; +} + + +/* + * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever + * name the user has authenticated to the system + * + * if there is an error, return NULL with an error message in errorMessage + */ +char * +pg_fe_getauthname(PQExpBuffer errorMessage) +{ + const char *name = NULL; + char *authn; + +#ifdef WIN32 + char username[128]; + DWORD namesize = sizeof(username) - 1; +#else + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pw = NULL; +#endif + + /* + * Some users are using configure --enable-thread-safety-force, so we + * might as well do the locking within our library to protect + * pqGetpwuid(). In fact, application developers can use getpwuid() in + * their application if they use the locking call we provide, or install + * their own locking function using PQregisterThreadLock(). + */ + pglock_thread(); + + if (!name) + { +#ifdef WIN32 + if (GetUserName(username, &namesize)) + name = username; +#else + if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0) + name = pw->pw_name; +#endif + } + + authn = name ? strdup(name) : NULL; + + pgunlock_thread(); + + return authn; +} + + +/* + * PQencryptPassword -- exported routine to encrypt a password + * + * This is intended to be used by client applications that wish to send + * commands like ALTER USER joe PASSWORD 'pwd'. The password need not + * be sent in cleartext if it is encrypted on the client side. This is + * good because it ensures the cleartext password won't end up in logs, + * pg_stat displays, etc. We export the function so that clients won't + * be dependent on low-level details like whether the enceyption is MD5 + * or something else. + * + * Arguments are the cleartext password, and the SQL name of the user it + * is for. + * + * Return value is a malloc'd string, or NULL if out-of-memory. The client + * may assume the string doesn't contain any special characters that would + * require escaping. + */ +char * +PQencryptPassword(const char *passwd, const char *user) +{ + char *crypt_pwd; + + crypt_pwd = malloc(MD5_PASSWD_LEN + 1); + if (!crypt_pwd) + return NULL; + + if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) + { + free(crypt_pwd); + return NULL; + } + + return crypt_pwd; +} diff --git a/src/libpq/fe-auth.h b/src/libpq/fe-auth.h new file mode 100644 index 00000000..bfe673e7 --- /dev/null +++ b/src/libpq/fe-auth.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.h + * + * Definitions for network authentication routines + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/fe-auth.h + * + *------------------------------------------------------------------------- + */ +#ifndef FE_AUTH_H +#define FE_AUTH_H + +#include "libpq-fe.h" +#include "libpq-int.h" + + +extern int pg_fe_sendauth(AuthRequest areq, PGconn *conn); +extern char *pg_fe_getauthname(PQExpBuffer errorMessage); + +#endif /* FE_AUTH_H */ diff --git a/src/libpq/fe-connect.c b/src/libpq/fe-connect.c new file mode 100644 index 00000000..2c3d544d --- /dev/null +++ b/src/libpq/fe-connect.c @@ -0,0 +1,5061 @@ +/*------------------------------------------------------------------------- + * + * fe-connect.c + * functions related to setting up a connection to the backend + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-connect.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include +#include +#include +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "fe-auth.h" +#include "pg_config_paths.h" + +#ifdef WIN32 +#include "win32.h" +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0500 +#ifdef near +#undef near +#endif +#define near +#include +#ifdef WIN32_ONLY_COMPILER /* mstcpip.h is missing on mingw */ +#include +#endif +#else +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#endif + +#ifdef USE_LDAP +#ifdef WIN32 +#include +#else +/* OpenLDAP deprecates RFC 1823, but we want standard conformance */ +#define LDAP_DEPRECATED 1 +#include +typedef struct timeval LDAP_TIMEVAL; +#endif +static int ldapServiceLookup(const char *purl, PQconninfoOption *options, + PQExpBuffer errorMessage); +#endif + +#include "libpq/ip.h" +#include "mb/pg_wchar.h" + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + + +#ifndef WIN32 +#define PGPASSFILE ".pgpass" +#else +#define PGPASSFILE "pgpass.conf" +#endif + +/* + * Pre-9.0 servers will return this SQLSTATE if asked to set + * application_name in a startup packet. We hard-wire the value rather + * than looking into errcodes.h since it reflects historical behavior + * rather than that of the current code. + */ +#define ERRCODE_APPNAME_UNKNOWN "42704" + +/* This is part of the protocol so just define it */ +#define ERRCODE_INVALID_PASSWORD "28P01" +/* This too */ +#define ERRCODE_CANNOT_CONNECT_NOW "57P03" + +/* + * fall back options if they are not specified by arguments or defined + * by environment variables + */ +#define DefaultHost "localhost" +#define DefaultTty "" +#define DefaultOption "" +#define DefaultAuthtype "" +#define DefaultPassword "" +#ifdef USE_SSL +#define DefaultSSLMode "prefer" +#else +#define DefaultSSLMode "disable" +#endif + +/* ---------- + * 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 value for the username is treated specially in conninfo_parse. + * If the Compiled-in resource is specified as a NULL value, the + * user is determined by pg_fe_getauthname(). + * + * 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", NULL, NULL, + "Database-Password", "*", 20}, + + {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL, + "Connect-timeout", "", 10}, /* strlen(INT32_MAX) == 10 */ + + {"dbname", "PGDATABASE", NULL, NULL, + "Database-Name", "", 20}, + + {"host", "PGHOST", NULL, NULL, + "Database-Host", "", 40}, + + {"hostaddr", "PGHOSTADDR", NULL, NULL, + "Database-Host-IP-Address", "", 45}, + + {"port", "PGPORT", DEF_PGPORT_STR, NULL, + "Database-Port", "", 6}, + + {"client_encoding", "PGCLIENTENCODING", NULL, NULL, + "Client-Encoding", "", 10}, + + /* + * "tty" is no longer used either, but keep it present for backwards + * compatibility. + */ + {"tty", "PGTTY", DefaultTty, NULL, + "Backend-Debug-TTY", "D", 40}, + + {"options", "PGOPTIONS", DefaultOption, NULL, + "Backend-Debug-Options", "D", 40}, + + {"application_name", "PGAPPNAME", NULL, NULL, + "Application-Name", "", 64}, + + {"fallback_application_name", NULL, NULL, NULL, + "Fallback-Application-Name", "", 64}, + + {"keepalives", NULL, NULL, NULL, + "TCP-Keepalives", "", 1}, /* should be just '0' or '1' */ + + {"keepalives_idle", NULL, NULL, NULL, + "TCP-Keepalives-Idle", "", 10}, /* strlen(INT32_MAX) == 10 */ + + {"keepalives_interval", NULL, NULL, NULL, + "TCP-Keepalives-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */ + + {"keepalives_count", NULL, NULL, NULL, + "TCP-Keepalives-Count", "", 10}, /* strlen(INT32_MAX) == 10 */ + +#ifdef USE_SSL + + /* + * "requiressl" is deprecated, its purpose having been taken over by + * "sslmode". It remains for backwards compatibility. + */ + {"requiressl", "PGREQUIRESSL", "0", NULL, + "Require-SSL", "D", 1}, +#endif + + /* + * ssl options are allowed even without client SSL support because the + * client can still handle SSL modes "disable" and "allow". Other + * parameters have no effect on non-SSL connections, so there is no reason + * to exclude them since none of them are mandatory. + */ + {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL, + "SSL-Mode", "", 8}, /* sizeof("disable") == 8 */ + + {"sslcert", "PGSSLCERT", NULL, NULL, + "SSL-Client-Cert", "", 64}, + + {"sslkey", "PGSSLKEY", NULL, NULL, + "SSL-Client-Key", "", 64}, + + {"sslrootcert", "PGSSLROOTCERT", NULL, NULL, + "SSL-Root-Certificate", "", 64}, + + {"sslcrl", "PGSSLCRL", NULL, NULL, + "SSL-Revocation-List", "", 64}, + + {"requirepeer", "PGREQUIREPEER", NULL, NULL, + "Require-Peer", "", 10}, + +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) + /* Kerberos and GSSAPI authentication support specifying the service name */ + {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL, + "Kerberos-service-name", "", 20}, +#endif + +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + + /* + * GSSAPI and SSPI both enabled, give a way to override which is used by + * default + */ + {"gsslib", "PGGSSLIB", NULL, NULL, + "GSS-library", "", 7}, /* sizeof("gssapi") = 7 */ +#endif + + {"replication", NULL, NULL, NULL, + "Replication", "D", 5}, + + /* Terminating entry --- MUST BE LAST */ + {NULL, NULL, NULL, NULL, + NULL, NULL, 0} +}; + +static const PQEnvironmentOption EnvironmentOptions[] = +{ + /* common user-interface settings */ + { + "PGDATESTYLE", "datestyle" + }, + { + "PGTZ", "timezone" + }, + /* internal performance-related settings */ + { + "PGGEQO", "geqo" + }, + { + NULL, NULL + } +}; + + +static bool connectOptions1(PGconn *conn, const char *conninfo); +static bool connectOptions2(PGconn *conn); +static int connectDBStart(PGconn *conn); +static int connectDBComplete(PGconn *conn); +static PGPing internal_ping(PGconn *conn); +static PGconn *makeEmptyPGconn(void); +static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions); +static void freePGconn(PGconn *conn); +static void closePGconn(PGconn *conn); +static PQconninfoOption *conninfo_parse(const char *conninfo, + PQExpBuffer errorMessage, bool use_defaults); +static PQconninfoOption *conninfo_array_parse(const char **keywords, + const char **values, PQExpBuffer errorMessage, + bool use_defaults, int expand_dbname); +static char *conninfo_getval(PQconninfoOption *connOptions, + const char *keyword); +static void defaultNoticeReceiver(void *arg, const PGresult *res); +static void defaultNoticeProcessor(void *arg, const char *message); +static int parseServiceInfo(PQconninfoOption *options, + PQExpBuffer errorMessage); +static int parseServiceFile(const char *serviceFile, + const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found); +static char *pwdfMatchesString(char *buf, char *token); +static char *PasswordFromFile(char *hostname, char *port, char *dbname, + char *username); +static bool getPgPassFilename(char *pgpassfile); +static void dot_pg_pass_warning(PGconn *conn); +static void default_threadlock(int acquire); + + +/* global variable because fe-auth.c needs to access it */ +pgthreadlock_t pg_g_threadlock = default_threadlock; + + +/* + * Connecting to a Database + * + * There are now six 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 or PQconnectdbParams. The former accepts a string + * of option = value pairs which must be parsed; the latter takes two NULL + * terminated arrays instead. + * + * To connect in an asynchronous (non-blocking) manner, use the functions + * PQconnectStart or PQconnectStartParams (which differ in the same way as + * PQconnectdb and PQconnectdbParams) and PQconnectPoll. + * + * Internally, the static functions connectDBStart, connectDBComplete + * are part of the connection procedure. + */ + +/* + * PQconnectdbParams + * + * establishes a connection to a postgres backend through the postmaster + * using connection information in two arrays. + * + * The keywords array is defined as + * + * const char *params[] = {"option1", "option2", NULL} + * + * The values array is defined as + * + * const char *values[] = {"value1", "value2", NULL} + * + * 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 * +PQconnectdbParams(const char **keywords, + const char **values, + int expand_dbname) +{ + PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); + + if (conn && conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + return conn; + +} + +/* + * PQpingParams + * + * check server status, accepting parameters identical to PQconnectdbParams + */ +PGPing +PQpingParams(const char **keywords, + const char **values, + int expand_dbname) +{ + PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); + PGPing ret; + + ret = internal_ping(conn); + PQfinish(conn); + + return ret; +} + +/* + * 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; +} + +/* + * PQping + * + * check server status, accepting parameters identical to PQconnectdb + */ +PGPing +PQping(const char *conninfo) +{ + PGconn *conn = PQconnectStart(conninfo); + PGPing ret; + + ret = internal_ping(conn); + PQfinish(conn); + + return ret; +} + +/* + * PQconnectStartParams + * + * Begins the establishment of a connection to a postgres backend through the + * postmaster using connection information in a struct. + * + * See comment for PQconnectdbParams 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 * +PQconnectStartParams(const char **keywords, + const char **values, + int expand_dbname) +{ + PGconn *conn; + PQconninfoOption *connOptions; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * Parse the conninfo arrays + */ + connOptions = conninfo_array_parse(keywords, values, + &conn->errorMessage, + true, expand_dbname); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return conn; + } + + /* + * Move option values into conn structure + */ + fillPGconn(conn, connOptions); + + /* + * Free the option info - all is in conn now + */ + PQconninfoFree(connOptions); + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (!connectDBStart(conn)) + { + /* Just in case we failed to set it in connectDBStart */ + conn->status = CONNECTION_BAD; + } + + 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; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * Parse the conninfo string + */ + if (!connectOptions1(conn, conninfo)) + return conn; + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (!connectDBStart(conn)) + { + /* Just in case we failed to set it in connectDBStart */ + conn->status = CONNECTION_BAD; + } + + return conn; +} + +static void +fillPGconn(PGconn *conn, PQconninfoOption *connOptions) +{ + char *tmp; + + /* + * Move option values into conn structure + * + * Don't put anything cute here --- intelligence should be in + * connectOptions2 ... + * + * XXX: probably worth checking strdup() return value here... + */ + 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, "tty"); + conn->pgtty = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "options"); + conn->pgoptions = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "application_name"); + conn->appname = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "fallback_application_name"); + conn->fbappname = 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; + tmp = conninfo_getval(connOptions, "connect_timeout"); + conn->connect_timeout = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "client_encoding"); + conn->client_encoding_initial = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "keepalives"); + conn->keepalives = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "keepalives_idle"); + conn->keepalives_idle = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "keepalives_interval"); + conn->keepalives_interval = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "keepalives_count"); + conn->keepalives_count = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "sslmode"); + conn->sslmode = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "sslkey"); + conn->sslkey = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "sslcert"); + conn->sslcert = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "sslrootcert"); + conn->sslrootcert = tmp ? strdup(tmp) : NULL; + tmp = conninfo_getval(connOptions, "sslcrl"); + conn->sslcrl = tmp ? strdup(tmp) : NULL; +#ifdef USE_SSL + tmp = conninfo_getval(connOptions, "requiressl"); + if (tmp && tmp[0] == '1') + { + /* here warn that the requiressl option is deprecated? */ + if (conn->sslmode) + free(conn->sslmode); + conn->sslmode = strdup("require"); + } +#endif + tmp = conninfo_getval(connOptions, "requirepeer"); + conn->requirepeer = tmp ? strdup(tmp) : NULL; +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) + tmp = conninfo_getval(connOptions, "krbsrvname"); + conn->krbsrvname = tmp ? strdup(tmp) : NULL; +#endif +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + tmp = conninfo_getval(connOptions, "gsslib"); + conn->gsslib = tmp ? strdup(tmp) : NULL; +#endif + tmp = conninfo_getval(connOptions, "replication"); + conn->replication = tmp ? strdup(tmp) : NULL; +} + +/* + * connectOptions1 + * + * Internal subroutine to set up connection parameters given an already- + * created PGconn and a conninfo string. Derived settings should be + * processed by calling connectOptions2 next. (We split them because + * PQsetdbLogin overrides defaults in between.) + * + * Returns true if OK, false if trouble (in which case errorMessage is set + * and so is conn->status). + */ +static bool +connectOptions1(PGconn *conn, const char *conninfo) +{ + PQconninfoOption *connOptions; + + /* + * Parse the conninfo string + */ + connOptions = conninfo_parse(conninfo, &conn->errorMessage, true); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return false; + } + + /* + * Move option values into conn structure + */ + fillPGconn(conn, connOptions); + + /* + * Free the option info - all is in conn now + */ + PQconninfoFree(connOptions); + + return true; +} + +/* + * connectOptions2 + * + * Compute derived connection options after absorbing all user-supplied info. + * + * Returns true if OK, false if trouble (in which case errorMessage is set + * and so is conn->status). + */ +static bool +connectOptions2(PGconn *conn) +{ + /* + * If database name was not given, default it to equal user name + */ + if ((conn->dbName == NULL || conn->dbName[0] == '\0') + && conn->pguser != NULL) + { + if (conn->dbName) + free(conn->dbName); + conn->dbName = strdup(conn->pguser); + } + + /* + * Supply default password if none given + */ + if (conn->pgpass == NULL || conn->pgpass[0] == '\0') + { + if (conn->pgpass) + free(conn->pgpass); + conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, + conn->dbName, conn->pguser); + if (conn->pgpass == NULL) + conn->pgpass = strdup(DefaultPassword); + else + conn->dot_pgpass_used = true; + } + + /* + * Allow unix socket specification in the host name + */ + if (conn->pghost && is_absolute_path(conn->pghost)) + { + if (conn->pgunixsocket) + free(conn->pgunixsocket); + conn->pgunixsocket = conn->pghost; + conn->pghost = NULL; + } + + /* + * validate sslmode option + */ + if (conn->sslmode) + { + if (strcmp(conn->sslmode, "disable") != 0 + && strcmp(conn->sslmode, "allow") != 0 + && strcmp(conn->sslmode, "prefer") != 0 + && strcmp(conn->sslmode, "require") != 0 + && strcmp(conn->sslmode, "verify-ca") != 0 + && strcmp(conn->sslmode, "verify-full") != 0) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid sslmode value: \"%s\"\n"), + conn->sslmode); + return false; + } + +#ifndef USE_SSL + switch (conn->sslmode[0]) + { + case 'a': /* "allow" */ + case 'p': /* "prefer" */ + + /* + * warn user that an SSL connection will never be negotiated + * since SSL was not compiled in? + */ + break; + + case 'r': /* "require" */ + case 'v': /* "verify-ca" or "verify-full" */ + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"), + conn->sslmode); + return false; + } +#endif + } + else + conn->sslmode = strdup(DefaultSSLMode); + + /* + * Resolve special "auto" client_encoding from the locale + */ + if (conn->client_encoding_initial && + strcmp(conn->client_encoding_initial, "auto") == 0) + { + free(conn->client_encoding_initial); + conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))); + } + + /* + * Only if we get this far is it appropriate to try to connect. (We need a + * state flag, rather than just the boolean result of this function, in + * case someone tries to PQreset() the PGconn.) + */ + conn->options_valid = true; + + return true; +} + +/* + * PQconndefaults + * + * Parse an empty string like PQconnectdb() would do and return the + * resulting connection options array, ie, all the default values that are + * available from the environment etc. On error (eg out of memory), + * NULL is returned. + * + * 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); + if (PQExpBufferBroken(&errorBuf)) + return NULL; /* out of memory already :-( */ + connOptions = conninfo_parse("", &errorBuf, true); + 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 only the errorMessage is likely to be useful. + * ---------------- + */ +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; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * If the dbName parameter contains '=', assume it's a conninfo string. + */ + if (dbName && strchr(dbName, '=')) + { + if (!connectOptions1(conn, dbName)) + return conn; + } + else + { + /* + * Old-style path: first, parse an empty conninfo string in order to + * set up the same defaults that PQconnectdb() would use. + */ + if (!connectOptions1(conn, "")) + return conn; + + /* Insert dbName parameter value into struct */ + if (dbName && dbName[0] != '\0') + { + if (conn->dbName) + free(conn->dbName); + conn->dbName = strdup(dbName); + } + } + + /* + * Insert remaining parameters into struct, overriding defaults (as well + * as any conflicting data from dbName taken as a conninfo). + */ + if (pghost && pghost[0] != '\0') + { + if (conn->pghost) + free(conn->pghost); + conn->pghost = strdup(pghost); + } + + if (pgport && pgport[0] != '\0') + { + if (conn->pgport) + free(conn->pgport); + conn->pgport = strdup(pgport); + } + + if (pgoptions && pgoptions[0] != '\0') + { + if (conn->pgoptions) + free(conn->pgoptions); + conn->pgoptions = strdup(pgoptions); + } + + if (pgtty && pgtty[0] != '\0') + { + if (conn->pgtty) + free(conn->pgtty); + conn->pgtty = strdup(pgtty); + } + + if (login && login[0] != '\0') + { + if (conn->pguser) + free(conn->pguser); + conn->pguser = strdup(login); + } + + if (pwd && pwd[0] != '\0') + { + if (conn->pgpass) + free(conn->pgpass); + conn->pgpass = strdup(pwd); + } + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (connectDBStart(conn)) + (void) connectDBComplete(conn); + + return conn; +} + + +/* ---------- + * connectNoDelay - + * Sets the TCP_NODELAY socket option. + * Returns 1 if successful, 0 if not. + * ---------- + */ +static int +connectNoDelay(PGconn *conn) +{ +#ifdef TCP_NODELAY + int on = 1; + + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, + sizeof(on)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to TCP no delay mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} + + +/* ---------- + * connectFailureMessage - + * create a friendly error message on connection failure. + * ---------- + */ +static void +connectFailureMessage(PGconn *conn, int errorno) +{ + char sebuf[256]; + +#ifdef HAVE_UNIX_SOCKETS + if (IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + char service[NI_MAXHOST]; + + pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen, + NULL, 0, + service, sizeof(service), + NI_NUMERICSERV); + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running locally and accepting\n" + "\tconnections on Unix domain socket \"%s\"?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + service); + } + else +#endif /* HAVE_UNIX_SOCKETS */ + { + char host_addr[NI_MAXHOST]; + const char *displayed_host; + struct sockaddr_storage *addr = &conn->raddr.addr; + + /* + * Optionally display the network address with the hostname. This is + * useful to distinguish between IPv4 and IPv6 connections. + */ + if (conn->pghostaddr != NULL) + strlcpy(host_addr, conn->pghostaddr, NI_MAXHOST); + else if (addr->ss_family == AF_INET) + { + if (inet_net_ntop(AF_INET, + &((struct sockaddr_in *) addr)->sin_addr.s_addr, + 32, + host_addr, sizeof(host_addr)) == NULL) + strcpy(host_addr, "???"); + } +#ifdef HAVE_IPV6 + else if (addr->ss_family == AF_INET6) + { + if (inet_net_ntop(AF_INET6, + &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr, + 128, + host_addr, sizeof(host_addr)) == NULL) + strcpy(host_addr, "???"); + } +#endif + else + strcpy(host_addr, "???"); + + if (conn->pghostaddr && conn->pghostaddr[0] != '\0') + displayed_host = conn->pghostaddr; + else if (conn->pghost && conn->pghost[0] != '\0') + displayed_host = conn->pghost; + else + displayed_host = DefaultHost; + + /* + * If the user did not supply an IP address using 'hostaddr', and + * 'host' was missing or does not match our lookup, display the + * looked-up IP address. + */ + if ((conn->pghostaddr == NULL) && + (conn->pghost == NULL || strcmp(conn->pghost, host_addr) != 0)) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running on host \"%s\" (%s) and accepting\n" + "\tTCP/IP connections on port %s?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + displayed_host, + host_addr, + conn->pgport); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running on host \"%s\" and accepting\n" + "\tTCP/IP connections on port %s?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + displayed_host, + conn->pgport); + } +} + +/* + * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if + * conn->keepalives is set to a value which is not parseable as an + * integer. + */ +static int +useKeepalives(PGconn *conn) +{ + char *ep; + int val; + + if (conn->keepalives == NULL) + return 1; + val = strtol(conn->keepalives, &ep, 10); + if (*ep) + return -1; + return val != 0 ? 1 : 0; +} + +#ifndef WIN32 +/* + * Set the keepalive idle timer. + */ +static int +setKeepalivesIdle(PGconn *conn) +{ + int idle; + + if (conn->keepalives_idle == NULL) + return 1; + + idle = atoi(conn->keepalives_idle); + if (idle < 0) + idle = 0; + +#ifdef TCP_KEEPIDLE + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPIDLE, + (char *) &idle, sizeof(idle)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(TCP_KEEPIDLE) failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#else +#ifdef TCP_KEEPALIVE + /* Darwin uses TCP_KEEPALIVE rather than TCP_KEEPIDLE */ + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPALIVE, + (char *) &idle, sizeof(idle)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(TCP_KEEPALIVE) failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif +#endif + + return 1; +} + +/* + * Set the keepalive interval. + */ +static int +setKeepalivesInterval(PGconn *conn) +{ + int interval; + + if (conn->keepalives_interval == NULL) + return 1; + + interval = atoi(conn->keepalives_interval); + if (interval < 0) + interval = 0; + +#ifdef TCP_KEEPINTVL + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL, + (char *) &interval, sizeof(interval)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(TCP_KEEPINTVL) failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} + +/* + * Set the count of lost keepalive packets that will trigger a connection + * break. + */ +static int +setKeepalivesCount(PGconn *conn) +{ + int count; + + if (conn->keepalives_count == NULL) + return 1; + + count = atoi(conn->keepalives_count); + if (count < 0) + count = 0; + +#ifdef TCP_KEEPCNT + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT, + (char *) &count, sizeof(count)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(TCP_KEEPCNT) failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} +#else /* Win32 */ +#ifdef SIO_KEEPALIVE_VALS +/* + * Enable keepalives and set the keepalive values on Win32, + * where they are always set in one batch. + */ +static int +setKeepalivesWin32(PGconn *conn) +{ + struct tcp_keepalive ka; + DWORD retsize; + int idle = 0; + int interval = 0; + + if (conn->keepalives_idle) + idle = atoi(conn->keepalives_idle); + if (idle <= 0) + idle = 2 * 60 * 60; /* 2 hours = default */ + + if (conn->keepalives_interval) + interval = atoi(conn->keepalives_interval); + if (interval <= 0) + interval = 1; /* 1 second = default */ + + ka.onoff = 1; + ka.keepalivetime = idle * 1000; + ka.keepaliveinterval = interval * 1000; + + if (WSAIoctl(conn->sock, + SIO_KEEPALIVE_VALS, + (LPVOID) &ka, + sizeof(ka), + NULL, + 0, + &retsize, + NULL, + NULL) + != 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"), + WSAGetLastError()); + return 0; + } + return 1; +} +#endif /* SIO_KEEPALIVE_VALS */ +#endif /* WIN32 */ + +/* ---------- + * connectDBStart - + * Begin the process of making a connection to the backend. + * + * Returns 1 if successful, 0 if not. + * ---------- + */ +static int +connectDBStart(PGconn *conn) +{ + int portnum; + char portstr[128]; + struct addrinfo *addrs = NULL; + struct addrinfo hint; + const char *node; + int ret; + + if (!conn) + return 0; + + if (!conn->options_valid) + goto connect_errReturn; + + /* Ensure our buffers are empty */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + + /* + * Determine the parameters to pass to pg_getaddrinfo_all. + */ + + /* Initialize hint structure */ + MemSet(&hint, 0, sizeof(hint)); + hint.ai_socktype = SOCK_STREAM; + hint.ai_family = AF_UNSPEC; + + /* Set up port number as a string */ + if (conn->pgport != NULL && conn->pgport[0] != '\0') + { + portnum = atoi(conn->pgport); + if (portnum < 1 || portnum > 65535) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid port number: \"%s\"\n"), + conn->pgport); + conn->options_valid = false; + goto connect_errReturn; + } + } + else + portnum = DEF_PGPORT; + snprintf(portstr, sizeof(portstr), "%d", portnum); + + if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0') + { + /* Using pghostaddr avoids a hostname lookup */ + node = conn->pghostaddr; + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + } + else if (conn->pghost != NULL && conn->pghost[0] != '\0') + { + /* Using pghost, so we have to look-up the hostname */ + node = conn->pghost; + hint.ai_family = AF_UNSPEC; + } + else + { +#ifdef HAVE_UNIX_SOCKETS + /* pghostaddr and pghost are NULL, so use Unix domain socket */ + node = NULL; + hint.ai_family = AF_UNIX; + UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket); +#else + /* Without Unix sockets, default to localhost instead */ + node = DefaultHost; + hint.ai_family = AF_UNSPEC; +#endif /* HAVE_UNIX_SOCKETS */ + } + + /* Use pg_getaddrinfo_all() to resolve the address */ + ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs); + if (ret || !addrs) + { + if (node) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not translate host name \"%s\" to address: %s\n"), + node, gai_strerror(ret)); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"), + portstr, gai_strerror(ret)); + if (addrs) + pg_freeaddrinfo_all(hint.ai_family, addrs); + conn->options_valid = false; + goto connect_errReturn; + } + +#ifdef USE_SSL + /* setup values based on SSL mode */ + if (conn->sslmode[0] == 'd') /* "disable" */ + conn->allow_ssl_try = false; + else if (conn->sslmode[0] == 'a') /* "allow" */ + conn->wait_ssl_try = true; +#endif + + /* + * Set up to try to connect, with protocol 3.0 as the first attempt. + */ + conn->addrlist = addrs; + conn->addr_cur = addrs; + conn->addrlist_family = hint.ai_family; + conn->pversion = PG_PROTOCOL(3, 0); + conn->send_appname = true; + conn->status = CONNECTION_NEEDED; + + /* + * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(), + * so that it can easily be re-executed if needed again during the + * asynchronous startup process. However, we must run it once here, + * because callers expect a success return from this routine to mean that + * we are in PGRES_POLLING_WRITING connection state. + */ + if (PQconnectPoll(conn) == PGRES_POLLING_WRITING) + return 1; + +connect_errReturn: + if (conn->sock >= 0) + { + pqsecure_close(conn); + closesocket(conn->sock); + 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; + time_t finish_time = ((time_t) -1); + + if (conn == NULL || conn->status == CONNECTION_BAD) + return 0; + + /* + * Set up a time limit, if connect_timeout isn't zero. + */ + if (conn->connect_timeout != NULL) + { + int timeout = atoi(conn->connect_timeout); + + if (timeout > 0) + { + /* + * Rounding could cause connection to fail; need at least 2 secs + */ + if (timeout < 2) + timeout = 2; + /* calculate the finish time based on start + timeout */ + finish_time = time(NULL) + timeout; + } + } + + 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_OK: + + /* + * Reset stored error messages since we now have a working + * connection + */ + resetPQExpBuffer(&conn->errorMessage); + return 1; /* success! */ + + case PGRES_POLLING_READING: + if (pqWaitTimed(1, 0, conn, finish_time)) + { + conn->status = CONNECTION_BAD; + return 0; + } + break; + + case PGRES_POLLING_WRITING: + if (pqWaitTimed(0, 1, conn, finish_time)) + { + 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 + * has arrived.. + * + * 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 PQconnectStart 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. + * + * ---------------- + */ +PostgresPollingStatusType +PQconnectPoll(PGconn *conn) +{ + PGresult *res; + char sebuf[256]; + int optval; + + 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; + + /* We allow pqSetenvPoll to decide whether to proceed. */ + case CONNECTION_SETENV: + break; + + /* Special cases: proceed without waiting. */ + case CONNECTION_SSL_STARTUP: + case CONNECTION_NEEDED: + break; + + default: + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "invalid 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 do. */ + switch (conn->status) + { + case CONNECTION_NEEDED: + { + /* + * Try to initiate a connection to one of the addresses + * returned by pg_getaddrinfo_all(). conn->addr_cur is the + * next one to try. We fail when we run out of addresses. + */ + while (conn->addr_cur != NULL) + { + struct addrinfo *addr_cur = conn->addr_cur; + + /* Remember current address for possible error msg */ + memcpy(&conn->raddr.addr, addr_cur->ai_addr, + addr_cur->ai_addrlen); + conn->raddr.salen = addr_cur->ai_addrlen; + + /* Open a socket */ + conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); + if (conn->sock < 0) + { + /* + * ignore socket() failure if we have more addresses + * to try + */ + if (addr_cur->ai_next != NULL) + { + conn->addr_cur = addr_cur->ai_next; + continue; + } + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create socket: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + break; + } + + /* + * Select socket options: no delay of outgoing data for + * TCP sockets, nonblock mode, close-on-exec. Fail if any + * of this fails. + */ + if (!IS_AF_UNIX(addr_cur->ai_family)) + { + if (!connectNoDelay(conn)) + { + closesocket(conn->sock); + conn->sock = -1; + conn->addr_cur = addr_cur->ai_next; + continue; + } + } + if (!pg_set_noblock(conn->sock)) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to non-blocking mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + closesocket(conn->sock); + conn->sock = -1; + conn->addr_cur = addr_cur->ai_next; + continue; + } + +#ifdef F_SETFD + if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to close-on-exec mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + closesocket(conn->sock); + conn->sock = -1; + conn->addr_cur = addr_cur->ai_next; + continue; + } +#endif /* F_SETFD */ + + if (!IS_AF_UNIX(addr_cur->ai_family)) + { +#ifndef WIN32 + int on = 1; +#endif + int usekeepalives = useKeepalives(conn); + int err = 0; + + if (usekeepalives < 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("keepalives parameter must be an integer\n")); + err = 1; + } + else if (usekeepalives == 0) + { + /* Do nothing */ + } +#ifndef WIN32 + else if (setsockopt(conn->sock, + SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(SO_KEEPALIVE) failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + err = 1; + } + else if (!setKeepalivesIdle(conn) + || !setKeepalivesInterval(conn) + || !setKeepalivesCount(conn)) + err = 1; +#else /* WIN32 */ +#ifdef SIO_KEEPALIVE_VALS + else if (!setKeepalivesWin32(conn)) + err = 1; +#endif /* SIO_KEEPALIVE_VALS */ +#endif /* WIN32 */ + + if (err) + { + closesocket(conn->sock); + conn->sock = -1; + conn->addr_cur = addr_cur->ai_next; + continue; + } + } + + /*---------- + * We have three methods of blocking SIGPIPE during + * send() calls to this socket: + * + * - setsockopt(sock, SO_NOSIGPIPE) + * - send(sock, ..., MSG_NOSIGNAL) + * - setting the signal mask to SIG_IGN during send() + * + * The third method requires three syscalls per send, + * so we prefer either of the first two, but they are + * less portable. The state is tracked in the following + * members of PGconn: + * + * conn->sigpipe_so - we have set up SO_NOSIGPIPE + * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL + * + * If we can use SO_NOSIGPIPE, then set sigpipe_so here + * and we're done. Otherwise, set sigpipe_flag so that + * we will try MSG_NOSIGNAL on sends. If we get an error + * with MSG_NOSIGNAL, we'll clear that flag and revert to + * signal masking. + *---------- + */ + conn->sigpipe_so = false; +#ifdef MSG_NOSIGNAL + conn->sigpipe_flag = true; +#else + conn->sigpipe_flag = false; +#endif /* MSG_NOSIGNAL */ + +#ifdef SO_NOSIGPIPE + optval = 1; + if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, + (char *) &optval, sizeof(optval)) == 0) + { + conn->sigpipe_so = true; + conn->sigpipe_flag = false; + } +#endif /* SO_NOSIGPIPE */ + + /* + * Start/make connection. This should not block, since we + * are in nonblock mode. If it does, well, too bad. + */ + if (connect(conn->sock, addr_cur->ai_addr, + addr_cur->ai_addrlen) < 0) + { + if (SOCK_ERRNO == EINPROGRESS || + SOCK_ERRNO == EWOULDBLOCK || + SOCK_ERRNO == EINTR || + SOCK_ERRNO == 0) + { + /* + * This is fine - we're in non-blocking mode, and + * the connection is in progress. Tell caller to + * wait for write-ready on socket. + */ + conn->status = CONNECTION_STARTED; + return PGRES_POLLING_WRITING; + } + /* otherwise, trouble */ + } + else + { + /* + * Hm, we're connected already --- seems the "nonblock + * connection" wasn't. Advance the state machine and + * go do the next stuff. + */ + conn->status = CONNECTION_STARTED; + goto keep_going; + } + + /* + * This connection failed --- set up error report, then + * close socket (do it this way in case close() affects + * the value of errno...). We will ignore the connect() + * failure and keep going if there are more addresses. + */ + connectFailureMessage(conn, SOCK_ERRNO); + if (conn->sock >= 0) + { + closesocket(conn->sock); + conn->sock = -1; + } + + /* + * Try the next address, if any. + */ + conn->addr_cur = addr_cur->ai_next; + } /* loop over addresses */ + + /* + * Ooops, no more addresses. An appropriate error message is + * already set up, so just set the right status. + */ + goto error_return; + } + + case CONNECTION_STARTED: + { + ACCEPT_TYPE_ARG3 optlen = sizeof(optval); + + /* + * Write ready, since we've made it here, so the connection + * has been made ... or has failed. + */ + + /* + * 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) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get socket error status: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + 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. + */ + connectFailureMessage(conn, optval); + + /* + * If more addresses remain, keep trying, just as in the + * case where connect() returned failure immediately. + */ + if (conn->addr_cur->ai_next != NULL) + { + if (conn->sock >= 0) + { + closesocket(conn->sock); + conn->sock = -1; + } + conn->addr_cur = conn->addr_cur->ai_next; + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + goto error_return; + } + + /* Fill in the client address */ + conn->laddr.salen = sizeof(conn->laddr.addr); + if (getsockname(conn->sock, + (struct sockaddr *) & conn->laddr.addr, + &conn->laddr.salen) < 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get client address from socket: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + goto error_return; + } + + /* + * Make sure we can write before advancing to next step. + */ + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + + case CONNECTION_MADE: + { + char *startpacket; + int packetlen; + +#ifdef HAVE_UNIX_SOCKETS + + /* + * Implement requirepeer check, if requested and it's a + * Unix-domain socket. + */ + if (conn->requirepeer && conn->requirepeer[0] && + IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + char pwdbuf[BUFSIZ]; + struct passwd pass_buf; + struct passwd *pass; + uid_t uid; + gid_t gid; + + errno = 0; + if (getpeereid(conn->sock, &uid, &gid) != 0) + { + /* + * Provide special error message if getpeereid is a + * stub + */ + if (errno == ENOSYS) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("requirepeer parameter is not supported on this platform\n")); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get peer credentials: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + goto error_return; + } + + pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass); + + if (pass == NULL) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("local user with ID %d does not exist\n"), + (int) uid); + goto error_return; + } + + if (strcmp(pass->pw_name, conn->requirepeer) != 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"), + conn->requirepeer, pass->pw_name); + goto error_return; + } + } +#endif /* HAVE_UNIX_SOCKETS */ + +#ifdef USE_SSL + + /* + * If SSL is enabled and we haven't already got it running, + * request it instead of sending the startup message. + */ + if (IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + /* Don't bother requesting SSL over a Unix socket */ + conn->allow_ssl_try = false; + } + if (conn->allow_ssl_try && !conn->wait_ssl_try && + conn->ssl == NULL) + { + ProtocolVersion pv; + + /* + * Send the SSL request packet. + * + * Theoretically, this could block, but it really + * shouldn't since we only got here if the socket is + * write-ready. + */ + pv = htonl(NEGOTIATE_SSL_CODE); + if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send SSL negotiation packet: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + goto error_return; + } + /* Ok, wait for response */ + conn->status = CONNECTION_SSL_STARTUP; + return PGRES_POLLING_READING; + } +#endif /* USE_SSL */ + + /* + * Build the startup packet. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + startpacket = pqBuildStartupPacket3(conn, &packetlen, + EnvironmentOptions); + else + startpacket = pqBuildStartupPacket2(conn, &packetlen, + EnvironmentOptions); + if (!startpacket) + { + /* + * will not appendbuffer here, since it's likely to also + * run out of memory + */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + goto error_return; + } + + /* + * 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, 0, startpacket, packetlen) != STATUS_OK) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send startup packet: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + free(startpacket); + goto error_return; + } + + free(startpacket); + + conn->status = CONNECTION_AWAITING_RESPONSE; + return PGRES_POLLING_READING; + } + + /* + * Handle SSL negotiation: wait for postmaster messages and + * respond as necessary. + */ + case CONNECTION_SSL_STARTUP: + { +#ifdef USE_SSL + PostgresPollingStatusType pollres; + + /* + * On first time through, get the postmaster's response to our + * SSL negotiation packet. + */ + if (conn->ssl == NULL) + { + /* + * We use pqReadData here since it has the logic to + * distinguish no-data-yet from connection closure. Since + * conn->ssl isn't set, a plain recv() will occur. + */ + char SSLok; + int rdresult; + + rdresult = pqReadData(conn); + if (rdresult < 0) + { + /* errorMessage is already filled in */ + goto error_return; + } + if (rdresult == 0) + { + /* caller failed to wait for data */ + return PGRES_POLLING_READING; + } + if (pqGetc(&SSLok, conn) < 0) + { + /* should not happen really */ + return PGRES_POLLING_READING; + } + if (SSLok == 'S') + { + /* mark byte consumed */ + conn->inStart = conn->inCursor; + /* Set up global SSL state if required */ + if (pqsecure_initialize(conn) != 0) + goto error_return; + } + else if (SSLok == 'N') + { + /* mark byte consumed */ + conn->inStart = conn->inCursor; + /* OK to do without SSL? */ + if (conn->sslmode[0] == 'r' || /* "require" */ + conn->sslmode[0] == 'v') /* "verify-ca" or + * "verify-full" */ + { + /* Require SSL, but server does not want it */ + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("server does not support SSL, but SSL was required\n")); + goto error_return; + } + /* Otherwise, proceed with normal startup */ + conn->allow_ssl_try = false; + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + else if (SSLok == 'E') + { + /* + * Server failure of some sort, such as failure to + * fork a backend process. We need to process and + * report the error message, which might be formatted + * according to either protocol 2 or protocol 3. + * Rather than duplicate the code for that, we flip + * into AWAITING_RESPONSE state and let the code there + * deal with it. Note we have *not* consumed the "E" + * byte here. + */ + conn->status = CONNECTION_AWAITING_RESPONSE; + goto keep_going; + } + else + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("received invalid response to SSL negotiation: %c\n"), + SSLok); + goto error_return; + } + } + + /* + * Begin or continue the SSL negotiation process. + */ + pollres = pqsecure_open_client(conn); + if (pollres == PGRES_POLLING_OK) + { + /* SSL handshake done, ready to send startup packet */ + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + if (pollres == PGRES_POLLING_FAILED) + { + /* + * Failed ... if sslmode is "prefer" then do a non-SSL + * retry + */ + if (conn->sslmode[0] == 'p' /* "prefer" */ + && conn->allow_ssl_try /* redundant? */ + && !conn->wait_ssl_try) /* redundant? */ + { + /* only retry once */ + conn->allow_ssl_try = false; + /* Must drop the old connection */ + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_NEEDED; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + goto keep_going; + } + } + return pollres; +#else /* !USE_SSL */ + /* can't get here */ + goto error_return; +#endif /* USE_SSL */ + } + + /* + * Handle authentication exchange: wait for postmaster messages + * and respond as necessary. + */ + case CONNECTION_AWAITING_RESPONSE: + { + char beresp; + int msgLength; + int avail; + 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; + + /* Read type byte */ + if (pqGetc(&beresp, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + + /* + * Validate message type: we expect only an authentication + * request or an error here. Anything else probably means + * it's not Postgres on the other end at all. + */ + if (!(beresp == 'R' || beresp == 'E')) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "expected authentication request from " + "server, but received %c\n"), + beresp); + goto error_return; + } + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* Read message length word */ + if (pqGetInt(&msgLength, 4, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + else + { + /* Set phony message length to disable checks below */ + msgLength = 8; + } + + /* + * Try to validate message length before using it. + * Authentication requests can't be very large, although GSS + * auth requests may not be that small. Errors can be a + * little larger, but not huge. If we see a large apparent + * length in an error, it means we're really talking to a + * pre-3.0-protocol server; cope. + */ + if (beresp == 'R' && (msgLength < 8 || msgLength > 2000)) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "expected authentication request from " + "server, but received %c\n"), + beresp); + goto error_return; + } + + if (beresp == 'E' && (msgLength < 8 || msgLength > 30000)) + { + /* Handle error from a pre-3.0 server */ + conn->inCursor = conn->inStart + 1; /* reread data */ + if (pqGets_append(&conn->errorMessage, conn)) + { + /* We'll come back when there is 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'); + + /* + * If we tried to open the connection in 3.0 protocol, + * fall back to 2.0 protocol. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + conn->pversion = PG_PROTOCOL(2, 0); + /* Must drop the old connection */ + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_NEEDED; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + goto keep_going; + } + + goto error_return; + } + + /* + * Can't process if message body isn't all here yet. + * + * (In protocol 2.0 case, we are assuming messages carry at + * least 4 bytes of data.) + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before returning, try to enlarge the input buffer if + * needed to hold the whole message; see notes in + * pqParseInput3. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + goto error_return; + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + + /* Handle errors. */ + if (beresp == 'E') + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (pqGetErrorNotice3(conn, true)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + else + { + if (pqGets_append(&conn->errorMessage, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + /* OK, we read the message; mark data consumed */ + conn->inStart = conn->inCursor; + +#ifdef USE_SSL + + /* + * if sslmode is "allow" and we haven't tried an SSL + * connection already, then retry with an SSL connection + */ + if (conn->sslmode[0] == 'a' /* "allow" */ + && conn->ssl == NULL + && conn->allow_ssl_try + && conn->wait_ssl_try) + { + /* only retry once */ + conn->wait_ssl_try = false; + /* Must drop the old connection */ + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_NEEDED; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + goto keep_going; + } + + /* + * if sslmode is "prefer" and we're in an SSL connection, + * then do a non-SSL retry + */ + if (conn->sslmode[0] == 'p' /* "prefer" */ + && conn->allow_ssl_try + && !conn->wait_ssl_try) /* redundant? */ + { + /* only retry once */ + conn->allow_ssl_try = false; + /* Must drop the old connection */ + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_NEEDED; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + goto keep_going; + } +#endif + + goto error_return; + } + + /* It is an authentication request. */ + conn->auth_req_received = true; + + /* 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_MD5) + { + if (pqGetnchar(conn->md5Salt, + sizeof(conn->md5Salt), conn)) + { + /* We'll come back when there are more data */ + return PGRES_POLLING_READING; + } + } +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + + /* + * Continue GSSAPI/SSPI authentication + */ + if (areq == AUTH_REQ_GSS_CONT) + { + int llen = msgLength - 4; + + /* + * We can be called repeatedly for the same buffer. Avoid + * re-allocating the buffer in this case - just re-use the + * old buffer. + */ + if (llen != conn->ginbuf.length) + { + if (conn->ginbuf.value) + free(conn->ginbuf.value); + + conn->ginbuf.length = llen; + conn->ginbuf.value = malloc(llen); + if (!conn->ginbuf.value) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory allocating GSSAPI buffer (%i)"), + llen); + goto error_return; + } + } + + if (pqGetnchar(conn->ginbuf.value, llen, conn)) + { + /* We'll come back when there is more data. */ + return PGRES_POLLING_READING; + } + } +#endif + + /* + * 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. + */ + + if (pg_fe_sendauth(areq, conn) != 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 pg_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 are ParameterStatus and BackendKeyData + * messages.) 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) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("unexpected message from server during startup\n")); + else if (conn->send_appname && + (conn->appname || conn->fbappname)) + { + /* + * If we tried to send application_name, check to see + * if the error is about that --- pre-9.0 servers will + * reject it at this stage of the process. If so, + * close the connection and retry without sending + * application_name. We could possibly get a false + * SQLSTATE match here and retry uselessly, but there + * seems no great harm in that; we'll just get the + * same error again if it's unrelated. + */ + const char *sqlstate; + + sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (sqlstate && + strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0) + { + PQclear(res); + conn->send_appname = false; + /* Must drop the old connection */ + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_NEEDED; + /* Discard any unread/unsent data */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + goto keep_going; + } + } + + /* + * 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; + } + + /* We can release the address list now. */ + pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); + conn->addrlist = NULL; + conn->addr_cur = NULL; + + /* Fire up post-connection housekeeping if needed */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + conn->status = CONNECTION_SETENV; + conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND; + conn->next_eo = EnvironmentOptions; + return PGRES_POLLING_WRITING; + } + + /* Otherwise, we are open for business! */ + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; + } + + case CONNECTION_SETENV: + + /* + * Do post-connection housekeeping (only needed in protocol 2.0). + * + * 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 */ + break; + + 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: + goto error_return; + } + + /* We are open for business! */ + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; + + default: + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid connection state %d, " + "probably indicative of memory corruption\n"), + conn->status); + goto error_return; + } + + /* Unreachable */ + +error_return: + + dot_pg_pass_warning(conn); + + /* + * 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). + */ + conn->status = CONNECTION_BAD; + return PGRES_POLLING_FAILED; +} + + +/* + * internal_ping + * Determine if a server is running and if we can connect to it. + * + * The argument is a connection that's been started, but not completed. + */ +static PGPing +internal_ping(PGconn *conn) +{ + /* Say "no attempt" if we never got to PQconnectPoll */ + if (!conn || !conn->options_valid) + return PQPING_NO_ATTEMPT; + + /* Attempt to complete the connection */ + if (conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + /* Definitely OK if we succeeded */ + if (conn->status != CONNECTION_BAD) + return PQPING_OK; + + /* + * Here begins the interesting part of "ping": determine the cause of the + * failure in sufficient detail to decide what to return. We do not want + * to report that the server is not up just because we didn't have a valid + * password, for example. In fact, any sort of authentication request + * implies the server is up. (We need this check since the libpq side of + * things might have pulled the plug on the connection before getting an + * error as such from the postmaster.) + */ + if (conn->auth_req_received) + return PQPING_OK; + + /* + * If we failed to get any ERROR response from the postmaster, report + * PQPING_NO_RESPONSE. This result could be somewhat misleading for a + * pre-7.4 server, since it won't send back a SQLSTATE, but those are long + * out of support. Another corner case where the server could return a + * failure without a SQLSTATE is fork failure, but NO_RESPONSE isn't + * totally unreasonable for that anyway. We expect that every other + * failure case in a modern server will produce a report with a SQLSTATE. + * + * NOTE: whenever we get around to making libpq generate SQLSTATEs for + * client-side errors, we should either not store those into + * last_sqlstate, or add an extra flag so we can tell client-side errors + * apart from server-side ones. + */ + if (strlen(conn->last_sqlstate) != 5) + return PQPING_NO_RESPONSE; + + /* + * Report PQPING_REJECT if server says it's not accepting connections. (We + * distinguish this case mainly for the convenience of pg_ctl.) + */ + if (strcmp(conn->last_sqlstate, ERRCODE_CANNOT_CONNECT_NOW) == 0) + return PQPING_REJECT; + + /* + * Any other SQLSTATE can be taken to indicate that the server is up. + * Presumably it didn't like our username, password, or database name; or + * perhaps it had some transient failure, but that should not be taken as + * meaning "it's down". + */ + return PQPING_OK; +} + + +/* + * makeEmptyPGconn + * - create a PGconn data structure with (as yet) no interesting data + */ +static PGconn * +makeEmptyPGconn(void) +{ + PGconn *conn; + +#ifdef WIN32 + + /* + * Make sure socket support is up and running. + */ + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(1, 1), &wsaData)) + return NULL; + WSASetLastError(0); +#endif + + conn = (PGconn *) malloc(sizeof(PGconn)); + if (conn == NULL) + { +#ifdef WIN32 + WSACleanup(); +#endif + return conn; + } + + /* Zero all pointers and booleans */ + MemSet(conn, 0, sizeof(PGconn)); + + conn->noticeHooks.noticeRec = defaultNoticeReceiver; + conn->noticeHooks.noticeProc = defaultNoticeProcessor; + conn->status = CONNECTION_BAD; + conn->asyncStatus = PGASYNC_IDLE; + conn->xactStatus = PQTRANS_IDLE; + conn->options_valid = false; + conn->nonblocking = false; + conn->setenv_state = SETENV_STATE_IDLE; + conn->client_encoding = PG_SQL_ASCII; + conn->std_strings = false; /* unless server says differently */ + conn->verbosity = PQERRORS_DEFAULT; + conn->sock = -1; + conn->auth_req_received = false; + conn->password_needed = false; + conn->dot_pgpass_used = false; +#ifdef USE_SSL + conn->allow_ssl_try = true; + conn->wait_ssl_try = false; +#endif + + /* + * We try to send at least 8K at a time, 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. The output buffer is initially made 16K in size, and we + * try to dump it after accumulating 8K. + * + * 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 = 16 * 1024; + conn->outBuffer = (char *) malloc(conn->outBufSize); + initPQExpBuffer(&conn->errorMessage); + initPQExpBuffer(&conn->workBuffer); + + if (conn->inBuffer == NULL || + conn->outBuffer == NULL || + PQExpBufferBroken(&conn->errorMessage) || + PQExpBufferBroken(&conn->workBuffer)) + { + /* out of memory already :-( */ + freePGconn(conn); + conn = NULL; + } + + return conn; +} + +/* + * freePGconn + * - free an idle (closed) PGconn data structure + * + * NOTE: this should not overlap any functionality with closePGconn(). + * Clearing/resetting of transient state belongs there; what we do here is + * release data that is to be held for the life of the PGconn structure. + * If a value ought to be cleared/freed during PQreset(), do it there not here. + */ +static void +freePGconn(PGconn *conn) +{ + int i; + + /* let any event procs clean up their state data */ + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnDestroy evt; + + evt.conn = conn; + (void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt, + conn->events[i].passThrough); + free(conn->events[i].name); + } + + if (conn->events) + free(conn->events); + 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->connect_timeout) + free(conn->connect_timeout); + if (conn->pgoptions) + free(conn->pgoptions); + if (conn->appname) + free(conn->appname); + if (conn->fbappname) + free(conn->fbappname); + if (conn->dbName) + free(conn->dbName); + if (conn->replication) + free(conn->replication); + if (conn->pguser) + free(conn->pguser); + if (conn->pgpass) + free(conn->pgpass); + if (conn->keepalives) + free(conn->keepalives); + if (conn->keepalives_idle) + free(conn->keepalives_idle); + if (conn->keepalives_interval) + free(conn->keepalives_interval); + if (conn->keepalives_count) + free(conn->keepalives_count); + if (conn->sslmode) + free(conn->sslmode); + if (conn->sslcert) + free(conn->sslcert); + if (conn->sslkey) + free(conn->sslkey); + if (conn->sslrootcert) + free(conn->sslrootcert); + if (conn->sslcrl) + free(conn->sslcrl); + if (conn->requirepeer) + free(conn->requirepeer); +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) + if (conn->krbsrvname) + free(conn->krbsrvname); +#endif +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->gsslib) + free(conn->gsslib); +#endif + /* Note that conn->Pfdebug is not ours to close or free */ + if (conn->last_query) + free(conn->last_query); + if (conn->inBuffer) + free(conn->inBuffer); + if (conn->outBuffer) + free(conn->outBuffer); + termPQExpBuffer(&conn->errorMessage); + termPQExpBuffer(&conn->workBuffer); + + free(conn); + +#ifdef WIN32 + WSACleanup(); +#endif +} + +/* + * closePGconn + * - properly close a connection to the backend + * + * This should reset or release all transient state, but NOT the connection + * parameters. On exit, the PGconn should be in condition to start a fresh + * connection with the same parameters (see PQreset()). + */ +static void +closePGconn(PGconn *conn) +{ + PGnotify *notify; + pgParameterStatus *pstatus; + + /* + * Note that the protocol doesn't allow us to send Terminate messages + * during the startup phase. + */ + if (conn->sock >= 0 && conn->status == CONNECTION_OK) + { + /* + * Try to send "close connection" message to backend. Ignore any + * error. + */ + pqPutMsgStart('X', false, conn); + pqPutMsgEnd(conn); + 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) + { + pqsecure_close(conn); + closesocket(conn->sock); + } + conn->sock = -1; + conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just + * absent */ + conn->asyncStatus = PGASYNC_IDLE; + pqClearAsyncResult(conn); /* deallocate result and curTuple */ + pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); + conn->addrlist = NULL; + conn->addr_cur = NULL; + notify = conn->notifyHead; + while (notify != NULL) + { + PGnotify *prev = notify; + + notify = notify->next; + free(prev); + } + conn->notifyHead = conn->notifyTail = NULL; + pstatus = conn->pstatus; + while (pstatus != NULL) + { + pgParameterStatus *prev = pstatus; + + pstatus = pstatus->next; + free(prev); + } + conn->pstatus = NULL; + if (conn->lobjfuncs) + free(conn->lobjfuncs); + conn->lobjfuncs = NULL; + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; +#ifdef ENABLE_GSS + { + OM_uint32 min_s; + + if (conn->gctx) + gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER); + if (conn->gtarg_nam) + gss_release_name(&min_s, &conn->gtarg_nam); + if (conn->ginbuf.length) + gss_release_buffer(&min_s, &conn->ginbuf); + if (conn->goutbuf.length) + gss_release_buffer(&min_s, &conn->goutbuf); + } +#endif +#ifdef ENABLE_SSPI + if (conn->ginbuf.length) + free(conn->ginbuf.value); + conn->ginbuf.length = 0; + conn->ginbuf.value = NULL; + if (conn->sspitarget) + free(conn->sspitarget); + conn->sspitarget = NULL; + if (conn->sspicred) + { + FreeCredentialsHandle(conn->sspicred); + free(conn->sspicred); + conn->sspicred = NULL; + } + if (conn->sspictx) + { + DeleteSecurityContext(conn->sspictx); + free(conn->sspictx); + conn->sspictx = NULL; + } +#endif +} + +/* + * 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 by closing the + * existing connection and creating a new one. + */ +void +PQreset(PGconn *conn) +{ + if (conn) + { + closePGconn(conn); + + if (connectDBStart(conn) && connectDBComplete(conn)) + { + /* + * Notify event procs of successful reset. We treat an event proc + * failure as disabling the connection ... good idea? + */ + int i; + + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnReset evt; + + evt.conn = conn; + if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, + conn->events[i].passThrough)) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), + conn->events[i].name); + break; + } + } + } + } +} + + +/* + * 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) + { + PostgresPollingStatusType status = PQconnectPoll(conn); + + if (status == PGRES_POLLING_OK) + { + /* + * Notify event procs of successful reset. We treat an event proc + * failure as disabling the connection ... good idea? + */ + int i; + + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnReset evt; + + evt.conn = conn; + if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, + conn->events[i].passThrough)) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), + conn->events[i].name); + return PGRES_POLLING_FAILED; + } + } + } + + return status; + } + + return PGRES_POLLING_FAILED; +} + +/* + * PQcancelGet: get a PGcancel structure corresponding to a connection. + * + * A copy is needed to be able to cancel a running query from a different + * thread. If the same structure is used all structure members would have + * to be individually locked (if the entire structure was locked, it would + * be impossible to cancel a synchronous query because the structure would + * have to stay locked for the duration of the query). + */ +PGcancel * +PQgetCancel(PGconn *conn) +{ + PGcancel *cancel; + + if (!conn) + return NULL; + + if (conn->sock < 0) + return NULL; + + cancel = malloc(sizeof(PGcancel)); + if (cancel == NULL) + return NULL; + + memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr)); + cancel->be_pid = conn->be_pid; + cancel->be_key = conn->be_key; + + return cancel; +} + +/* PQfreeCancel: free a cancel structure */ +void +PQfreeCancel(PGcancel *cancel) +{ + if (cancel) + free(cancel); +} + + +/* + * PQcancel and 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 an error message is available). + * Note: successful dispatch is no guarantee that there will be any effect at + * the backend. The application must read the operation result as usual. + * + * 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. + * We also save/restore errno in case the signal handler support doesn't. + * + * internal_cancel() is an internal helper function to make code-sharing + * between the two versions of the cancel function possible. + */ +static int +internal_cancel(SockAddr *raddr, int be_pid, int be_key, + char *errbuf, int errbufsize) +{ + int save_errno = SOCK_ERRNO; + int tmpsock = -1; + char sebuf[256]; + int maxlen; + struct + { + uint32 packetlen; + CancelRequestPacket cp; + } crp; + + /* + * We need to open a temporary connection to the postmaster. Do this with + * only kernel calls. + */ + if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) < 0) + { + strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize); + goto cancel_errReturn; + } +retry3: + if (connect(tmpsock, (struct sockaddr *) & raddr->addr, + raddr->salen) < 0) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry3; + strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize); + 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(be_pid); + crp.cp.cancelAuthCode = htonl(be_key); + +retry4: + if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry4; + strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize); + goto cancel_errReturn; + } + + /* + * Wait for the postmaster to close the connection, which indicates that + * it's processed the request. Without this delay, we might issue another + * command only to find that our cancel zaps that command instead of the + * one we thought we were canceling. Note we don't actually expect this + * read to obtain any data, we are just waiting for EOF to be signaled. + */ +retry5: + if (recv(tmpsock, (char *) &crp, 1, 0) < 0) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry5; + /* we ignore other error conditions */ + } + + /* All done */ + closesocket(tmpsock); + SOCK_ERRNO_SET(save_errno); + return TRUE; + +cancel_errReturn: + + /* + * Make sure we don't overflow the error buffer. Leave space for the \n at + * the end, and for the terminating zero. + */ + maxlen = errbufsize - strlen(errbuf) - 2; + if (maxlen >= 0) + { + strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), + maxlen); + strcat(errbuf, "\n"); + } + if (tmpsock >= 0) + closesocket(tmpsock); + SOCK_ERRNO_SET(save_errno); + return FALSE; +} + +/* + * PQcancel: request query cancel + * + * Returns TRUE if able to send the cancel request, FALSE if not. + * + * On failure, an error message is stored in *errbuf, which must be of size + * errbufsize (recommended size is 256 bytes). *errbuf is not changed on + * success return. + */ +int +PQcancel(PGcancel *cancel, char *errbuf, int errbufsize) +{ + if (!cancel) + { + strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize); + return FALSE; + } + + return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key, + errbuf, errbufsize); +} + +/* + * PQrequestCancel: old, not thread-safe function for requesting query cancel + * + * Returns TRUE if able to send the cancel request, FALSE if not. + * + * On failure, the error message is saved in conn->errorMessage; this means + * that this can't be used when there might be other active operations on + * the connection object. + * + * NOTE: error messages will be cut off at the current size of the + * error message buffer, since we dare not try to expand conn->errorMessage! + */ +int +PQrequestCancel(PGconn *conn) +{ + int r; + + /* Check we have an open connection */ + if (!conn) + return FALSE; + + if (conn->sock < 0) + { + strlcpy(conn->errorMessage.data, + "PQrequestCancel() -- connection is not open\n", + conn->errorMessage.maxlen); + conn->errorMessage.len = strlen(conn->errorMessage.data); + + return FALSE; + } + + r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key, + conn->errorMessage.data, conn->errorMessage.maxlen); + + if (!r) + conn->errorMessage.len = strlen(conn->errorMessage.data); + + return r; +} + + +/* + * pqPacketSend() -- convenience routine to send a message to server. + * + * pack_type: the single-byte message type code. (Pass zero for startup + * packets, which have no message type code.) + * + * buf, buf_len: contents of message. The given length includes only what + * is in buf; the message type and message length fields are added here. + * + * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. + * SIDE_EFFECTS: may block. + * + * Note: all messages sent with this routine have a length word, whether + * it's protocol 2.0 or 3.0. + */ +int +pqPacketSend(PGconn *conn, char pack_type, + const void *buf, size_t buf_len) +{ + /* Start the message. */ + if (pqPutMsgStart(pack_type, true, conn)) + return STATUS_ERROR; + + /* Send the message body. */ + if (pqPutnchar(buf, buf_len, conn)) + return STATUS_ERROR; + + /* Finish the message. */ + if (pqPutMsgEnd(conn)) + return STATUS_ERROR; + + /* Flush to ensure backend gets it. */ + if (pqFlush(conn)) + return STATUS_ERROR; + + return STATUS_OK; +} + +#ifdef USE_LDAP + +#define LDAP_URL "ldap://" +#define LDAP_DEF_PORT 389 +#define PGLDAP_TIMEOUT 2 + +#define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t') +#define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n') + + +/* + * ldapServiceLookup + * + * Search the LDAP URL passed as first argument, treat the result as a + * string of connection options that are parsed and added to the array of + * options passed as second argument. + * + * LDAP URLs must conform to RFC 1959 without escape sequences. + * ldap://host:port/dn?attributes?scope?filter?extensions + * + * Returns + * 0 if the lookup was successful, + * 1 if the connection to the LDAP server could be established but + * the search was unsuccessful, + * 2 if a connection could not be established, and + * 3 if a fatal error occurred. + * + * An error message is returned in the third argument for return codes 1 and 3. + */ +static int +ldapServiceLookup(const char *purl, PQconninfoOption *options, + PQExpBuffer errorMessage) +{ + int port = LDAP_DEF_PORT, + scope, + rc, + msgid, + size, + state, + oldstate, + i; + bool found_keyword; + char *url, + *hostname, + *portstr, + *endptr, + *dn, + *scopestr, + *filter, + *result, + *p, + *p1 = NULL, + *optname = NULL, + *optval = NULL; + char *attrs[2] = {NULL, NULL}; + LDAP *ld = NULL; + LDAPMessage *res, + *entry; + struct berval **values; + LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0}; + + if ((url = strdup(purl)) == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + return 3; + } + + /* + * Parse URL components, check for correctness. Basically, url has '\0' + * placed at component boundaries and variables are pointed at each + * component. + */ + + if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl); + free(url); + return 3; + } + + /* hostname */ + hostname = url + strlen(LDAP_URL); + if (*hostname == '/') /* no hostname? */ + hostname = DefaultHost; /* the default */ + + /* dn, "distinguished name" */ + p = strchr(url + strlen(LDAP_URL), '/'); + if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": missing distinguished name\n"), purl); + free(url); + return 3; + } + *p = '\0'; /* terminate hostname */ + dn = p + 1; + + /* attribute */ + if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + *p = '\0'; + attrs[0] = p + 1; + + /* scope */ + if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + *p = '\0'; + scopestr = p + 1; + + /* filter */ + if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl); + free(url); + return 3; + } + *p = '\0'; + filter = p + 1; + if ((p = strchr(filter, '?')) != NULL) + *p = '\0'; + + /* port number? */ + if ((p1 = strchr(hostname, ':')) != NULL) + { + long lport; + + *p1 = '\0'; + portstr = p1 + 1; + errno = 0; + lport = strtol(portstr, &endptr, 10); + if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": invalid port number\n"), purl); + free(url); + return 3; + } + port = (int) lport; + } + + /* Allow only one attribute */ + if (strchr(attrs[0], ',') != NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + + /* set scope */ + if (pg_strcasecmp(scopestr, "base") == 0) + scope = LDAP_SCOPE_BASE; + else if (pg_strcasecmp(scopestr, "one") == 0) + scope = LDAP_SCOPE_ONELEVEL; + else if (pg_strcasecmp(scopestr, "sub") == 0) + scope = LDAP_SCOPE_SUBTREE; + else + { + printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + + /* initialize LDAP structure */ + if ((ld = ldap_init(hostname, port)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("could not create LDAP structure\n")); + free(url); + return 3; + } + + /* + * Initialize connection to the server. We do an explicit bind because we + * want to return 2 if the bind fails. + */ + if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1) + { + /* error in ldap_simple_bind() */ + free(url); + ldap_unbind(ld); + return 2; + } + + /* wait some time for the connection to succeed */ + res = NULL; + if ((rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 || + res == NULL) + { + if (res != NULL) + { + /* timeout */ + ldap_msgfree(res); + } + /* error in ldap_result() */ + free(url); + ldap_unbind(ld); + return 2; + } + ldap_msgfree(res); + + /* search */ + res = NULL; + if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res)) + != LDAP_SUCCESS) + { + if (res != NULL) + ldap_msgfree(res); + printfPQExpBuffer(errorMessage, + libpq_gettext("lookup on LDAP server failed: %s\n"), + ldap_err2string(rc)); + ldap_unbind(ld); + free(url); + return 1; + } + + /* complain if there was not exactly one result */ + if ((rc = ldap_count_entries(ld, res)) != 1) + { + printfPQExpBuffer(errorMessage, + rc ? libpq_gettext("more than one entry found on LDAP lookup\n") + : libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get entry */ + if ((entry = ldap_first_entry(ld, res)) == NULL) + { + /* should never happen */ + printfPQExpBuffer(errorMessage, + libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get values */ + if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + ldap_msgfree(res); + free(url); + + if (values[0] == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 1; + } + + /* concatenate values into a single string with newline terminators */ + size = 1; /* for the trailing null */ + for (i = 0; values[i] != NULL; i++) + size += values[i]->bv_len + 1; + if ((result = malloc(size)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 3; + } + p = result; + for (i = 0; values[i] != NULL; i++) + { + memcpy(p, values[i]->bv_val, values[i]->bv_len); + p += values[i]->bv_len; + *(p++) = '\n'; + } + *p = '\0'; + + ldap_value_free_len(values); + ldap_unbind(ld); + + /* parse result string */ + oldstate = state = 0; + for (p = result; *p != '\0'; ++p) + { + switch (state) + { + case 0: /* between entries */ + if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p)) + { + optname = p; + state = 1; + } + break; + case 1: /* in option name */ + if (ld_is_sp_tab(*p)) + { + *p = '\0'; + state = 2; + } + else if (ld_is_nl_cr(*p)) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "missing \"=\" after \"%s\" in connection info string\n"), + optname); + free(result); + return 3; + } + else if (*p == '=') + { + *p = '\0'; + state = 3; + } + break; + case 2: /* after option name */ + if (*p == '=') + { + state = 3; + } + else if (!ld_is_sp_tab(*p)) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "missing \"=\" after \"%s\" in connection info string\n"), + optname); + free(result); + return 3; + } + break; + case 3: /* before option value */ + if (*p == '\'') + { + optval = p + 1; + p1 = p + 1; + state = 5; + } + else if (ld_is_nl_cr(*p)) + { + optval = optname + strlen(optname); /* empty */ + state = 0; + } + else if (!ld_is_sp_tab(*p)) + { + optval = p; + state = 4; + } + break; + case 4: /* in unquoted option value */ + if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p)) + { + *p = '\0'; + state = 0; + } + break; + case 5: /* in quoted option value */ + if (*p == '\'') + { + *p1 = '\0'; + state = 0; + } + else if (*p == '\\') + state = 6; + else + *(p1++) = *p; + break; + case 6: /* in quoted option value after escape */ + *(p1++) = *p; + state = 5; + break; + } + + if (state == 0 && oldstate != 0) + { + found_keyword = false; + for (i = 0; options[i].keyword; i++) + { + if (strcmp(options[i].keyword, optname) == 0) + { + if (options[i].val == NULL) + options[i].val = strdup(optval); + found_keyword = true; + break; + } + } + if (!found_keyword) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + optname); + free(result); + return 1; + } + optname = NULL; + optval = NULL; + } + oldstate = state; + } + + free(result); + + if (state == 5 || state == 6) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "unterminated quoted string in connection info string\n")); + return 3; + } + + return 0; +} +#endif + +#define MAXBUFSIZE 256 + +static int +parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) +{ + char *service = conninfo_getval(options, "service"); + char serviceFile[MAXPGPATH]; + char *env; + bool group_found = false; + int status; + struct stat stat_buf; + + /* + * We have to special-case the environment variable PGSERVICE here, since + * this is and should be called before inserting environment defaults for + * other connection options. + */ + if (service == NULL) + service = getenv("PGSERVICE"); + + if (service == NULL) + return 0; + + if ((env = getenv("PGSERVICEFILE")) != NULL) + strlcpy(serviceFile, env, sizeof(serviceFile)); + else + { + char homedir[MAXPGPATH]; + + if (!pqGetHomeDirectory(homedir, sizeof(homedir))) + { + printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file")); + return 1; + } + snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf"); + errno = 0; + if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) + goto next_file; + } + + status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); + if (group_found || status != 0) + return status; + +next_file: + + /* + * This could be used by any application so we can't use the binary + * location to find our config files. + */ + snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf", + getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); + errno = 0; + if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) + goto last_file; + + status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); + if (status != 0) + return status; + +last_file: + if (!group_found) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("definition of service \"%s\" not found\n"), service); + return 3; + } + + return 0; +} + +static int +parseServiceFile(const char *serviceFile, + const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found) +{ + int linenr = 0, + i; + FILE *f; + char buf[MAXBUFSIZE], + *line; + + f = fopen(serviceFile, "r"); + if (f == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"), + serviceFile); + return 1; + } + + while ((line = fgets(buf, sizeof(buf), f)) != NULL) + { + linenr++; + + if (strlen(line) >= sizeof(buf) - 1) + { + fclose(f); + printfPQExpBuffer(errorMessage, + libpq_gettext("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((unsigned char) 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 = true; + else + *group_found = false; + } + else + { + if (*group_found) + { + /* + * Finally, we are in the right group and can parse the line + */ + char *key, + *val; + bool found_keyword; + +#ifdef USE_LDAP + if (strncmp(line, "ldap", 4) == 0) + { + int rc = ldapServiceLookup(line, options, errorMessage); + + /* if rc = 2, go on reading for fallback */ + switch (rc) + { + case 0: + fclose(f); + return 0; + case 1: + case 3: + fclose(f); + return 3; + case 2: + continue; + } + } +#endif + + key = line; + val = strchr(line, '='); + if (val == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("syntax error in service file \"%s\", line %d\n"), + serviceFile, + linenr); + fclose(f); + return 3; + } + *val++ = '\0'; + + /* + * Set the parameter --- but don't override any previous + * explicit setting. + */ + found_keyword = false; + for (i = 0; options[i].keyword; i++) + { + if (strcmp(options[i].keyword, key) == 0) + { + if (options[i].val == NULL) + options[i].val = strdup(val); + found_keyword = true; + break; + } + } + + if (!found_keyword) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("syntax error in service file \"%s\", line %d\n"), + serviceFile, + linenr); + fclose(f); + return 3; + } + } + } + } + + fclose(f); + + return 0; +} + + +/* + * PQconninfoParse + * + * Parse a string like PQconnectdb() would do and return the + * resulting connection options array. NULL is returned on failure. + * The result contains only options specified directly in the string, + * not any possible default values. + * + * If errmsg isn't NULL, *errmsg is set to NULL on success, or a malloc'd + * string on failure (use PQfreemem to free it). In out-of-memory conditions + * both *errmsg and the result could be NULL. + * + * NOTE: the returned array is dynamically allocated and should + * be freed when no longer needed via PQconninfoFree(). + */ +PQconninfoOption * +PQconninfoParse(const char *conninfo, char **errmsg) +{ + PQExpBufferData errorBuf; + PQconninfoOption *connOptions; + + if (errmsg) + *errmsg = NULL; /* default */ + initPQExpBuffer(&errorBuf); + if (PQExpBufferBroken(&errorBuf)) + return NULL; /* out of memory already :-( */ + connOptions = conninfo_parse(conninfo, &errorBuf, false); + if (connOptions == NULL && errmsg) + *errmsg = errorBuf.data; + else + termPQExpBuffer(&errorBuf); + return connOptions; +} + +/* + * 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. + * Defaults are supplied (from a service file, environment variables, etc) + * for unspecified options, but only if use_defaults is TRUE. + */ +static PQconninfoOption * +conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, + bool use_defaults) +{ + char *pname; + char *pval; + char *buf; + char *tmp; + char *cp; + char *cp2; + PQconninfoOption *options; + PQconninfoOption *option; + + /* Make a working copy of PQconninfoOptions */ + options = malloc(sizeof(PQconninfoOptions)); + if (options == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions)); + + /* Need a modifiable copy of the input string */ + if ((buf = strdup(conninfo)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + cp = buf; + + while (*cp) + { + /* Skip blanks before the parameter name */ + if (isspace((unsigned char) *cp)) + { + cp++; + continue; + } + + /* Get the parameter name */ + pname = cp; + while (*cp) + { + if (*cp == '=') + break; + if (isspace((unsigned char) *cp)) + { + *cp++ = '\0'; + while (*cp) + { + if (!isspace((unsigned char) *cp)) + break; + cp++; + } + break; + } + cp++; + } + + /* Check that there is a following '=' */ + if (*cp != '=') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), + pname); + PQconninfoFree(options); + free(buf); + return NULL; + } + *cp++ = '\0'; + + /* Skip blanks after the '=' */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + break; + cp++; + } + + /* Get the parameter value */ + pval = cp; + + if (*cp != '\'') + { + cp2 = pval; + while (*cp) + { + if (isspace((unsigned char) *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, + libpq_gettext("unterminated quoted string in connection info string\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, + libpq_gettext("invalid connection option \"%s\"\n"), + pname); + PQconninfoFree(options); + free(buf); + return NULL; + } + + /* + * Store the value + */ + if (option->val) + free(option->val); + option->val = strdup(pval); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + free(buf); + return NULL; + } + } + + /* Done with the modifiable input string */ + free(buf); + + /* + * Stop here if caller doesn't want defaults filled in. + */ + if (!use_defaults) + return options; + + /* + * If there's a service spec, use it to obtain any not-explicitly-given + * parameters. + */ + if (parseServiceInfo(options, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + + /* + * Get the fallback resources for parameters not specified in the conninfo + * string nor the service. + */ + for (option = options; option->keyword != NULL; option++) + { + if (option->val != NULL) + continue; /* Value was in conninfo or service */ + + /* + * Try to get the environment variable fallback + */ + if (option->envvar != NULL) + { + if ((tmp = getenv(option->envvar)) != NULL) + { + option->val = strdup(tmp); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + } + + /* + * No environment variable specified or this one isn't set - try + * compiled in + */ + if (option->compiled != NULL) + { + option->val = strdup(option->compiled); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + + /* + * Special handling for user + */ + if (strcmp(option->keyword, "user") == 0) + { + option->val = pg_fe_getauthname(errorMessage); + continue; + } + } + + return options; +} + +/* + * Conninfo array 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. + * Defaults are supplied (from a service file, environment variables, etc) + * for unspecified options, but only if use_defaults is TRUE. + * + * If expand_dbname is non-zero, and the value passed for keyword "dbname" + * contains an "=", assume it is a conninfo string and process it, + * overriding any previously processed conflicting keywords. Subsequent + * keywords will take precedence, however. + */ +static PQconninfoOption * +conninfo_array_parse(const char **keywords, const char **values, + PQExpBuffer errorMessage, bool use_defaults, + int expand_dbname) +{ + char *tmp; + PQconninfoOption *options; + PQconninfoOption *str_options = NULL; + PQconninfoOption *option; + int i = 0; + + /* + * If expand_dbname is non-zero, check keyword "dbname" to see if val is + * actually a conninfo string + */ + while (expand_dbname && keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + /* first find "dbname" if any */ + if (strcmp(pname, "dbname") == 0) + { + /* next look for "=" in the value */ + if (pvalue && strchr(pvalue, '=')) + { + /* + * Must be a conninfo string, so parse it, but do not use + * defaults here -- those get picked up later. We only want to + * override for those parameters actually passed. + */ + str_options = conninfo_parse(pvalue, errorMessage, false); + if (str_options == NULL) + return NULL; + } + break; + } + ++i; + } + + /* Make a working copy of PQconninfoOptions */ + options = malloc(sizeof(PQconninfoOptions)); + if (options == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions)); + + i = 0; + /* Parse the keywords/values arrays */ + while (keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + if (pvalue != NULL) + { + /* Search for the param record */ + for (option = options; option->keyword != NULL; option++) + { + if (strcmp(option->keyword, pname) == 0) + break; + } + + /* Check for invalid connection option */ + if (option->keyword == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + pname); + PQconninfoFree(options); + return NULL; + } + + /* + * If we are on the dbname parameter, and we have a parsed + * conninfo string, copy those parameters across, overriding any + * existing previous settings + */ + if (strcmp(pname, "dbname") == 0 && str_options) + { + PQconninfoOption *str_option; + + for (str_option = str_options; str_option->keyword != NULL; str_option++) + { + if (str_option->val != NULL) + { + int k; + + for (k = 0; options[k].keyword; k++) + { + if (strcmp(options[k].keyword, str_option->keyword) == 0) + { + if (options[k].val) + free(options[k].val); + options[k].val = strdup(str_option->val); + break; + } + } + } + } + } + else + { + /* + * Store the value, overriding previous settings + */ + if (option->val) + free(option->val); + option->val = strdup(pvalue); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + } + } + ++i; + } + PQconninfoFree(str_options); + + /* + * Stop here if caller doesn't want defaults filled in. + */ + if (!use_defaults) + return options; + + /* + * If there's a service spec, use it to obtain any not-explicitly-given + * parameters. + */ + if (parseServiceInfo(options, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + + /* + * Get the fallback resources for parameters not specified in the conninfo + * string nor the service. + */ + for (option = options; option->keyword != NULL; option++) + { + if (option->val != NULL) + continue; /* Value was in conninfo or service */ + + /* + * Try to get the environment variable fallback + */ + if (option->envvar != NULL) + { + if ((tmp = getenv(option->envvar)) != NULL) + { + option->val = strdup(tmp); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + } + + /* + * No environment variable specified or this one isn't set - try + * compiled in + */ + if (option->compiled != NULL) + { + option->val = strdup(option->compiled); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + continue; + } + + /* + * Special handling for user + */ + if (strcmp(option->keyword, "user") == 0) + { + option->val = pg_fe_getauthname(errorMessage); + 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 NULL; + return conn->dbName; +} + +char * +PQuser(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pguser; +} + +char * +PQpass(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgpass; +} + +char * +PQhost(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pghost ? conn->pghost : conn->pgunixsocket; +} + +char * +PQport(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgport; +} + +char * +PQtty(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgtty; +} + +char * +PQoptions(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgoptions; +} + +ConnStatusType +PQstatus(const PGconn *conn) +{ + if (!conn) + return CONNECTION_BAD; + return conn->status; +} + +PGTransactionStatusType +PQtransactionStatus(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return PQTRANS_UNKNOWN; + if (conn->asyncStatus != PGASYNC_IDLE) + return PQTRANS_ACTIVE; + return conn->xactStatus; +} + +const char * +PQparameterStatus(const PGconn *conn, const char *paramName) +{ + const pgParameterStatus *pstatus; + + if (!conn || !paramName) + return NULL; + for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next) + { + if (strcmp(pstatus->name, paramName) == 0) + return pstatus->value; + } + return NULL; +} + +int +PQprotocolVersion(const PGconn *conn) +{ + if (!conn) + return 0; + if (conn->status == CONNECTION_BAD) + return 0; + return PG_PROTOCOL_MAJOR(conn->pversion); +} + +int +PQserverVersion(const PGconn *conn) +{ + if (!conn) + return 0; + if (conn->status == CONNECTION_BAD) + return 0; + return conn->sversion; +} + +char * +PQerrorMessage(const PGconn *conn) +{ + if (!conn) + return libpq_gettext("connection 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 +PQconnectionNeedsPassword(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->password_needed && + (conn->pgpass == NULL || conn->pgpass[0] == '\0')) + return true; + else + return false; +} + +int +PQconnectionUsedPassword(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->password_needed) + return true; + else + return false; +} + +int +PQclientEncoding(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return -1; + return conn->client_encoding; +} + +int +PQsetClientEncoding(PGconn *conn, const char *encoding) +{ + char qbuf[128]; + static const char query[] = "set client_encoding to '%s'"; + PGresult *res; + int status; + + if (!conn || conn->status != CONNECTION_OK) + return -1; + + if (!encoding) + return -1; + + /* Resolve special "auto" value from the locale */ + if (strcmp(encoding, "auto") == 0) + encoding = pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)); + + /* 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 == NULL) + return -1; + if (res->resultStatus != PGRES_COMMAND_OK) + status = -1; + else + { + /* + * In protocol 2 we have to assume the setting will stick, and adjust + * our state immediately. In protocol 3 and up we can rely on the + * backend to report the parameter value, and we'll change state at + * that time. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + pqSaveParameterStatus(conn, "client_encoding", encoding); + status = 0; /* everything is ok */ + } + PQclear(res); + return status; +} + +PGVerbosity +PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity) +{ + PGVerbosity old; + + if (!conn) + return PQERRORS_DEFAULT; + old = conn->verbosity; + conn->verbosity = verbosity; + return old; +} + +void +PQtrace(PGconn *conn, FILE *debug_port) +{ + if (conn == NULL) + return; + PQuntrace(conn); + conn->Pfdebug = debug_port; +} + +void +PQuntrace(PGconn *conn) +{ + if (conn == NULL) + return; + if (conn->Pfdebug) + { + fflush(conn->Pfdebug); + conn->Pfdebug = NULL; + } +} + +PQnoticeReceiver +PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg) +{ + PQnoticeReceiver old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.noticeRec; + if (proc) + { + conn->noticeHooks.noticeRec = proc; + conn->noticeHooks.noticeRecArg = arg; + } + return old; +} + +PQnoticeProcessor +PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) +{ + PQnoticeProcessor old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.noticeProc; + if (proc) + { + conn->noticeHooks.noticeProc = proc; + conn->noticeHooks.noticeProcArg = arg; + } + return old; +} + +/* + * The default notice message receiver just gets the standard notice text + * and sends it to the notice processor. This two-level setup exists + * mostly for backwards compatibility; perhaps we should deprecate use of + * PQsetNoticeProcessor? + */ +static void +defaultNoticeReceiver(void *arg, const PGresult *res) +{ + (void) arg; /* not used */ + if (res->noticeHooks.noticeProc != NULL) + (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg, + PQresultErrorMessage(res)); +} + +/* + * The default notice 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); +} + +/* + * returns a pointer to the next token or NULL if the current + * token doesn't match + */ +static char * +pwdfMatchesString(char *buf, char *token) +{ + char *tbuf, + *ttok; + bool bslash = false; + + if (buf == NULL || token == NULL) + return NULL; + tbuf = buf; + ttok = token; + if (tbuf[0] == '*' && tbuf[1] == ':') + return tbuf + 2; + while (*tbuf != 0) + { + if (*tbuf == '\\' && !bslash) + { + tbuf++; + bslash = true; + } + if (*tbuf == ':' && *ttok == 0 && !bslash) + return tbuf + 1; + bslash = false; + if (*ttok == 0) + return NULL; + if (*tbuf == *ttok) + { + tbuf++; + ttok++; + } + else + return NULL; + } + return NULL; +} + +/* Get a password from the password file. Return value is malloc'd. */ +static char * +PasswordFromFile(char *hostname, char *port, char *dbname, char *username) +{ + FILE *fp; + char pgpassfile[MAXPGPATH]; + struct stat stat_buf; + +#define LINELEN NAMEDATALEN*5 + char buf[LINELEN]; + + if (dbname == NULL || strlen(dbname) == 0) + return NULL; + + if (username == NULL || strlen(username) == 0) + return NULL; + + /* 'localhost' matches pghost of '' or the default socket directory */ + if (hostname == NULL) + hostname = DefaultHost; + else if (is_absolute_path(hostname)) + + /* + * We should probably use canonicalize_path(), but then we have to + * bring path.c into libpq, and it doesn't seem worth it. + */ + if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0) + hostname = DefaultHost; + + if (port == NULL) + port = DEF_PGPORT_STR; + + if (!getPgPassFilename(pgpassfile)) + return NULL; + + /* If password file cannot be opened, ignore it. */ + if (stat(pgpassfile, &stat_buf) != 0) + return NULL; + +#ifndef WIN32 + if (!S_ISREG(stat_buf.st_mode)) + { + fprintf(stderr, + libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"), + pgpassfile); + return NULL; + } + + /* If password file is insecure, alert the user and ignore it. */ + if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) + { + fprintf(stderr, + libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"), + pgpassfile); + return NULL; + } +#else + + /* + * On Win32, the directory is protected, so we don't have to check the + * file. + */ +#endif + + fp = fopen(pgpassfile, "r"); + if (fp == NULL) + return NULL; + + while (!feof(fp) && !ferror(fp)) + { + char *t = buf, + *ret; + int len; + + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + + len = strlen(buf); + if (len == 0) + continue; + + /* Remove trailing newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = 0; + + if ((t = pwdfMatchesString(t, hostname)) == NULL || + (t = pwdfMatchesString(t, port)) == NULL || + (t = pwdfMatchesString(t, dbname)) == NULL || + (t = pwdfMatchesString(t, username)) == NULL) + continue; + ret = strdup(t); + fclose(fp); + return ret; + } + + fclose(fp); + return NULL; + +#undef LINELEN +} + + +static bool +getPgPassFilename(char *pgpassfile) +{ + char *passfile_env; + + if ((passfile_env = getenv("PGPASSFILE")) != NULL) + /* use the literal path from the environment, if set */ + strlcpy(pgpassfile, passfile_env, MAXPGPATH); + else + { + char homedir[MAXPGPATH]; + + if (!pqGetHomeDirectory(homedir, sizeof(homedir))) + return false; + snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); + } + return true; +} + +/* + * If the connection failed, we should mention if + * we got the password from .pgpass in case that + * password is wrong. + */ +static void +dot_pg_pass_warning(PGconn *conn) +{ + /* If it was 'invalid authorization', add .pgpass mention */ + if (conn->dot_pgpass_used && conn->password_needed && conn->result && + /* only works with >= 9.0 servers */ + strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE), + ERRCODE_INVALID_PASSWORD) == 0) + { + char pgpassfile[MAXPGPATH]; + + if (!getPgPassFilename(pgpassfile)) + return; + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("password retrieved from file \"%s\"\n"), + pgpassfile); + } +} + + +/* + * Obtain user's home directory, return in given buffer + * + * On Unix, this actually returns the user's home directory. On Windows + * it returns the PostgreSQL-specific application data folder. + * + * This is essentially the same as get_home_path(), but we don't use that + * because we don't want to pull path.c into libpq (it pollutes application + * namespace) + */ +bool +pqGetHomeDirectory(char *buf, int bufsize) +{ +#ifndef WIN32 + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pwd = NULL; + + if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0) + return false; + strlcpy(buf, pwd->pw_dir, bufsize); + return true; +#else + char tmppath[MAX_PATH]; + + ZeroMemory(tmppath, sizeof(tmppath)); + if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK) + return false; + snprintf(buf, bufsize, "%s/postgresql", tmppath); + return true; +#endif +} + +/* + * To keep the API consistent, the locking stubs are always provided, even + * if they are not required. + */ + +static void +default_threadlock(int acquire) +{ +#ifdef ENABLE_THREAD_SAFETY +#ifndef WIN32 + static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER; +#else + static pthread_mutex_t singlethread_lock = NULL; + static long mutex_initlock = 0; + + if (singlethread_lock == NULL) + { + while (InterlockedExchange(&mutex_initlock, 1) == 1) + /* loop, another thread own the lock */ ; + if (singlethread_lock == NULL) + { + if (pthread_mutex_init(&singlethread_lock, NULL)) + PGTHREAD_ERROR("failed to initialize mutex"); + } + InterlockedExchange(&mutex_initlock, 0); + } +#endif + if (acquire) + { + if (pthread_mutex_lock(&singlethread_lock)) + PGTHREAD_ERROR("failed to lock mutex"); + } + else + { + if (pthread_mutex_unlock(&singlethread_lock)) + PGTHREAD_ERROR("failed to unlock mutex"); + } +#endif +} + +pgthreadlock_t +PQregisterThreadLock(pgthreadlock_t newhandler) +{ + pgthreadlock_t prev = pg_g_threadlock; + + if (newhandler) + pg_g_threadlock = newhandler; + else + pg_g_threadlock = default_threadlock; + + return prev; +} diff --git a/src/libpq/fe-exec.c b/src/libpq/fe-exec.c new file mode 100644 index 00000000..605d2428 --- /dev/null +++ b/src/libpq/fe-exec.c @@ -0,0 +1,3487 @@ +/*------------------------------------------------------------------------- + * + * fe-exec.c + * functions related to sending a query down to the backend + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-exec.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#endif + +/* keep this in same order as ExecStatusType in libpq-fe.h */ +char *const pgresStatus[] = { + "PGRES_EMPTY_QUERY", + "PGRES_COMMAND_OK", + "PGRES_TUPLES_OK", + "PGRES_COPY_OUT", + "PGRES_COPY_IN", + "PGRES_BAD_RESPONSE", + "PGRES_NONFATAL_ERROR", + "PGRES_FATAL_ERROR", + "PGRES_COPY_BOTH" +}; + +/* + * static state needed by PQescapeString and PQescapeBytea; initialize to + * values that result in backward-compatible behavior + */ +static int static_client_encoding = PG_SQL_ASCII; +static bool static_std_strings = false; + + +static PGEvent *dupEvents(PGEvent *events, int count); +static bool PQsendQueryStart(PGconn *conn); +static int PQsendQueryGuts(PGconn *conn, + const char *command, + const char *stmtName, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +static void parseInput(PGconn *conn); +static bool PQexecStart(PGconn *conn); +static PGresult *PQexecFinish(PGconn *conn); +static int PQsendDescribe(PGconn *conn, char desc_type, + const char *desc_target); +static int check_field_number(const PGresult *res, int field_num); + + +/* ---------------- + * Space management for PGresult. + * + * Formerly, libpq did a separate malloc() for each field of each tuple + * returned by a query. This was remarkably expensive --- malloc/free + * consumed a sizable part of the application's runtime. And there is + * no real need to keep track of the fields separately, since they will + * all be freed together when the PGresult is released. So now, we grab + * large blocks of storage from malloc and allocate space for query data + * within these blocks, using a trivially simple allocator. This reduces + * the number of malloc/free calls dramatically, and it also avoids + * fragmentation of the malloc storage arena. + * The PGresult structure itself is still malloc'd separately. We could + * combine it with the first allocation block, but that would waste space + * for the common case that no extra storage is actually needed (that is, + * the SQL command did not return tuples). + * + * We also malloc the top-level array of tuple pointers separately, because + * we need to be able to enlarge it via realloc, and our trivial space + * allocator doesn't handle that effectively. (Too bad the FE/BE protocol + * doesn't tell us up front how many tuples will be returned.) + * All other subsidiary storage for a PGresult is kept in PGresult_data blocks + * of size PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block + * is just a link to the next one, if any. Free-space management info is + * kept in the owning PGresult. + * A query returning a small amount of data will thus require three malloc + * calls: one for the PGresult, one for the tuples pointer array, and one + * PGresult_data block. + * + * Only the most recently allocated PGresult_data block is a candidate to + * have more stuff added to it --- any extra space left over in older blocks + * is wasted. We could be smarter and search the whole chain, but the point + * here is to be simple and fast. Typical applications do not keep a PGresult + * around very long anyway, so some wasted space within one is not a problem. + * + * Tuning constants for the space allocator are: + * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes + * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data + * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate + * blocks, instead of being crammed into a regular allocation block. + * Requirements for correct function are: + * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements + * of all machine data types. (Currently this is set from configure + * tests, so it should be OK automatically.) + * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <= + * PGRESULT_DATA_BLOCKSIZE + * pqResultAlloc assumes an object smaller than the threshold will fit + * in a new block. + * The amount of space wasted at the end of a block could be as much as + * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large. + * ---------------- + */ + +#define PGRESULT_DATA_BLOCKSIZE 2048 +#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */ +#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY) +#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2) + + +/* + * PQmakeEmptyPGresult + * returns a newly allocated, initialized PGresult with given status. + * If conn is not NULL and status indicates an error, the conn's + * errorMessage is copied. Also, any PGEvents are copied from the conn. + */ +PGresult * +PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) +{ + PGresult *result; + + result = (PGresult *) malloc(sizeof(PGresult)); + if (!result) + return NULL; + + result->ntups = 0; + result->numAttributes = 0; + result->attDescs = NULL; + result->tuples = NULL; + result->tupArrSize = 0; + result->numParameters = 0; + result->paramDescs = NULL; + result->resultStatus = status; + result->cmdStatus[0] = '\0'; + result->binary = 0; + result->events = NULL; + result->nEvents = 0; + result->errMsg = NULL; + result->errFields = NULL; + result->null_field[0] = '\0'; + result->curBlock = NULL; + result->curOffset = 0; + result->spaceLeft = 0; + + if (conn) + { + /* copy connection data we might need for operations on PGresult */ + result->noticeHooks = conn->noticeHooks; + result->client_encoding = conn->client_encoding; + + /* consider copying conn's errorMessage */ + switch (status) + { + case PGRES_EMPTY_QUERY: + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COPY_BOTH: + /* non-error cases */ + break; + default: + pqSetResultError(result, conn->errorMessage.data); + break; + } + + /* copy events last; result must be valid if we need to PQclear */ + if (conn->nEvents > 0) + { + result->events = dupEvents(conn->events, conn->nEvents); + if (!result->events) + { + PQclear(result); + return NULL; + } + result->nEvents = conn->nEvents; + } + } + else + { + /* defaults... */ + result->noticeHooks.noticeRec = NULL; + result->noticeHooks.noticeRecArg = NULL; + result->noticeHooks.noticeProc = NULL; + result->noticeHooks.noticeProcArg = NULL; + result->client_encoding = PG_SQL_ASCII; + } + + return result; +} + +/* + * PQsetResultAttrs + * + * Set the attributes for a given result. This function fails if there are + * already attributes contained in the provided result. The call is + * ignored if numAttributes is is zero or attDescs is NULL. If the + * function fails, it returns zero. If the function succeeds, it + * returns a non-zero value. + */ +int +PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs) +{ + int i; + + /* If attrs already exist, they cannot be overwritten. */ + if (!res || res->numAttributes > 0) + return FALSE; + + /* ignore no-op request */ + if (numAttributes <= 0 || !attDescs) + return TRUE; + + res->attDescs = (PGresAttDesc *) + PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc)); + + if (!res->attDescs) + return FALSE; + + res->numAttributes = numAttributes; + memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc)); + + /* deep-copy the attribute names, and determine format */ + res->binary = 1; + for (i = 0; i < res->numAttributes; i++) + { + if (res->attDescs[i].name) + res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name); + else + res->attDescs[i].name = res->null_field; + + if (!res->attDescs[i].name) + return FALSE; + + if (res->attDescs[i].format == 0) + res->binary = 0; + } + + return TRUE; +} + +/* + * PQcopyResult + * + * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL. + * The 'flags' argument controls which portions of the result will or will + * NOT be copied. The created result is always put into the + * PGRES_TUPLES_OK status. The source result error message is not copied, + * although cmdStatus is. + * + * To set custom attributes, use PQsetResultAttrs. That function requires + * that there are no attrs contained in the result, so to use that + * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES + * options with this function. + * + * Options: + * PG_COPYRES_ATTRS - Copy the source result's attributes + * + * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies + * copying the attrs, seeeing how the attrs are needed by the tuples. + * + * PG_COPYRES_EVENTS - Copy the source result's events. + * + * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks. + */ +PGresult * +PQcopyResult(const PGresult *src, int flags) +{ + PGresult *dest; + int i; + + if (!src) + return NULL; + + dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK); + if (!dest) + return NULL; + + /* Always copy these over. Is cmdStatus really useful here? */ + dest->client_encoding = src->client_encoding; + strcpy(dest->cmdStatus, src->cmdStatus); + + /* Wants attrs? */ + if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES)) + { + if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs)) + { + PQclear(dest); + return NULL; + } + } + + /* Wants to copy tuples? */ + if (flags & PG_COPYRES_TUPLES) + { + int tup, + field; + + for (tup = 0; tup < src->ntups; tup++) + { + for (field = 0; field < src->numAttributes; field++) + { + if (!PQsetvalue(dest, tup, field, + src->tuples[tup][field].value, + src->tuples[tup][field].len)) + { + PQclear(dest); + return NULL; + } + } + } + } + + /* Wants to copy notice hooks? */ + if (flags & PG_COPYRES_NOTICEHOOKS) + dest->noticeHooks = src->noticeHooks; + + /* Wants to copy PGEvents? */ + if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0) + { + dest->events = dupEvents(src->events, src->nEvents); + if (!dest->events) + { + PQclear(dest); + return NULL; + } + dest->nEvents = src->nEvents; + } + + /* Okay, trigger PGEVT_RESULTCOPY event */ + for (i = 0; i < dest->nEvents; i++) + { + if (src->events[i].resultInitialized) + { + PGEventResultCopy evt; + + evt.src = src; + evt.dest = dest; + if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt, + dest->events[i].passThrough)) + { + PQclear(dest); + return NULL; + } + dest->events[i].resultInitialized = TRUE; + } + } + + return dest; +} + +/* + * Copy an array of PGEvents (with no extra space for more). + * Does not duplicate the event instance data, sets this to NULL. + * Also, the resultInitialized flags are all cleared. + */ +static PGEvent * +dupEvents(PGEvent *events, int count) +{ + PGEvent *newEvents; + int i; + + if (!events || count <= 0) + return NULL; + + newEvents = (PGEvent *) malloc(count * sizeof(PGEvent)); + if (!newEvents) + return NULL; + + for (i = 0; i < count; i++) + { + newEvents[i].proc = events[i].proc; + newEvents[i].passThrough = events[i].passThrough; + newEvents[i].data = NULL; + newEvents[i].resultInitialized = FALSE; + newEvents[i].name = strdup(events[i].name); + if (!newEvents[i].name) + { + while (--i >= 0) + free(newEvents[i].name); + free(newEvents); + return NULL; + } + } + + return newEvents; +} + + +/* + * Sets the value for a tuple field. The tup_num must be less than or + * equal to PQntuples(res). If it is equal, a new tuple is created and + * added to the result. + * Returns a non-zero value for success and zero for failure. + */ +int +PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) +{ + PGresAttValue *attval; + + if (!check_field_number(res, field_num)) + return FALSE; + + /* Invalid tup_num, must be <= ntups */ + if (tup_num < 0 || tup_num > res->ntups) + return FALSE; + + /* need to allocate a new tuple? */ + if (tup_num == res->ntups) + { + PGresAttValue *tup; + int i; + + tup = (PGresAttValue *) + pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue), + TRUE); + + if (!tup) + return FALSE; + + /* initialize each column to NULL */ + for (i = 0; i < res->numAttributes; i++) + { + tup[i].len = NULL_LEN; + tup[i].value = res->null_field; + } + + /* add it to the array */ + if (!pqAddTuple(res, tup)) + return FALSE; + } + + attval = &res->tuples[tup_num][field_num]; + + /* treat either NULL_LEN or NULL value pointer as a NULL field */ + if (len == NULL_LEN || value == NULL) + { + attval->len = NULL_LEN; + attval->value = res->null_field; + } + else if (len <= 0) + { + attval->len = 0; + attval->value = res->null_field; + } + else + { + attval->value = (char *) pqResultAlloc(res, len + 1, TRUE); + if (!attval->value) + return FALSE; + attval->len = len; + memcpy(attval->value, value, len); + attval->value[len] = '\0'; + } + + return TRUE; +} + +/* + * pqResultAlloc - exported routine to allocate local storage in a PGresult. + * + * We force all such allocations to be maxaligned, since we don't know + * whether the value might be binary. + */ +void * +PQresultAlloc(PGresult *res, size_t nBytes) +{ + return pqResultAlloc(res, nBytes, TRUE); +} + +/* + * pqResultAlloc - + * Allocate subsidiary storage for a PGresult. + * + * nBytes is the amount of space needed for the object. + * If isBinary is true, we assume that we need to align the object on + * a machine allocation boundary. + * If isBinary is false, we assume the object is a char string and can + * be allocated on any byte boundary. + */ +void * +pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) +{ + char *space; + PGresult_data *block; + + if (!res) + return NULL; + + if (nBytes <= 0) + return res->null_field; + + /* + * If alignment is needed, round up the current position to an alignment + * boundary. + */ + if (isBinary) + { + int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY; + + if (offset) + { + res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset; + res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset; + } + } + + /* If there's enough space in the current block, no problem. */ + if (nBytes <= (size_t) res->spaceLeft) + { + space = res->curBlock->space + res->curOffset; + res->curOffset += nBytes; + res->spaceLeft -= nBytes; + return space; + } + + /* + * If the requested object is very large, give it its own block; this + * avoids wasting what might be most of the current block to start a new + * block. (We'd have to special-case requests bigger than the block size + * anyway.) The object is always given binary alignment in this case. + */ + if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD) + { + block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD); + if (!block) + return NULL; + space = block->space + PGRESULT_BLOCK_OVERHEAD; + if (res->curBlock) + { + /* + * Tuck special block below the active block, so that we don't + * have to waste the free space in the active block. + */ + block->next = res->curBlock->next; + res->curBlock->next = block; + } + else + { + /* Must set up the new block as the first active block. */ + block->next = NULL; + res->curBlock = block; + res->spaceLeft = 0; /* be sure it's marked full */ + } + return space; + } + + /* Otherwise, start a new block. */ + block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE); + if (!block) + return NULL; + block->next = res->curBlock; + res->curBlock = block; + if (isBinary) + { + /* object needs full alignment */ + res->curOffset = PGRESULT_BLOCK_OVERHEAD; + res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD; + } + else + { + /* we can cram it right after the overhead pointer */ + res->curOffset = sizeof(PGresult_data); + res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data); + } + + space = block->space + res->curOffset; + res->curOffset += nBytes; + res->spaceLeft -= nBytes; + return space; +} + +/* + * pqResultStrdup - + * Like strdup, but the space is subsidiary PGresult space. + */ +char * +pqResultStrdup(PGresult *res, const char *str) +{ + char *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE); + + if (space) + strcpy(space, str); + return space; +} + +/* + * pqSetResultError - + * assign a new error message to a PGresult + */ +void +pqSetResultError(PGresult *res, const char *msg) +{ + if (!res) + return; + if (msg && *msg) + res->errMsg = pqResultStrdup(res, msg); + else + res->errMsg = NULL; +} + +/* + * pqCatenateResultError - + * concatenate a new error message to the one already in a PGresult + */ +void +pqCatenateResultError(PGresult *res, const char *msg) +{ + PQExpBufferData errorBuf; + + if (!res || !msg) + return; + initPQExpBuffer(&errorBuf); + if (res->errMsg) + appendPQExpBufferStr(&errorBuf, res->errMsg); + appendPQExpBufferStr(&errorBuf, msg); + pqSetResultError(res, errorBuf.data); + termPQExpBuffer(&errorBuf); +} + +/* + * PQclear - + * free's the memory associated with a PGresult + */ +void +PQclear(PGresult *res) +{ + PGresult_data *block; + int i; + + if (!res) + return; + + for (i = 0; i < res->nEvents; i++) + { + /* only send DESTROY to successfully-initialized event procs */ + if (res->events[i].resultInitialized) + { + PGEventResultDestroy evt; + + evt.result = res; + (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt, + res->events[i].passThrough); + } + free(res->events[i].name); + } + + if (res->events) + free(res->events); + + /* Free all the subsidiary blocks */ + while ((block = res->curBlock) != NULL) + { + res->curBlock = block->next; + free(block); + } + + /* Free the top-level tuple pointer array */ + if (res->tuples) + free(res->tuples); + + /* zero out the pointer fields to catch programming errors */ + res->attDescs = NULL; + res->tuples = NULL; + res->paramDescs = NULL; + res->errFields = NULL; + res->events = NULL; + res->nEvents = 0; + /* res->curBlock was zeroed out earlier */ + + /* Free the PGresult structure itself */ + free(res); +} + +/* + * Handy subroutine to deallocate any partially constructed async result. + */ + +void +pqClearAsyncResult(PGconn *conn) +{ + if (conn->result) + PQclear(conn->result); + conn->result = NULL; + conn->curTuple = NULL; +} + +/* + * This subroutine deletes any existing async result, sets conn->result + * to a PGresult with status PGRES_FATAL_ERROR, and stores the current + * contents of conn->errorMessage into that result. It differs from a + * plain call on PQmakeEmptyPGresult() in that if there is already an + * async result with status PGRES_FATAL_ERROR, the current error message + * is APPENDED to the old error message instead of replacing it. This + * behavior lets us report multiple error conditions properly, if necessary. + * (An example where this is needed is when the backend sends an 'E' message + * and immediately closes the connection --- we want to report both the + * backend error and the connection closure error.) + */ +void +pqSaveErrorResult(PGconn *conn) +{ + /* + * If no old async result, just let PQmakeEmptyPGresult make one. Likewise + * if old result is not an error message. + */ + if (conn->result == NULL || + conn->result->resultStatus != PGRES_FATAL_ERROR || + conn->result->errMsg == NULL) + { + pqClearAsyncResult(conn); + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + } + else + { + /* Else, concatenate error message to existing async result. */ + pqCatenateResultError(conn->result, conn->errorMessage.data); + } +} + +/* + * This subroutine prepares an async result object for return to the caller. + * If there is not already an async result object, build an error object + * using whatever is in conn->errorMessage. In any case, clear the async + * result storage and make sure PQerrorMessage will agree with the result's + * error string. + */ +PGresult * +pqPrepareAsyncResult(PGconn *conn) +{ + PGresult *res; + + /* + * conn->result is the PGresult to return. If it is NULL (which probably + * shouldn't happen) we assume there is an appropriate error message in + * conn->errorMessage. + */ + res = conn->result; + conn->result = NULL; /* handing over ownership to caller */ + conn->curTuple = NULL; /* just in case */ + if (!res) + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + else + { + /* + * Make sure PQerrorMessage agrees with result; it could be different + * if we have concatenated messages. + */ + resetPQExpBuffer(&conn->errorMessage); + appendPQExpBufferStr(&conn->errorMessage, + PQresultErrorMessage(res)); + } + return res; +} + +/* + * pqInternalNotice - produce an internally-generated notice message + * + * A format string and optional arguments can be passed. Note that we do + * libpq_gettext() here, so callers need not. + * + * The supplied text is taken as primary message (ie., it should not include + * a trailing newline, and should not be more than one line). + */ +void +pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) +{ + char msgBuf[1024]; + va_list args; + PGresult *res; + + if (hooks->noticeRec == NULL) + return; /* nobody home to receive notice? */ + + /* Format the message */ + va_start(args, fmt); + vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args); + va_end(args); + msgBuf[sizeof(msgBuf) - 1] = '\0'; /* make real sure it's terminated */ + + /* Make a PGresult to pass to the notice receiver */ + res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR); + if (!res) + return; + res->noticeHooks = *hooks; + + /* + * Set up fields of notice. + */ + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf); + pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE")); + /* XXX should provide a SQLSTATE too? */ + + /* + * Result text is always just the primary message + newline. If we can't + * allocate it, don't bother invoking the receiver. + */ + res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE); + if (res->errMsg) + { + sprintf(res->errMsg, "%s\n", msgBuf); + + /* + * Pass to receiver, then free it. + */ + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + } + PQclear(res); +} + +/* + * pqAddTuple + * add a row pointer to the PGresult structure, growing it if necessary + * Returns TRUE if OK, FALSE if not enough memory to add the row + */ +int +pqAddTuple(PGresult *res, PGresAttValue *tup) +{ + if (res->ntups >= res->tupArrSize) + { + /* + * Try to grow the array. + * + * We can use realloc because shallow copying of the structure is + * okay. Note that the first time through, res->tuples is NULL. While + * ANSI says that realloc() should act like malloc() in that case, + * some old C libraries (like SunOS 4.1.x) coredump instead. On + * failure realloc is supposed to return NULL without damaging the + * existing allocation. Note that the positions beyond res->ntups are + * garbage, not necessarily NULL. + */ + int newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128; + PGresAttValue **newTuples; + + if (res->tuples == NULL) + newTuples = (PGresAttValue **) + malloc(newSize * sizeof(PGresAttValue *)); + else + newTuples = (PGresAttValue **) + realloc(res->tuples, newSize * sizeof(PGresAttValue *)); + if (!newTuples) + return FALSE; /* malloc or realloc failed */ + res->tupArrSize = newSize; + res->tuples = newTuples; + } + res->tuples[res->ntups] = tup; + res->ntups++; + return TRUE; +} + +/* + * pqSaveMessageField - save one field of an error or notice message + */ +void +pqSaveMessageField(PGresult *res, char code, const char *value) +{ + PGMessageField *pfield; + + pfield = (PGMessageField *) + pqResultAlloc(res, + sizeof(PGMessageField) + strlen(value), + TRUE); + if (!pfield) + return; /* out of memory? */ + pfield->code = code; + strcpy(pfield->contents, value); + pfield->next = res->errFields; + res->errFields = pfield; +} + +/* + * pqSaveParameterStatus - remember parameter status sent by backend + */ +void +pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) +{ + pgParameterStatus *pstatus; + pgParameterStatus *prev; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n", + name, value); + + /* + * Forget any old information about the parameter + */ + for (pstatus = conn->pstatus, prev = NULL; + pstatus != NULL; + prev = pstatus, pstatus = pstatus->next) + { + if (strcmp(pstatus->name, name) == 0) + { + if (prev) + prev->next = pstatus->next; + else + conn->pstatus = pstatus->next; + free(pstatus); /* frees name and value strings too */ + break; + } + } + + /* + * Store new info as a single malloc block + */ + pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) + + strlen(name) +strlen(value) + 2); + if (pstatus) + { + char *ptr; + + ptr = ((char *) pstatus) + sizeof(pgParameterStatus); + pstatus->name = ptr; + strcpy(ptr, name); + ptr += strlen(name) + 1; + pstatus->value = ptr; + strcpy(ptr, value); + pstatus->next = conn->pstatus; + conn->pstatus = pstatus; + } + + /* + * Special hacks: remember client_encoding and + * standard_conforming_strings, and convert server version to a numeric + * form. We keep the first two of these in static variables as well, so + * that PQescapeString and PQescapeBytea can behave somewhat sanely (at + * least in single-connection-using programs). + */ + if (strcmp(name, "client_encoding") == 0) + { + conn->client_encoding = pg_char_to_encoding(value); + /* if we don't recognize the encoding name, fall back to SQL_ASCII */ + if (conn->client_encoding < 0) + conn->client_encoding = PG_SQL_ASCII; + static_client_encoding = conn->client_encoding; + } + else if (strcmp(name, "standard_conforming_strings") == 0) + { + conn->std_strings = (strcmp(value, "on") == 0); + static_std_strings = conn->std_strings; + } + else if (strcmp(name, "server_version") == 0) + { + int cnt; + int vmaj, + vmin, + vrev; + + cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev); + + if (cnt < 2) + conn->sversion = 0; /* unknown */ + else + { + if (cnt == 2) + vrev = 0; + conn->sversion = (100 * vmaj + vmin) * 100 + vrev; + } + } +} + + +/* + * PQsendQuery + * Submit a query, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendQuery(PGconn *conn, const char *query) +{ + if (!PQsendQueryStart(conn)) + return 0; + + if (!query) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + + /* construct the outgoing Query message */ + if (pqPutMsgStart('Q', false, conn) < 0 || + pqPuts(query, conn) < 0 || + pqPutMsgEnd(conn) < 0) + { + pqHandleSendFailure(conn); + return 0; + } + + /* remember we are using simple query protocol */ + conn->queryclass = PGQUERY_SIMPLE; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + conn->last_query = strdup(query); + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + { + pqHandleSendFailure(conn); + return 0; + } + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; +} + +/* + * PQsendQueryParams + * Like PQsendQuery, but use protocol 3.0 so we can pass parameters + */ +int +PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQsendQueryStart(conn)) + return 0; + + if (!command) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + + return PQsendQueryGuts(conn, + command, + "", /* use unnamed statement */ + nParams, + paramTypes, + paramValues, + paramLengths, + paramFormats, + resultFormat); +} + +/* + * PQsendPrepare + * Submit a Parse message, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendPrepare(PGconn *conn, + const char *stmtName, const char *query, + int nParams, const Oid *paramTypes) +{ + if (!PQsendQueryStart(conn)) + return 0; + + if (!stmtName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); + return 0; + } + + if (!query) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* construct the Parse message */ + if (pqPutMsgStart('P', false, conn) < 0 || + pqPuts(stmtName, conn) < 0 || + pqPuts(query, conn) < 0) + goto sendFailed; + + if (nParams > 0 && paramTypes) + { + int i; + + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramTypes[i], 4, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are doing just a Parse */ + conn->queryclass = PGQUERY_PREPARE; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + conn->last_query = strdup(query); + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * PQsendQueryPrepared + * Like PQsendQuery, but execute a previously prepared statement, + * using protocol 3.0 so we can pass parameters + */ +int +PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQsendQueryStart(conn)) + return 0; + + if (!stmtName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); + return 0; + } + + return PQsendQueryGuts(conn, + NULL, /* no command to parse */ + stmtName, + nParams, + NULL, /* no param types */ + paramValues, + paramLengths, + paramFormats, + resultFormat); +} + +/* + * Common startup code for PQsendQuery and sibling routines + */ +static bool +PQsendQueryStart(PGconn *conn) +{ + if (!conn) + return false; + + /* clear the error string */ + resetPQExpBuffer(&conn->errorMessage); + + /* Don't try to send if we know there's no live connection. */ + if (conn->status != CONNECTION_OK) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no connection to the server\n")); + return false; + } + /* Can't send while already busy, either. */ + if (conn->asyncStatus != PGASYNC_IDLE) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("another command is already in progress\n")); + return false; + } + + /* initialize async result-accumulation state */ + conn->result = NULL; + conn->curTuple = NULL; + + /* ready to send command message */ + return true; +} + +/* + * PQsendQueryGuts + * Common code for protocol-3.0 query sending + * PQsendQueryStart should be done already + * + * command may be NULL to indicate we use an already-prepared statement + */ +static int +PQsendQueryGuts(PGconn *conn, + const char *command, + const char *stmtName, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + int i; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* + * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync, + * using specified statement name and the unnamed portal. + */ + + if (command) + { + /* construct the Parse message */ + if (pqPutMsgStart('P', false, conn) < 0 || + pqPuts(stmtName, conn) < 0 || + pqPuts(command, conn) < 0) + goto sendFailed; + if (nParams > 0 && paramTypes) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramTypes[i], 4, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + } + + /* Construct the Bind message */ + if (pqPutMsgStart('B', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPuts(stmtName, conn) < 0) + goto sendFailed; + + /* Send parameter formats */ + if (nParams > 0 && paramFormats) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramFormats[i], 2, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + + /* Send parameters */ + for (i = 0; i < nParams; i++) + { + if (paramValues && paramValues[i]) + { + int nbytes; + + if (paramFormats && paramFormats[i] != 0) + { + /* binary parameter */ + if (paramLengths) + nbytes = paramLengths[i]; + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("length must be given for binary parameter\n")); + goto sendFailed; + } + } + else + { + /* text parameter, do not use paramLengths */ + nbytes = strlen(paramValues[i]); + } + if (pqPutInt(nbytes, 4, conn) < 0 || + pqPutnchar(paramValues[i], nbytes, conn) < 0) + goto sendFailed; + } + else + { + /* take the param as NULL */ + if (pqPutInt(-1, 4, conn) < 0) + goto sendFailed; + } + } + if (pqPutInt(1, 2, conn) < 0 || + pqPutInt(resultFormat, 2, conn)) + goto sendFailed; + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Describe Portal message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc('P', conn) < 0 || + pqPuts("", conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Execute message */ + if (pqPutMsgStart('E', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPutInt(0, 4, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are using extended query protocol */ + conn->queryclass = PGQUERY_EXTENDED; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + if (command) + conn->last_query = strdup(command); + else + conn->last_query = NULL; + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * pqHandleSendFailure: try to clean up after failure to send command. + * + * Primarily, what we want to accomplish here is to process an async + * NOTICE message that the backend might have sent just before it died. + * + * NOTE: this routine should only be called in PGASYNC_IDLE state. + */ +void +pqHandleSendFailure(PGconn *conn) +{ + /* + * Accept any available input data, ignoring errors. Note that if + * pqReadData decides the backend has closed the channel, it will close + * our side of the socket --- that's just what we want here. + */ + while (pqReadData(conn) > 0) + /* loop until no more data readable */ ; + + /* + * Parse any available input messages. Since we are in PGASYNC_IDLE + * state, only NOTICE and NOTIFY messages will be eaten. + */ + parseInput(conn); +} + +/* + * Consume any available input from the backend + * 0 return: some kind of trouble + * 1 return: no problem + */ +int +PQconsumeInput(PGconn *conn) +{ + if (!conn) + return 0; + + /* + * for non-blocking connections try to flush the send-queue, otherwise we + * may never get a response for something that may not have already been + * sent because it's in our write buffer! + */ + if (pqIsnonblocking(conn)) + { + if (pqFlush(conn) < 0) + return 0; + } + + /* + * Load more data, if available. We do this no matter what state we are + * in, since we are probably getting called because the application wants + * to get rid of a read-select condition. Note that we will NOT block + * waiting for more input. + */ + if (pqReadData(conn) < 0) + return 0; + + /* Parsing of the data waits till later. */ + return 1; +} + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +static void +parseInput(PGconn *conn) +{ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + pqParseInput3(conn); + else + pqParseInput2(conn); +} + +/* + * PQisBusy + * Return TRUE if PQgetResult would block waiting for input. + */ + +int +PQisBusy(PGconn *conn) +{ + if (!conn) + return FALSE; + + /* Parse any available data, if our state permits. */ + parseInput(conn); + + /* PQgetResult will return immediately in all states except BUSY. */ + return conn->asyncStatus == PGASYNC_BUSY; +} + + +/* + * PQgetResult + * Get the next PGresult produced by a query. Returns NULL if no + * query work remains or an error has occurred (e.g. out of + * memory). + */ + +PGresult * +PQgetResult(PGconn *conn) +{ + PGresult *res; + + if (!conn) + return NULL; + + /* Parse any available data, if our state permits. */ + parseInput(conn); + + /* If not ready to return something, block until we are. */ + while (conn->asyncStatus == PGASYNC_BUSY) + { + int flushResult; + + /* + * If data remains unsent, send it. Else we might be waiting for the + * result of a command the backend hasn't even got yet. + */ + while ((flushResult = pqFlush(conn)) > 0) + { + if (pqWait(FALSE, TRUE, conn)) + { + flushResult = -1; + break; + } + } + + /* Wait for some more data, and load it. */ + if (flushResult || + pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + /* + * conn->errorMessage has been set by pqWait or pqReadData. We + * want to append it to any already-received error message. + */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_IDLE; + return pqPrepareAsyncResult(conn); + } + + /* Parse it. */ + parseInput(conn); + } + + /* Return the appropriate thing. */ + switch (conn->asyncStatus) + { + case PGASYNC_IDLE: + res = NULL; /* query is complete */ + break; + case PGASYNC_READY: + res = pqPrepareAsyncResult(conn); + /* Set the state back to BUSY, allowing parsing to proceed. */ + conn->asyncStatus = PGASYNC_BUSY; + break; + case PGASYNC_COPY_IN: + if (conn->result && conn->result->resultStatus == PGRES_COPY_IN) + res = pqPrepareAsyncResult(conn); + else + res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN); + break; + case PGASYNC_COPY_OUT: + if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT) + res = pqPrepareAsyncResult(conn); + else + res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT); + break; + case PGASYNC_COPY_BOTH: + if (conn->result && conn->result->resultStatus == PGRES_COPY_BOTH) + res = pqPrepareAsyncResult(conn); + else + res = PQmakeEmptyPGresult(conn, PGRES_COPY_BOTH); + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unexpected asyncStatus: %d\n"), + (int) conn->asyncStatus); + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + break; + } + + if (res) + { + int i; + + for (i = 0; i < res->nEvents; i++) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"), + res->events[i].name); + pqSetResultError(res, conn->errorMessage.data); + res->resultStatus = PGRES_FATAL_ERROR; + break; + } + res->events[i].resultInitialized = TRUE; + } + } + + return res; +} + + +/* + * PQexec + * send a query to the backend and package up the result in a PGresult + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQexec(PGconn *conn, const char *query) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQuery(conn, query)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQexecParams + * Like PQexec, but use protocol 3.0 so we can pass parameters + */ +PGresult * +PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQueryParams(conn, command, + nParams, paramTypes, paramValues, paramLengths, + paramFormats, resultFormat)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQprepare + * Creates a prepared statement by issuing a v3.0 parse message. + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQprepare(PGconn *conn, + const char *stmtName, const char *query, + int nParams, const Oid *paramTypes) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQexecPrepared + * Like PQexec, but execute a previously prepared statement, + * using protocol 3.0 so we can pass parameters + */ +PGresult * +PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQueryPrepared(conn, stmtName, + nParams, paramValues, paramLengths, + paramFormats, resultFormat)) + return NULL; + return PQexecFinish(conn); +} + +/* + * Common code for PQexec and sibling routines: prepare to send command + */ +static bool +PQexecStart(PGconn *conn) +{ + PGresult *result; + + if (!conn) + return false; + + /* + * Silently discard any prior query result that application didn't eat. + * This is probably poor design, but it's here for backward compatibility. + */ + while ((result = PQgetResult(conn)) != NULL) + { + ExecStatusType resultStatus = result->resultStatus; + + PQclear(result); /* only need its status */ + if (resultStatus == PGRES_COPY_IN) + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* In protocol 3, we can get out of a COPY IN state */ + if (PQputCopyEnd(conn, + libpq_gettext("COPY terminated by new PQexec")) < 0) + return false; + /* keep waiting to swallow the copy's failure message */ + } + else + { + /* In older protocols we have to punt */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("COPY IN state must be terminated first\n")); + return false; + } + } + else if (resultStatus == PGRES_COPY_OUT) + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* + * In protocol 3, we can get out of a COPY OUT state: we just + * switch back to BUSY and allow the remaining COPY data to be + * dropped on the floor. + */ + conn->asyncStatus = PGASYNC_BUSY; + /* keep waiting to swallow the copy's completion message */ + } + else + { + /* In older protocols we have to punt */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("COPY OUT state must be terminated first\n")); + return false; + } + } + else if (resultStatus == PGRES_COPY_BOTH) + { + /* We don't allow PQexec during COPY BOTH */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PQexec not allowed during COPY BOTH\n")); + return false; + } + /* check for loss of connection, too */ + if (conn->status == CONNECTION_BAD) + return false; + } + + /* OK to send a command */ + return true; +} + +/* + * Common code for PQexec and sibling routines: wait for command result + */ +static PGresult * +PQexecFinish(PGconn *conn) +{ + PGresult *result; + PGresult *lastResult; + + /* + * For backwards compatibility, return the last result if there are more + * than one --- but merge error messages if we get more than one error + * result. + * + * We have to stop if we see copy in/out/both, however. We will resume + * parsing after application performs the data transfer. + * + * Also stop if the connection is lost (else we'll loop infinitely). + */ + lastResult = NULL; + while ((result = PQgetResult(conn)) != NULL) + { + if (lastResult) + { + if (lastResult->resultStatus == PGRES_FATAL_ERROR && + result->resultStatus == PGRES_FATAL_ERROR) + { + pqCatenateResultError(lastResult, result->errMsg); + PQclear(result); + result = lastResult; + + /* + * Make sure PQerrorMessage agrees with concatenated result + */ + resetPQExpBuffer(&conn->errorMessage); + appendPQExpBufferStr(&conn->errorMessage, result->errMsg); + } + else + PQclear(lastResult); + } + lastResult = result; + if (result->resultStatus == PGRES_COPY_IN || + result->resultStatus == PGRES_COPY_OUT || + result->resultStatus == PGRES_COPY_BOTH || + conn->status == CONNECTION_BAD) + break; + } + + return lastResult; +} + +/* + * PQdescribePrepared + * Obtain information about a previously prepared statement + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). On success, the PGresult contains status + * PGRES_COMMAND_OK, and its parameter and column-heading fields describe + * the statement's inputs and outputs respectively. + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQdescribePrepared(PGconn *conn, const char *stmt) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendDescribe(conn, 'S', stmt)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQdescribePortal + * Obtain information about a previously created portal + * + * This is much like PQdescribePrepared, except that no parameter info is + * returned. Note that at the moment, libpq doesn't really expose portals + * to the client; but this can be used with a portal created by a SQL + * DECLARE CURSOR command. + */ +PGresult * +PQdescribePortal(PGconn *conn, const char *portal) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendDescribe(conn, 'P', portal)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQsendDescribePrepared + * Submit a Describe Statement command, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendDescribePrepared(PGconn *conn, const char *stmt) +{ + return PQsendDescribe(conn, 'S', stmt); +} + +/* + * PQsendDescribePortal + * Submit a Describe Portal command, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendDescribePortal(PGconn *conn, const char *portal) +{ + return PQsendDescribe(conn, 'P', portal); +} + +/* + * PQsendDescribe + * Common code to send a Describe command + * + * Available options for desc_type are + * 'S' to describe a prepared statement; or + * 'P' to describe a portal. + * Returns 1 on success and 0 on failure. + */ +static int +PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target) +{ + /* Treat null desc_target as empty string */ + if (!desc_target) + desc_target = ""; + + if (!PQsendQueryStart(conn)) + return 0; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* construct the Describe message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc(desc_type, conn) < 0 || + pqPuts(desc_target, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are doing a Describe */ + conn->queryclass = PGQUERY_DESCRIBE; + + /* reset last-query string (not relevant now) */ + if (conn->last_query) + { + free(conn->last_query); + conn->last_query = NULL; + } + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * PQnotifies + * returns a PGnotify* structure of the latest async notification + * that has not yet been handled + * + * returns NULL, if there is currently + * no unhandled async notification from the backend + * + * the CALLER is responsible for FREE'ing the structure returned + */ +PGnotify * +PQnotifies(PGconn *conn) +{ + PGnotify *event; + + if (!conn) + return NULL; + + /* Parse any available data to see if we can extract NOTIFY messages. */ + parseInput(conn); + + event = conn->notifyHead; + if (event) + { + conn->notifyHead = event->next; + if (!conn->notifyHead) + conn->notifyTail = NULL; + event->next = NULL; /* don't let app see the internal state */ + } + return event; +} + +/* + * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyData(PGconn *conn, const char *buffer, int nbytes) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + + /* + * Process any NOTICE or NOTIFY messages that might be pending in the + * input buffer. Since the server might generate many notices during the + * COPY, we want to clean those out reasonably promptly to prevent + * indefinite expansion of the input buffer. (Note: the actual read of + * input data into the input buffer happens down inside pqSendSome, but + * it's not authorized to get rid of the data again.) + */ + parseInput(conn); + + if (nbytes > 0) + { + /* + * Try to flush any previously sent data in preference to growing the + * output buffer. If we can't enlarge the buffer enough to hold the + * data, return 0 in the nonblock case, else hard error. (For + * simplicity, always assume 5 bytes of overhead even in protocol 2.0 + * case.) + */ + if ((conn->outBufSize - conn->outCount - 5) < nbytes) + { + if (pqFlush(conn) < 0) + return -1; + if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes, + conn)) + return pqIsnonblocking(conn) ? 0 : -1; + } + /* Send the data (too simple to delegate to fe-protocol files) */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (pqPutMsgStart('d', false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + if (pqPutMsgStart(0, false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + return 1; +} + +/* + * PQputCopyEnd - send EOF indication to the backend during COPY IN + * + * After calling this, use PQgetResult() to check command completion status. + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyEnd(PGconn *conn, const char *errormsg) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + + /* + * Send the COPY END indicator. This is simple enough that we don't + * bother delegating it to the fe-protocol files. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (errormsg) + { + /* Send COPY FAIL */ + if (pqPutMsgStart('f', false, conn) < 0 || + pqPuts(errormsg, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + /* Send COPY DONE */ + if (pqPutMsgStart('c', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + + /* + * If we sent the COPY command in extended-query mode, we must issue a + * Sync as well. + */ + if (conn->queryclass != PGQUERY_SIMPLE) + { + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + else + { + if (errormsg) + { + /* Ooops, no way to do this in 2.0 */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return -1; + } + else + { + /* Send old-style end-of-data marker */ + if (pqPutMsgStart(0, false, conn) < 0 || + pqPutnchar("\\.\n", 3, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* Try to flush data */ + if (pqFlush(conn) < 0) + return -1; + + return 1; +} + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * or COPY BOTH + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +PQgetCopyData(PGconn *conn, char **buffer, int async) +{ + *buffer = NULL; /* for all failure cases */ + if (!conn) + return -2; + if (conn->asyncStatus != PGASYNC_COPY_OUT && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -2; + } + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetCopyData3(conn, buffer, async); + else + return pqGetCopyData2(conn, buffer, async); +} + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * Chiefly here so that applications can use "COPY to stdout" + * and read the output string. Returns a null-terminated string in s. + * + * XXX this routine is now deprecated, because it can't handle binary data. + * If called during a COPY BINARY we return EOF. + * + * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips + * the terminating \n (like gets(3)). + * + * CAUTION: the caller is responsible for detecting the end-of-copy signal + * (a line containing just "\.") when using this routine. + * + * RETURNS: + * EOF if error (eg, invalid arguments are given) + * 0 if EOL is reached (i.e., \n has been read) + * (this is required for backward-compatibility -- this + * routine used to always return EOF or 0, assuming that + * the line ended within maxlen bytes.) + * 1 in other cases (i.e., the buffer was filled before \n is reached) + */ +int +PQgetline(PGconn *conn, char *s, int maxlen) +{ + if (!s || maxlen <= 0) + return EOF; + *s = '\0'; + /* maxlen must be at least 3 to hold the \. terminator! */ + if (maxlen < 3) + return EOF; + + if (!conn) + return EOF; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetline3(conn, s, maxlen); + else + return pqGetline2(conn, s, maxlen); +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * This routine is for applications that want to do "COPY to stdout" + * asynchronously, that is without blocking. Having issued the COPY command + * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput + * and this routine until the end-of-data signal is detected. Unlike + * PQgetline, this routine takes responsibility for detecting end-of-data. + * + * On each call, PQgetlineAsync will return data if a complete data row + * is available in libpq's input buffer. Otherwise, no data is returned + * until the rest of the row arrives. + * + * If -1 is returned, the end-of-data signal has been recognized (and removed + * from libpq's input buffer). The caller *must* next call PQendcopy and + * then return to normal processing. + * + * RETURNS: + * -1 if the end-of-copy-data marker has been recognized + * 0 if no data is available + * >0 the number of bytes returned. + * + * The data returned will not extend beyond a data-row boundary. If possible + * a whole row will be returned at one time. But if the buffer offered by + * the caller is too small to hold a row sent by the backend, then a partial + * data row will be returned. In text mode this can be detected by testing + * whether the last returned byte is '\n' or not. + * + * The returned data is *not* null-terminated. + */ + +int +PQgetlineAsync(PGconn *conn, char *buffer, int bufsize) +{ + if (!conn) + return -1; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetlineAsync3(conn, buffer, bufsize); + else + return pqGetlineAsync2(conn, buffer, bufsize); +} + +/* + * PQputline -- sends a string to the backend during COPY IN. + * Returns 0 if OK, EOF if not. + * + * This is deprecated primarily because the return convention doesn't allow + * caller to tell the difference between a hard error and a nonblock-mode + * send failure. + */ +int +PQputline(PGconn *conn, const char *s) +{ + return PQputnbytes(conn, s, strlen(s)); +} + +/* + * PQputnbytes -- like PQputline, but buffer need not be null-terminated. + * Returns 0 if OK, EOF if not. + */ +int +PQputnbytes(PGconn *conn, const char *buffer, int nbytes) +{ + if (PQputCopyData(conn, buffer, nbytes) > 0) + return 0; + else + return EOF; +} + +/* + * PQendcopy + * After completing the data transfer portion of a copy in/out, + * the application must call this routine to finish the command protocol. + * + * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult + * to get the transfer status. Note however that when using 2.0 protocol, + * recovering from a copy failure often requires a PQreset. PQendcopy will + * take care of that, PQgetResult won't. + * + * RETURNS: + * 0 on success + * 1 on failure + */ +int +PQendcopy(PGconn *conn) +{ + if (!conn) + return 0; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqEndcopy3(conn); + else + return pqEndcopy2(conn); +} + + +/* ---------------- + * PQfn - Send a function call to the POSTGRES backend. + * + * conn : backend connection + * fnid : function id + * result_buf : pointer to result buffer (&int if integer) + * result_len : length of return value. + * actual_result_len: actual length returned. (differs from result_len + * for varlena structures.) + * result_type : If the result is an integer, this must be 1, + * otherwise this should be 0 + * args : pointer to an array of function arguments. + * (each has length, if integer, and value/pointer) + * nargs : # of arguments in args array. + * + * RETURNS + * PGresult with status = PGRES_COMMAND_OK if successful. + * *actual_result_len is > 0 if there is a return value, 0 if not. + * PGresult with status = PGRES_FATAL_ERROR if backend returns an error. + * NULL on communications failure. conn->errorMessage will be set. + * ---------------- + */ + +PGresult * +PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *actual_result_len, + int result_is_int, + const PQArgBlock *args, + int nargs) +{ + *actual_result_len = 0; + + if (!conn) + return NULL; + + /* clear the error string */ + resetPQExpBuffer(&conn->errorMessage); + + if (conn->sock < 0 || conn->asyncStatus != PGASYNC_IDLE || + conn->result != NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection in wrong state\n")); + return NULL; + } + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqFunctionCall3(conn, fnid, + result_buf, actual_result_len, + result_is_int, + args, nargs); + else + return pqFunctionCall2(conn, fnid, + result_buf, actual_result_len, + result_is_int, + args, nargs); +} + + +/* ====== accessor funcs for PGresult ======== */ + +ExecStatusType +PQresultStatus(const PGresult *res) +{ + if (!res) + return PGRES_FATAL_ERROR; + return res->resultStatus; +} + +char * +PQresStatus(ExecStatusType status) +{ + if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0]) + return libpq_gettext("invalid ExecStatusType code"); + return pgresStatus[status]; +} + +char * +PQresultErrorMessage(const PGresult *res) +{ + if (!res || !res->errMsg) + return ""; + return res->errMsg; +} + +char * +PQresultErrorField(const PGresult *res, int fieldcode) +{ + PGMessageField *pfield; + + if (!res) + return NULL; + for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) + { + if (pfield->code == fieldcode) + return pfield->contents; + } + return NULL; +} + +int +PQntuples(const PGresult *res) +{ + if (!res) + return 0; + return res->ntups; +} + +int +PQnfields(const PGresult *res) +{ + if (!res) + return 0; + return res->numAttributes; +} + +int +PQbinaryTuples(const PGresult *res) +{ + if (!res) + return 0; + return res->binary; +} + +/* + * Helper routines to range-check field numbers and tuple numbers. + * Return TRUE if OK, FALSE if not + */ + +static int +check_field_number(const PGresult *res, int field_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (field_num < 0 || field_num >= res->numAttributes) + { + pqInternalNotice(&res->noticeHooks, + "column number %d is out of range 0..%d", + field_num, res->numAttributes - 1); + return FALSE; + } + return TRUE; +} + +static int +check_tuple_field_number(const PGresult *res, + int tup_num, int field_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (tup_num < 0 || tup_num >= res->ntups) + { + pqInternalNotice(&res->noticeHooks, + "row number %d is out of range 0..%d", + tup_num, res->ntups - 1); + return FALSE; + } + if (field_num < 0 || field_num >= res->numAttributes) + { + pqInternalNotice(&res->noticeHooks, + "column number %d is out of range 0..%d", + field_num, res->numAttributes - 1); + return FALSE; + } + return TRUE; +} + +static int +check_param_number(const PGresult *res, int param_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (param_num < 0 || param_num >= res->numParameters) + { + pqInternalNotice(&res->noticeHooks, + "parameter number %d is out of range 0..%d", + param_num, res->numParameters - 1); + return FALSE; + } + + return TRUE; +} + +/* + * returns NULL if the field_num is invalid + */ +char * +PQfname(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return NULL; + if (res->attDescs) + return res->attDescs[field_num].name; + else + return NULL; +} + +/* + * PQfnumber: find column number given column name + * + * The column name is parsed as if it were in a SQL statement, including + * case-folding and double-quote processing. But note a possible gotcha: + * downcasing in the frontend might follow different locale rules than + * downcasing in the backend... + * + * Returns -1 if no match. In the present backend it is also possible + * to have multiple matches, in which case the first one is found. + */ +int +PQfnumber(const PGresult *res, const char *field_name) +{ + char *field_case; + bool in_quotes; + char *iptr; + char *optr; + int i; + + if (!res) + return -1; + + /* + * Note: it is correct to reject a zero-length input string; the proper + * input to match a zero-length field name would be "". + */ + if (field_name == NULL || + field_name[0] == '\0' || + res->attDescs == NULL) + return -1; + + /* + * Note: this code will not reject partially quoted strings, eg + * foo"BAR"foo will become fooBARfoo when it probably ought to be an error + * condition. + */ + field_case = strdup(field_name); + if (field_case == NULL) + return -1; /* grotty */ + + in_quotes = false; + optr = field_case; + for (iptr = field_case; *iptr; iptr++) + { + char c = *iptr; + + if (in_quotes) + { + if (c == '"') + { + if (iptr[1] == '"') + { + /* doubled quotes become a single quote */ + *optr++ = '"'; + iptr++; + } + else + in_quotes = false; + } + else + *optr++ = c; + } + else if (c == '"') + in_quotes = true; + else + { + c = pg_tolower((unsigned char) c); + *optr++ = c; + } + } + *optr = '\0'; + + for (i = 0; i < res->numAttributes; i++) + { + if (strcmp(field_case, res->attDescs[i].name) == 0) + { + free(field_case); + return i; + } + } + free(field_case); + return -1; +} + +Oid +PQftable(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return InvalidOid; + if (res->attDescs) + return res->attDescs[field_num].tableid; + else + return InvalidOid; +} + +int +PQftablecol(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].columnid; + else + return 0; +} + +int +PQfformat(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].format; + else + return 0; +} + +Oid +PQftype(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return InvalidOid; + if (res->attDescs) + return res->attDescs[field_num].typid; + else + return InvalidOid; +} + +int +PQfsize(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].typlen; + else + return 0; +} + +int +PQfmod(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].atttypmod; + else + return 0; +} + +char * +PQcmdStatus(PGresult *res) +{ + if (!res) + return NULL; + return res->cmdStatus; +} + +/* + * PQoidStatus - + * if the last command was an INSERT, return the oid string + * if not, return "" + */ +char * +PQoidStatus(const PGresult *res) +{ + /* + * This must be enough to hold the result. Don't laugh, this is better + * than what this function used to do. + */ + static char buf[24]; + + size_t len; + + if (!res || !res->cmdStatus || strncmp(res->cmdStatus, "INSERT ", 7) != 0) + return ""; + + len = strspn(res->cmdStatus + 7, "0123456789"); + if (len > 23) + len = 23; + strncpy(buf, res->cmdStatus + 7, len); + buf[len] = '\0'; + + return buf; +} + +/* + * PQoidValue - + * a perhaps preferable form of the above which just returns + * an Oid type + */ +Oid +PQoidValue(const PGresult *res) +{ + char *endptr = NULL; + unsigned long result; + + if (!res || + !res->cmdStatus || + strncmp(res->cmdStatus, "INSERT ", 7) != 0 || + res->cmdStatus[7] < '0' || + res->cmdStatus[7] > '9') + return InvalidOid; + + result = strtoul(res->cmdStatus + 7, &endptr, 10); + + if (!endptr || (*endptr != ' ' && *endptr != '\0')) + return InvalidOid; + else + return (Oid) result; +} + + +/* + * PQcmdTuples - + * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return + * a string containing the number of inserted/affected tuples. If not, + * return "". + * + * XXX: this should probably return an int + */ +char * +PQcmdTuples(PGresult *res) +{ + char *p, + *c; + + if (!res) + return ""; + + if (strncmp(res->cmdStatus, "INSERT ", 7) == 0) + { + p = res->cmdStatus + 7; + /* INSERT: skip oid and space */ + while (*p && *p != ' ') + p++; + if (*p == 0) + goto interpret_error; /* no space? */ + p++; + } + else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 || + strncmp(res->cmdStatus, "DELETE ", 7) == 0 || + strncmp(res->cmdStatus, "UPDATE ", 7) == 0) + p = res->cmdStatus + 7; + else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) + p = res->cmdStatus + 6; + else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 || + strncmp(res->cmdStatus, "COPY ", 5) == 0) + p = res->cmdStatus + 5; + else + return ""; + + /* check that we have an integer (at least one digit, nothing else) */ + for (c = p; *c; c++) + { + if (!isdigit((unsigned char) *c)) + goto interpret_error; + } + if (c == p) + goto interpret_error; + + return p; + +interpret_error: + pqInternalNotice(&res->noticeHooks, + "could not interpret result from server: %s", + res->cmdStatus); + return ""; +} + +/* + * PQgetvalue: + * return the value of field 'field_num' of row 'tup_num' + */ +char * +PQgetvalue(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return NULL; + return res->tuples[tup_num][field_num].value; +} + +/* PQgetlength: + * returns the actual length of a field value in bytes. + */ +int +PQgetlength(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return 0; + if (res->tuples[tup_num][field_num].len != NULL_LEN) + return res->tuples[tup_num][field_num].len; + else + return 0; +} + +/* PQgetisnull: + * returns the null status of a field value. + */ +int +PQgetisnull(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return 1; /* pretend it is null */ + if (res->tuples[tup_num][field_num].len == NULL_LEN) + return 1; + else + return 0; +} + +/* PQnparams: + * returns the number of input parameters of a prepared statement. + */ +int +PQnparams(const PGresult *res) +{ + if (!res) + return 0; + return res->numParameters; +} + +/* PQparamtype: + * returns type Oid of the specified statement parameter. + */ +Oid +PQparamtype(const PGresult *res, int param_num) +{ + if (!check_param_number(res, param_num)) + return InvalidOid; + if (res->paramDescs) + return res->paramDescs[param_num].typid; + else + return InvalidOid; +} + + +/* PQsetnonblocking: + * sets the PGconn's database connection non-blocking if the arg is TRUE + * or makes it blocking if the arg is FALSE, this will not protect + * you from PQexec(), you'll only be safe when using the non-blocking API. + * Needs to be called only on a connected database connection. + */ +int +PQsetnonblocking(PGconn *conn, int arg) +{ + bool barg; + + if (!conn || conn->status == CONNECTION_BAD) + return -1; + + barg = (arg ? TRUE : FALSE); + + /* early out if the socket is already in the state requested */ + if (barg == conn->nonblocking) + return 0; + + /* + * to guarantee constancy for flushing/query/result-polling behavior we + * need to flush the send queue at this point in order to guarantee proper + * behavior. this is ok because either they are making a transition _from_ + * or _to_ blocking mode, either way we can block them. + */ + /* if we are going from blocking to non-blocking flush here */ + if (pqFlush(conn)) + return -1; + + conn->nonblocking = barg; + + return 0; +} + +/* + * return the blocking status of the database connection + * TRUE == nonblocking, FALSE == blocking + */ +int +PQisnonblocking(const PGconn *conn) +{ + return pqIsnonblocking(conn); +} + +/* libpq is thread-safe? */ +int +PQisthreadsafe(void) +{ +#ifdef ENABLE_THREAD_SAFETY + return true; +#else + return false; +#endif +} + + +/* try to force data out, really only useful for non-blocking users */ +int +PQflush(PGconn *conn) +{ + return pqFlush(conn); +} + + +/* + * PQfreemem - safely frees memory allocated + * + * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6) + * Used for freeing memory from PQescapeByte()a/PQunescapeBytea() + */ +void +PQfreemem(void *ptr) +{ + free(ptr); +} + +/* + * PQfreeNotify - free's the memory associated with a PGnotify + * + * This function is here only for binary backward compatibility. + * New code should use PQfreemem(). A macro will automatically map + * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24 + */ + +#undef PQfreeNotify +void PQfreeNotify(PGnotify *notify); + +void +PQfreeNotify(PGnotify *notify) +{ + PQfreemem(notify); +} + + +/* + * Escaping arbitrary strings to get valid SQL literal strings. + * + * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\". + * + * length is the length of the source string. (Note: if a terminating NUL + * is encountered sooner, PQescapeString stops short of "length"; the behavior + * is thus rather like strncpy.) + * + * For safety the buffer at "to" must be at least 2*length + 1 bytes long. + * A terminating NUL character is added to the output string, whether the + * input is NUL-terminated or not. + * + * Returns the actual length of the output (not counting the terminating NUL). + */ +static size_t +PQescapeStringInternal(PGconn *conn, + char *to, const char *from, size_t length, + int *error, + int encoding, bool std_strings) +{ + const char *source = from; + char *target = to; + size_t remaining = length; + + if (error) + *error = 0; + + while (remaining > 0 && *source != '\0') + { + char c = *source; + int len; + int i; + + /* Fast path for plain ASCII */ + if (!IS_HIGHBIT_SET(c)) + { + /* Apply quoting if needed */ + if (SQL_STR_DOUBLE(c, !std_strings)) + *target++ = c; + /* Copy the character */ + *target++ = c; + source++; + remaining--; + continue; + } + + /* Slow path for possible multibyte characters */ + len = pg_encoding_mblen(encoding, source); + + /* Copy the character */ + for (i = 0; i < len; i++) + { + if (remaining == 0 || *source == '\0') + break; + *target++ = *source++; + remaining--; + } + + /* + * If we hit premature end of string (ie, incomplete multibyte + * character), try to pad out to the correct length with spaces. We + * may not be able to pad completely, but we will always be able to + * insert at least one pad space (since we'd not have quoted a + * multibyte character). This should be enough to make a string that + * the server will error out on. + */ + if (i < len) + { + if (error) + *error = 1; + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); + for (; i < len; i++) + { + if (((size_t) (target - to)) / 2 >= length) + break; + *target++ = ' '; + } + break; + } + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +size_t +PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error) +{ + if (!conn) + { + /* force empty-string result */ + *to = '\0'; + if (error) + *error = 1; + return 0; + } + return PQescapeStringInternal(conn, to, from, length, error, + conn->client_encoding, + conn->std_strings); +} + +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + return PQescapeStringInternal(NULL, to, from, length, NULL, + static_client_encoding, + static_std_strings); +} + + +/* + * Escape arbitrary strings. If as_ident is true, we escape the result + * as an identifier; if false, as a literal. The result is returned in + * a newly allocated buffer. If we fail due to an encoding violation or out + * of memory condition, we return NULL, storing an error message into conn. + */ +static char * +PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) +{ + const char *s; + char *result; + char *rp; + int num_quotes = 0; /* single or double, depending on as_ident */ + int num_backslashes = 0; + int input_len; + int result_size; + char quote_char = as_ident ? '"' : '\''; + + /* We must have a connection, else fail immediately. */ + if (!conn) + return NULL; + + /* Scan the string for characters that must be escaped. */ + for (s = str; (s - str) < len && *s != '\0'; ++s) + { + if (*s == quote_char) + ++num_quotes; + else if (*s == '\\') + ++num_backslashes; + else if (IS_HIGHBIT_SET(*s)) + { + int charlen; + + /* Slow path for possible multibyte characters */ + charlen = pg_encoding_mblen(conn->client_encoding, s); + + /* Multibyte character overruns allowable length. */ + if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); + return NULL; + } + + /* Adjust s, bearing in mind that for loop will increment it. */ + s += charlen - 1; + } + } + + /* Allocate output buffer. */ + input_len = s - str; + result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */ + if (!as_ident && num_backslashes > 0) + result_size += num_backslashes + 2; + result = rp = (char *) malloc(result_size); + if (rp == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + + /* + * If we are escaping a literal that contains backslashes, we use the + * escape string syntax so that the result is correct under either value + * of standard_conforming_strings. We also emit a leading space in this + * case, to guard against the possibility that the result might be + * interpolated immediately following an identifier. + */ + if (!as_ident && num_backslashes > 0) + { + *rp++ = ' '; + *rp++ = 'E'; + } + + /* Opening quote. */ + *rp++ = quote_char; + + /* + * Use fast path if possible. + * + * We've already verified that the input string is well-formed in the + * current encoding. If it contains no quotes and, in the case of + * literal-escaping, no backslashes, then we can just copy it directly to + * the output buffer, adding the necessary quotes. + * + * If not, we must rescan the input and process each character + * individually. + */ + if (num_quotes == 0 && (num_backslashes == 0 || as_ident)) + { + memcpy(rp, str, input_len); + rp += input_len; + } + else + { + for (s = str; s - str < input_len; ++s) + { + if (*s == quote_char || (!as_ident && *s == '\\')) + { + *rp++ = *s; + *rp++ = *s; + } + else if (!IS_HIGHBIT_SET(*s)) + *rp++ = *s; + else + { + int i = pg_encoding_mblen(conn->client_encoding, s); + + while (1) + { + *rp++ = *s; + if (--i == 0) + break; + ++s; /* for loop will provide the final increment */ + } + } + } + } + + /* Closing quote and terminating NUL. */ + *rp++ = quote_char; + *rp = '\0'; + + return result; +} + +char * +PQescapeLiteral(PGconn *conn, const char *str, size_t len) +{ + return PQescapeInternal(conn, str, len, false); +} + +char * +PQescapeIdentifier(PGconn *conn, const char *str, size_t len) +{ + return PQescapeInternal(conn, str, len, true); +} + +/* HEX encoding support for bytea */ +static const char hextbl[] = "0123456789abcdef"; + +static const int8 hexlookup[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static inline char +get_hex(char c) +{ + int res = -1; + + if (c > 0 && c < 127) + res = hexlookup[(unsigned char) c]; + + return (char) res; +} + + +/* + * PQescapeBytea - converts from binary string to the + * minimal encoding necessary to include the string in an SQL + * INSERT statement with a bytea type column as the target. + * + * We can use either hex or escape (traditional) encoding. + * In escape mode, the following transformations are applied: + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == '' + * '\\' == ASCII 92 == \\ + * anything < 0x20, or > 0x7e ---> \ooo + * (where ooo is an octal expression) + * + * If not std_strings, all backslashes sent to the output are doubled. + */ +static unsigned char * +PQescapeByteaInternal(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length, bool std_strings, bool use_hex) +{ + const unsigned char *vp; + unsigned char *rp; + unsigned char *result; + size_t i; + size_t len; + size_t bslash_len = (std_strings ? 1 : 2); + + /* + * empty string has 1 char ('\0') + */ + len = 1; + + if (use_hex) + { + len += bslash_len + 1 + 2 * from_length; + } + else + { + vp = from; + for (i = from_length; i > 0; i--, vp++) + { + if (*vp < 0x20 || *vp > 0x7e) + len += bslash_len + 3; + else if (*vp == '\'') + len += 2; + else if (*vp == '\\') + len += bslash_len + bslash_len; + else + len++; + } + } + + *to_length = len; + rp = result = (unsigned char *) malloc(len); + if (rp == NULL) + { + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + + if (use_hex) + { + if (!std_strings) + *rp++ = '\\'; + *rp++ = '\\'; + *rp++ = 'x'; + } + + vp = from; + for (i = from_length; i > 0; i--, vp++) + { + unsigned char c = *vp; + + if (use_hex) + { + *rp++ = hextbl[(c >> 4) & 0xF]; + *rp++ = hextbl[c & 0xF]; + } + else if (c < 0x20 || c > 0x7e) + { + if (!std_strings) + *rp++ = '\\'; + *rp++ = '\\'; + *rp++ = (c >> 6) + '0'; + *rp++ = ((c >> 3) & 07) + '0'; + *rp++ = (c & 07) + '0'; + } + else if (c == '\'') + { + *rp++ = '\''; + *rp++ = '\''; + } + else if (c == '\\') + { + if (!std_strings) + { + *rp++ = '\\'; + *rp++ = '\\'; + } + *rp++ = '\\'; + *rp++ = '\\'; + } + else + *rp++ = c; + } + *rp = '\0'; + + return result; +} + +unsigned char * +PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length) +{ + if (!conn) + return NULL; + return PQescapeByteaInternal(conn, from, from_length, to_length, + conn->std_strings, + (conn->sversion >= 90000)); +} + +unsigned char * +PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length) +{ + return PQescapeByteaInternal(NULL, from, from_length, to_length, + static_std_strings, + false /* can't use hex */ ); +} + + +#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3') +#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7') +#define OCTVAL(CH) ((CH) - '0') + +/* + * PQunescapeBytea - converts the null terminated string representation + * of a bytea, strtext, into binary, filling a buffer. It returns a + * pointer to the buffer (or NULL on error), and the size of the + * buffer in retbuflen. The pointer may subsequently be used as an + * argument to the function PQfreemem. + * + * The following transformations are made: + * \\ == ASCII 92 == \ + * \ooo == a byte whose value = ooo (ooo is an octal number) + * \x == x (x is any character not matched by the above transformations) + */ +unsigned char * +PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen) +{ + size_t strtextlen, + buflen; + unsigned char *buffer, + *tmpbuf; + size_t i, + j; + + if (strtext == NULL) + return NULL; + + strtextlen = strlen((const char *) strtext); + + if (strtext[0] == '\\' && strtext[1] == 'x') + { + const unsigned char *s; + unsigned char *p; + + buflen = (strtextlen - 2) / 2; + /* Avoid unportable malloc(0) */ + buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1); + if (buffer == NULL) + return NULL; + + s = strtext + 2; + p = buffer; + while (*s) + { + char v1, + v2; + + /* + * Bad input is silently ignored. Note that this includes + * whitespace between hex pairs, which is allowed by byteain. + */ + v1 = get_hex(*s++); + if (!*s || v1 == (char) -1) + continue; + v2 = get_hex(*s++); + if (v2 != (char) -1) + *p++ = (v1 << 4) | v2; + } + + buflen = p - buffer; + } + else + { + /* + * Length of input is max length of output, but add one to avoid + * unportable malloc(0) if input is zero-length. + */ + buffer = (unsigned char *) malloc(strtextlen + 1); + if (buffer == NULL) + return NULL; + + for (i = j = 0; i < strtextlen;) + { + switch (strtext[i]) + { + case '\\': + i++; + if (strtext[i] == '\\') + buffer[j++] = strtext[i++]; + else + { + if ((ISFIRSTOCTDIGIT(strtext[i])) && + (ISOCTDIGIT(strtext[i + 1])) && + (ISOCTDIGIT(strtext[i + 2]))) + { + int byte; + + byte = OCTVAL(strtext[i++]); + byte = (byte << 3) + OCTVAL(strtext[i++]); + byte = (byte << 3) + OCTVAL(strtext[i++]); + buffer[j++] = byte; + } + } + + /* + * Note: if we see '\' followed by something that isn't a + * recognized escape sequence, we loop around having done + * nothing except advance i. Therefore the something will + * be emitted as ordinary data on the next cycle. Corner + * case: '\' at end of string will just be discarded. + */ + break; + + default: + buffer[j++] = strtext[i++]; + break; + } + } + buflen = j; /* buflen is the length of the dequoted data */ + } + + /* Shrink the buffer to be no larger than necessary */ + /* +1 avoids unportable behavior when buflen==0 */ + tmpbuf = realloc(buffer, buflen + 1); + + /* It would only be a very brain-dead realloc that could fail, but... */ + if (!tmpbuf) + { + free(buffer); + return NULL; + } + + *retbuflen = buflen; + return tmpbuf; +} diff --git a/src/libpq/fe-lobj.c b/src/libpq/fe-lobj.c new file mode 100644 index 00000000..27f93e6a --- /dev/null +++ b/src/libpq/fe-lobj.c @@ -0,0 +1,844 @@ +/*------------------------------------------------------------------------- + * + * fe-lobj.c + * Front-end large object interface + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-lobj.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 +/* + * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h + * must be included first on MS C. Might as well do it for all WIN32's + * here. + */ +#include +#endif + +#include "postgres_fe.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#endif + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "libpq/libpq-fs.h" /* must come after sys/stat.h */ + +#define LO_BUFSIZE 8192 + +static int lo_initialize(PGconn *conn); + +static Oid + lo_import_internal(PGconn *conn, const char *filename, const Oid oid); + +/* + * lo_open + * opens an existing large object + * + * returns the file descriptor for use in later lo_* calls + * return -1 upon failure. + */ +int +lo_open(PGconn *conn, Oid lobjId, int mode) +{ + int fd; + int result_len; + PQArgBlock argv[2]; + PGresult *res; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = mode; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return fd; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_close + * closes an existing large object + * + * returns 0 upon success + * returns -1 upon failure. + */ +int +lo_close(PGconn *conn, int fd) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + res = PQfn(conn, conn->lobjfuncs->fn_lo_close, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_truncate + * truncates an existing large object to the given size + * + * returns 0 upon success + * returns -1 upon failure + */ +int +lo_truncate(PGconn *conn, int fd, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int retval; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + /* Must check this on-the-fly because it's not there pre-8.3 */ + if (conn->lobjfuncs->fn_lo_truncate == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_truncate\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = len; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate, + &retval, &result_len, 1, argv, 2); + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + + +/* + * lo_read + * read len bytes of the large object into buf + * + * returns the number of bytes read, or -1 on failure. + * the CALLER must have allocated enough space to hold the result returned + */ + +int +lo_read(PGconn *conn, int fd, char *buf, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = len; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_read, + (int *) buf, &result_len, 0, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return result_len; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_write + * write len bytes of buf into the large object fd + * + * returns the number of bytes written, or -1 on failure. + */ +int +lo_write(PGconn *conn, int fd, const char *buf, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int result_len; + int retval; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + if (len <= 0) + return 0; + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 0; + argv[1].len = len; + argv[1].u.ptr = (int *) buf; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_write, + &retval, &result_len, 1, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_lseek + * change the current read or write location on a large object + * currently, only L_SET is a legal value for whence + * + */ + +int +lo_lseek(PGconn *conn, int fd, int offset, int whence) +{ + PQArgBlock argv[3]; + PGresult *res; + int retval; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = offset; + + argv[2].isint = 1; + argv[2].len = 4; + argv[2].u.integer = whence; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek, + &retval, &result_len, 1, argv, 3); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_creat + * create a new large object + * the mode is ignored (once upon a time it had a use) + * + * returns the oid of the large object created or + * InvalidOid upon failure + */ +Oid +lo_creat(PGconn *conn, int mode) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return InvalidOid; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = mode; + res = PQfn(conn, conn->lobjfuncs->fn_lo_creat, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return (Oid) retval; + } + else + { + PQclear(res); + return InvalidOid; + } +} + +/* + * lo_create + * create a new large object + * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create + * + * returns the oid of the large object created or + * InvalidOid upon failure + */ +Oid +lo_create(PGconn *conn, Oid lobjId) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return InvalidOid; + } + + /* Must check this on-the-fly because it's not there pre-8.1 */ + if (conn->lobjfuncs->fn_lo_create == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_create\n")); + return InvalidOid; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + res = PQfn(conn, conn->lobjfuncs->fn_lo_create, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return (Oid) retval; + } + else + { + PQclear(res); + return InvalidOid; + } +} + + +/* + * lo_tell + * returns the current seek location of the large object + * + */ + +int +lo_tell(PGconn *conn, int fd) +{ + int retval; + PQArgBlock argv[1]; + PGresult *res; + int result_len; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_tell, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_unlink + * delete a file + * + */ + +int +lo_unlink(PGconn *conn, Oid lobjId) +{ + PQArgBlock argv[1]; + PGresult *res; + int result_len; + int retval; + + if (conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_import - + * imports a file as an (inversion) large object. + * + * returns the oid of that object upon success, + * returns InvalidOid upon failure + */ + +Oid +lo_import(PGconn *conn, const char *filename) +{ + return lo_import_internal(conn, filename, InvalidOid); +} + +/* + * lo_import_with_oid - + * imports a file as an (inversion) large object. + * large object id can be specified. + * + * returns the oid of that object upon success, + * returns InvalidOid upon failure + */ + +Oid +lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId) +{ + return lo_import_internal(conn, filename, lobjId); +} + +static Oid +lo_import_internal(PGconn *conn, const char *filename, const Oid oid) +{ + int fd; + int nbytes, + tmp; + char buf[LO_BUFSIZE]; + Oid lobjOid; + int lobj; + char sebuf[256]; + + /* + * open the file to be read in + */ + fd = open(filename, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + { /* error */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + return InvalidOid; + } + + /* + * create an inversion object + */ + if (oid == InvalidOid) + lobjOid = lo_creat(conn, INV_READ | INV_WRITE); + else + lobjOid = lo_create(conn, oid); + + if (lobjOid == InvalidOid) + { + /* we assume lo_create() already set a suitable error message */ + (void) close(fd); + return InvalidOid; + } + + lobj = lo_open(conn, lobjOid, INV_WRITE); + if (lobj == -1) + { + /* we assume lo_open() already set a suitable error message */ + (void) close(fd); + return InvalidOid; + } + + /* + * read in from the file and write to the large object + */ + while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) + { + tmp = lo_write(conn, lobj, buf, nbytes); + if (tmp != nbytes) + { + /* + * If lo_write() failed, we are now in an aborted transaction so + * there's no need for lo_close(); furthermore, if we tried it + * we'd overwrite the useful error result with a useless one. So + * just nail the doors shut and get out of town. + */ + (void) close(fd); + return InvalidOid; + } + } + + if (nbytes < 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read from file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + lobjOid = InvalidOid; + } + + (void) close(fd); + + if (lo_close(conn, lobj) != 0) + { + /* we assume lo_close() already set a suitable error message */ + return InvalidOid; + } + + return lobjOid; +} + +/* + * lo_export - + * exports an (inversion) large object. + * returns -1 upon failure, 1 if OK + */ +int +lo_export(PGconn *conn, Oid lobjId, const char *filename) +{ + int result = 1; + int fd; + int nbytes, + tmp; + char buf[LO_BUFSIZE]; + int lobj; + char sebuf[256]; + + /* + * open the large object. + */ + lobj = lo_open(conn, lobjId, INV_READ); + if (lobj == -1) + { + /* we assume lo_open() already set a suitable error message */ + return -1; + } + + /* + * create the file to be written to + */ + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); + if (fd < 0) + { /* error */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + (void) lo_close(conn, lobj); + return -1; + } + + /* + * read in from the large object and write to the file + */ + while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) + { + tmp = write(fd, buf, nbytes); + if (tmp != nbytes) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not write to file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + (void) lo_close(conn, lobj); + (void) close(fd); + return -1; + } + } + + /* + * If lo_read() failed, we are now in an aborted transaction so there's no + * need for lo_close(); furthermore, if we tried it we'd overwrite the + * useful error result with a useless one. So skip lo_close() if we got a + * failure result. + */ + if (nbytes < 0 || + lo_close(conn, lobj) != 0) + { + /* assume lo_read() or lo_close() left a suitable error message */ + result = -1; + } + + if (close(fd)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not write to file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + result = -1; + } + + return result; +} + + +/* + * lo_initialize + * + * Initialize the large object interface for an existing connection. + * We ask the backend about the functions OID's in pg_proc for all + * functions that are required for large object operations. + */ +static int +lo_initialize(PGconn *conn) +{ + PGresult *res; + PGlobjfuncs *lobjfuncs; + int n; + const char *query; + const char *fname; + Oid foid; + + /* + * Allocate the structure to hold the functions OID's + */ + lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs)); + if (lobjfuncs == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -1; + } + MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs)); + + /* + * Execute the query to get all the functions at once. In 7.3 and later + * we need to be schema-safe. lo_create only exists in 8.1 and up. + * lo_truncate only exists in 8.3 and up. + */ + if (conn->sversion >= 70300) + query = "select proname, oid from pg_catalog.pg_proc " + "where proname in (" + "'lo_open', " + "'lo_close', " + "'lo_creat', " + "'lo_create', " + "'lo_unlink', " + "'lo_lseek', " + "'lo_tell', " + "'lo_truncate', " + "'loread', " + "'lowrite') " + "and pronamespace = (select oid from pg_catalog.pg_namespace " + "where nspname = 'pg_catalog')"; + else + query = "select proname, oid from pg_proc " + "where proname = 'lo_open' " + "or proname = 'lo_close' " + "or proname = 'lo_creat' " + "or proname = 'lo_unlink' " + "or proname = 'lo_lseek' " + "or proname = 'lo_tell' " + "or proname = 'loread' " + "or proname = 'lowrite'"; + + res = PQexec(conn, query); + if (res == NULL) + { + free(lobjfuncs); + return -1; + } + + if (res->resultStatus != PGRES_TUPLES_OK) + { + free(lobjfuncs); + PQclear(res); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("query to initialize large object functions did not return data\n")); + return -1; + } + + /* + * Examine the result and put the OID's into the struct + */ + for (n = 0; n < PQntuples(res); n++) + { + fname = PQgetvalue(res, n, 0); + foid = (Oid) atoi(PQgetvalue(res, n, 1)); + if (!strcmp(fname, "lo_open")) + lobjfuncs->fn_lo_open = foid; + else if (!strcmp(fname, "lo_close")) + lobjfuncs->fn_lo_close = foid; + else if (!strcmp(fname, "lo_creat")) + lobjfuncs->fn_lo_creat = foid; + else if (!strcmp(fname, "lo_create")) + lobjfuncs->fn_lo_create = foid; + else if (!strcmp(fname, "lo_unlink")) + lobjfuncs->fn_lo_unlink = foid; + else if (!strcmp(fname, "lo_lseek")) + lobjfuncs->fn_lo_lseek = foid; + else if (!strcmp(fname, "lo_tell")) + lobjfuncs->fn_lo_tell = foid; + else if (!strcmp(fname, "lo_truncate")) + lobjfuncs->fn_lo_truncate = foid; + else if (!strcmp(fname, "loread")) + lobjfuncs->fn_lo_read = foid; + else if (!strcmp(fname, "lowrite")) + lobjfuncs->fn_lo_write = foid; + } + + PQclear(res); + + /* + * Finally check that we really got all large object interface functions + */ + if (lobjfuncs->fn_lo_open == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_open\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_close == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_close\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_creat == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_creat\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_unlink == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_unlink\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_lseek == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_lseek\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_tell == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_tell\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_read == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function loread\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_write == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lowrite\n")); + free(lobjfuncs); + return -1; + } + + /* + * Put the structure into the connection control + */ + conn->lobjfuncs = lobjfuncs; + return 0; +} diff --git a/src/libpq/fe-misc.c b/src/libpq/fe-misc.c new file mode 100644 index 00000000..edf7682e --- /dev/null +++ b/src/libpq/fe-misc.c @@ -0,0 +1,1187 @@ +/*------------------------------------------------------------------------- + * + * FILE + * fe-misc.c + * + * DESCRIPTION + * miscellaneous useful functions + * + * The communication routines here are analogous to the ones in + * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate + * in the considerably different environment of the frontend libpq. + * In particular, we work with a bare nonblock-mode socket, rather than + * a stdio stream, so that we can avoid unwanted blocking of the application. + * + * XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart + * will cause repeat printouts. + * + * We must speak the same transmitted data representations as the backend + * routines. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/interfaces/libpq/fe-misc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include + +#include +#include + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "pqsignal.h" +#include "mb/pg_wchar.h" +#include "pg_config_paths.h" + + +static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn); +static int pqSendSome(PGconn *conn, int len); +static int pqSocketCheck(PGconn *conn, int forRead, int forWrite, + time_t end_time); +static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); + +/* + * PQlibVersion: return the libpq version number + */ +int +PQlibVersion(void) +{ + return PG_VERSION_NUM; +} + +/* + * fputnbytes: print exactly N bytes to a file + * + * We avoid using %.*s here because it can misbehave if the data + * is not valid in what libc thinks is the prevailing encoding. + */ +static void +fputnbytes(FILE *f, const char *str, size_t n) +{ + while (n-- > 0) + fputc(*str++, f); +} + + +/* + * pqGetc: get 1 character from the connection + * + * All these routines return 0 on success, EOF on error. + * Note that for the Get routines, EOF only means there is not enough + * data in the buffer, not that there is necessarily a hard error. + */ +int +pqGetc(char *result, PGconn *conn) +{ + if (conn->inCursor >= conn->inEnd) + return EOF; + + *result = conn->inBuffer[conn->inCursor++]; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend> %c\n", *result); + + return 0; +} + + +/* + * pqPutc: write 1 char to the current message + */ +int +pqPutc(char c, PGconn *conn) +{ + if (pqPutMsgBytes(&c, 1, conn)) + return EOF; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> %c\n", c); + + return 0; +} + + +/* + * pqGets[_append]: + * get a null-terminated string from the connection, + * and store it in an expansible PQExpBuffer. + * If we run out of memory, all of the string is still read, + * but the excess characters are silently discarded. + */ +static int +pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer) +{ + /* Copy conn data to locals for faster search loop */ + char *inBuffer = conn->inBuffer; + int inCursor = conn->inCursor; + int inEnd = conn->inEnd; + int slen; + + while (inCursor < inEnd && inBuffer[inCursor]) + inCursor++; + + if (inCursor >= inEnd) + return EOF; + + slen = inCursor - conn->inCursor; + + if (resetbuffer) + resetPQExpBuffer(buf); + + appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen); + + conn->inCursor = ++inCursor; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend> \"%s\"\n", + buf->data); + + return 0; +} + +int +pqGets(PQExpBuffer buf, PGconn *conn) +{ + return pqGets_internal(buf, conn, true); +} + +int +pqGets_append(PQExpBuffer buf, PGconn *conn) +{ + return pqGets_internal(buf, conn, false); +} + + +/* + * pqPuts: write a null-terminated string to the current message + */ +int +pqPuts(const char *s, PGconn *conn) +{ + if (pqPutMsgBytes(s, strlen(s) + 1, conn)) + return EOF; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s); + + return 0; +} + +/* + * pqGetnchar: + * get a string of exactly len bytes in buffer s, no null termination + */ +int +pqGetnchar(char *s, size_t len, PGconn *conn) +{ + if (len > (size_t) (conn->inEnd - conn->inCursor)) + return EOF; + + memcpy(s, conn->inBuffer + conn->inCursor, len); + /* no terminating null */ + + conn->inCursor += len; + + if (conn->Pfdebug) + { + fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len); + fputnbytes(conn->Pfdebug, s, len); + fprintf(conn->Pfdebug, "\n"); + } + + return 0; +} + +/* + * pqPutnchar: + * write exactly len bytes to the current message + */ +int +pqPutnchar(const char *s, size_t len, PGconn *conn) +{ + if (pqPutMsgBytes(s, len, conn)) + return EOF; + + if (conn->Pfdebug) + { + fprintf(conn->Pfdebug, "To backend> "); + fputnbytes(conn->Pfdebug, s, len); + fprintf(conn->Pfdebug, "\n"); + } + + return 0; +} + +/* + * pqGetInt + * read a 2 or 4 byte integer and convert from network byte order + * to local byte order + */ +int +pqGetInt(int *result, size_t bytes, PGconn *conn) +{ + uint16 tmp2; + uint32 tmp4; + + switch (bytes) + { + case 2: + if (conn->inCursor + 2 > conn->inEnd) + return EOF; + memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2); + conn->inCursor += 2; + *result = (int) ntohs(tmp2); + break; + case 4: + if (conn->inCursor + 4 > conn->inEnd) + return EOF; + memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4); + conn->inCursor += 4; + *result = (int) ntohl(tmp4); + break; + default: + pqInternalNotice(&conn->noticeHooks, + "integer of size %lu not supported by pqGetInt", + (unsigned long) bytes); + return EOF; + } + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result); + + return 0; +} + +/* + * pqPutInt + * write an integer of 2 or 4 bytes, converting from host byte order + * to network byte order. + */ +int +pqPutInt(int value, size_t bytes, PGconn *conn) +{ + uint16 tmp2; + uint32 tmp4; + + switch (bytes) + { + case 2: + tmp2 = htons((uint16) value); + if (pqPutMsgBytes((const char *) &tmp2, 2, conn)) + return EOF; + break; + case 4: + tmp4 = htonl((uint32) value); + if (pqPutMsgBytes((const char *) &tmp4, 4, conn)) + return EOF; + break; + default: + pqInternalNotice(&conn->noticeHooks, + "integer of size %lu not supported by pqPutInt", + (unsigned long) bytes); + return EOF; + } + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value); + + return 0; +} + +/* + * Make sure conn's output buffer can hold bytes_needed bytes (caller must + * include already-stored data into the value!) + * + * Returns 0 on success, EOF if failed to enlarge buffer + */ +int +pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn) +{ + int newsize = conn->outBufSize; + char *newbuf; + + if (bytes_needed <= (size_t) newsize) + return 0; + + /* + * If we need to enlarge the buffer, we first try to double it in size; if + * that doesn't work, enlarge in multiples of 8K. This avoids thrashing + * the malloc pool by repeated small enlargements. + * + * Note: tests for newsize > 0 are to catch integer overflow. + */ + do + { + newsize *= 2; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->outBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->outBuffer = newbuf; + conn->outBufSize = newsize; + return 0; + } + } + + newsize = conn->outBufSize; + do + { + newsize += 8192; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->outBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->outBuffer = newbuf; + conn->outBufSize = newsize; + return 0; + } + } + + /* realloc failed. Probably out of memory */ + printfPQExpBuffer(&conn->errorMessage, + "cannot allocate memory for output buffer\n"); + return EOF; +} + +/* + * Make sure conn's input buffer can hold bytes_needed bytes (caller must + * include already-stored data into the value!) + * + * Returns 0 on success, EOF if failed to enlarge buffer + */ +int +pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn) +{ + int newsize = conn->inBufSize; + char *newbuf; + + if (bytes_needed <= (size_t) newsize) + return 0; + + /* + * If we need to enlarge the buffer, we first try to double it in size; if + * that doesn't work, enlarge in multiples of 8K. This avoids thrashing + * the malloc pool by repeated small enlargements. + * + * Note: tests for newsize > 0 are to catch integer overflow. + */ + do + { + newsize *= 2; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->inBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->inBuffer = newbuf; + conn->inBufSize = newsize; + return 0; + } + } + + newsize = conn->inBufSize; + do + { + newsize += 8192; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->inBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->inBuffer = newbuf; + conn->inBufSize = newsize; + return 0; + } + } + + /* realloc failed. Probably out of memory */ + printfPQExpBuffer(&conn->errorMessage, + "cannot allocate memory for input buffer\n"); + return EOF; +} + +/* + * pqPutMsgStart: begin construction of a message to the server + * + * msg_type is the message type byte, or 0 for a message without type byte + * (only startup messages have no type byte) + * + * force_len forces the message to have a length word; otherwise, we add + * a length word if protocol 3. + * + * Returns 0 on success, EOF on error + * + * The idea here is that we construct the message in conn->outBuffer, + * beginning just past any data already in outBuffer (ie, at + * outBuffer+outCount). We enlarge the buffer as needed to hold the message. + * When the message is complete, we fill in the length word (if needed) and + * then advance outCount past the message, making it eligible to send. + * + * The state variable conn->outMsgStart points to the incomplete message's + * length word: it is either outCount or outCount+1 depending on whether + * there is a type byte. If we are sending a message without length word + * (pre protocol 3.0 only), then outMsgStart is -1. The state variable + * conn->outMsgEnd is the end of the data collected so far. + */ +int +pqPutMsgStart(char msg_type, bool force_len, PGconn *conn) +{ + int lenPos; + int endPos; + + /* allow room for message type byte */ + if (msg_type) + endPos = conn->outCount + 1; + else + endPos = conn->outCount; + + /* do we want a length word? */ + if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + lenPos = endPos; + /* allow room for message length */ + endPos += 4; + } + else + lenPos = -1; + + /* make sure there is room for message header */ + if (pqCheckOutBufferSpace(endPos, conn)) + return EOF; + /* okay, save the message type byte if any */ + if (msg_type) + conn->outBuffer[conn->outCount] = msg_type; + /* set up the message pointers */ + conn->outMsgStart = lenPos; + conn->outMsgEnd = endPos; + /* length word, if needed, will be filled in by pqPutMsgEnd */ + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> Msg %c\n", + msg_type ? msg_type : ' '); + + return 0; +} + +/* + * pqPutMsgBytes: add bytes to a partially-constructed message + * + * Returns 0 on success, EOF on error + */ +static int +pqPutMsgBytes(const void *buf, size_t len, PGconn *conn) +{ + /* make sure there is room for it */ + if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn)) + return EOF; + /* okay, save the data */ + memcpy(conn->outBuffer + conn->outMsgEnd, buf, len); + conn->outMsgEnd += len; + /* no Pfdebug call here, caller should do it */ + return 0; +} + +/* + * pqPutMsgEnd: finish constructing a message and possibly send it + * + * Returns 0 on success, EOF on error + * + * We don't actually send anything here unless we've accumulated at least + * 8K worth of data (the typical size of a pipe buffer on Unix systems). + * This avoids sending small partial packets. The caller must use pqFlush + * when it's important to flush all the data out to the server. + */ +int +pqPutMsgEnd(PGconn *conn) +{ + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n", + conn->outMsgEnd - conn->outCount); + + /* Fill in length word if needed */ + if (conn->outMsgStart >= 0) + { + uint32 msgLen = conn->outMsgEnd - conn->outMsgStart; + + msgLen = htonl(msgLen); + memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4); + } + + /* Make message eligible to send */ + conn->outCount = conn->outMsgEnd; + + if (conn->outCount >= 8192) + { + int toSend = conn->outCount - (conn->outCount % 8192); + + if (pqSendSome(conn, toSend) < 0) + return EOF; + /* in nonblock mode, don't complain if unable to send it all */ + } + + return 0; +} + +/* ---------- + * pqReadData: read more data, if any is available + * Possible return values: + * 1: successfully loaded at least one more byte + * 0: no data is presently available, but no error detected + * -1: error detected (including EOF = connection closure); + * conn->errorMessage set + * NOTE: callers must not assume that pointers or indexes into conn->inBuffer + * remain valid across this call! + * ---------- + */ +int +pqReadData(PGconn *conn) +{ + int someread = 0; + int nread; + + if (conn->sock < 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection not open\n")); + return -1; + } + + /* Left-justify any data in the buffer to make room */ + if (conn->inStart < conn->inEnd) + { + if (conn->inStart > 0) + { + memmove(conn->inBuffer, conn->inBuffer + conn->inStart, + conn->inEnd - conn->inStart); + conn->inEnd -= conn->inStart; + conn->inCursor -= conn->inStart; + conn->inStart = 0; + } + } + else + { + /* buffer is logically empty, reset it */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + } + + /* + * If the buffer is fairly full, enlarge it. We need to be able to enlarge + * the buffer in case a single message exceeds the initial buffer size. We + * enlarge before filling the buffer entirely so as to avoid asking the + * kernel for a partial packet. The magic constant here should be large + * enough for a TCP packet or Unix pipe bufferload. 8K is the usual pipe + * buffer size, so... + */ + if (conn->inBufSize - conn->inEnd < 8192) + { + if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn)) + { + /* + * We don't insist that the enlarge worked, but we need some room + */ + if (conn->inBufSize - conn->inEnd < 100) + return -1; /* errorMessage already set */ + } + } + + /* OK, try to read some data */ +retry3: + nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); + if (nread < 0) + { + if (SOCK_ERRNO == EINTR) + goto retry3; + /* Some systems return EAGAIN/EWOULDBLOCK for no data */ +#ifdef EAGAIN + if (SOCK_ERRNO == EAGAIN) + return someread; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + if (SOCK_ERRNO == EWOULDBLOCK) + return someread; +#endif + /* We might get ECONNRESET here if using TCP and backend died */ +#ifdef ECONNRESET + if (SOCK_ERRNO == ECONNRESET) + goto definitelyFailed; +#endif + /* pqsecure_read set the error message for us */ + return -1; + } + if (nread > 0) + { + conn->inEnd += nread; + + /* + * Hack to deal with the fact that some kernels will only give us back + * 1 packet per recv() call, even if we asked for more and there is + * more available. If it looks like we are reading a long message, + * loop back to recv() again immediately, until we run out of data or + * buffer space. Without this, the block-and-restart behavior of + * libpq's higher levels leads to O(N^2) performance on long messages. + * + * Since we left-justified the data above, conn->inEnd gives the + * amount of data already read in the current message. We consider + * the message "long" once we have acquired 32k ... + */ + if (conn->inEnd > 32768 && + (conn->inBufSize - conn->inEnd) >= 8192) + { + someread = 1; + goto retry3; + } + return 1; + } + + if (someread) + return 1; /* got a zero read after successful tries */ + + /* + * A return value of 0 could mean just that no data is now available, or + * it could mean EOF --- that is, the server has closed the connection. + * Since we have the socket in nonblock mode, the only way to tell the + * difference is to see if select() is saying that the file is ready. + * Grumble. Fortunately, we don't expect this path to be taken much, + * since in normal practice we should not be trying to read data unless + * the file selected for reading already. + * + * In SSL mode it's even worse: SSL_read() could say WANT_READ and then + * data could arrive before we make the pqReadReady() test. So we must + * play dumb and assume there is more data, relying on the SSL layer to + * detect true EOF. + */ + +#ifdef USE_SSL + if (conn->ssl) + return 0; +#endif + + switch (pqReadReady(conn)) + { + case 0: + /* definitely no data available */ + return 0; + case 1: + /* ready for read */ + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + goto definitelyFailed; + } + + /* + * Still not sure that it's EOF, because some data could have just + * arrived. + */ +retry4: + nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); + if (nread < 0) + { + if (SOCK_ERRNO == EINTR) + goto retry4; + /* Some systems return EAGAIN/EWOULDBLOCK for no data */ +#ifdef EAGAIN + if (SOCK_ERRNO == EAGAIN) + return 0; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + if (SOCK_ERRNO == EWOULDBLOCK) + return 0; +#endif + /* We might get ECONNRESET here if using TCP and backend died */ +#ifdef ECONNRESET + if (SOCK_ERRNO == ECONNRESET) + goto definitelyFailed; +#endif + /* pqsecure_read set the error message for us */ + return -1; + } + if (nread > 0) + { + conn->inEnd += nread; + return 1; + } + + /* + * OK, we are getting a zero read even though select() says ready. This + * means the connection has been closed. Cope. Note that errorMessage + * has been set already. + */ +definitelyFailed: + conn->status = CONNECTION_BAD; /* No more connection to backend */ + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + + return -1; +} + +/* + * pqSendSome: send data waiting in the output buffer. + * + * len is how much to try to send (typically equal to outCount, but may + * be less). + * + * Return 0 on success, -1 on failure and 1 when not all data could be sent + * because the socket would block and the connection is non-blocking. + */ +static int +pqSendSome(PGconn *conn, int len) +{ + char *ptr = conn->outBuffer; + int remaining = conn->outCount; + int result = 0; + + if (conn->sock < 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection not open\n")); + return -1; + } + + /* while there's still data to send */ + while (len > 0) + { + int sent; + +#ifndef WIN32 + sent = pqsecure_write(conn, ptr, len); +#else + + /* + * Windows can fail on large sends, per KB article Q201213. The + * failure-point appears to be different in different versions of + * Windows, but 64k should always be safe. + */ + sent = pqsecure_write(conn, ptr, Min(len, 65536)); +#endif + + if (sent < 0) + { + /* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */ + switch (SOCK_ERRNO) + { +#ifdef EAGAIN + case EAGAIN: + break; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: + break; +#endif + case EINTR: + continue; + + default: + /* pqsecure_write set the error message for us */ + + /* + * We used to close the socket here, but that's a bad idea + * since there might be unread data waiting (typically, a + * NOTICE message from the backend telling us it's + * committing hara-kiri...). Leave the socket open until + * pqReadData finds no more data can be read. But abandon + * attempt to send data. + */ + conn->outCount = 0; + return -1; + } + } + else + { + ptr += sent; + len -= sent; + remaining -= sent; + } + + if (len > 0) + { + /* + * We didn't send it all, wait till we can send more. + * + * If the connection is in non-blocking mode we don't wait, but + * return 1 to indicate that data is still pending. + */ + if (pqIsnonblocking(conn)) + { + result = 1; + break; + } + + /* + * There are scenarios in which we can't send data because the + * communications channel is full, but we cannot expect the server + * to clear the channel eventually because it's blocked trying to + * send data to us. (This can happen when we are sending a large + * amount of COPY data, and the server has generated lots of + * NOTICE responses.) To avoid a deadlock situation, we must be + * prepared to accept and buffer incoming data before we try + * again. Furthermore, it is possible that such incoming data + * might not arrive until after we've gone to sleep. Therefore, + * we wait for either read ready or write ready. + */ + if (pqReadData(conn) < 0) + { + result = -1; /* error message already set up */ + break; + } + if (pqWait(TRUE, TRUE, conn)) + { + result = -1; + break; + } + } + } + + /* shift the remaining contents of the buffer */ + if (remaining > 0) + memmove(conn->outBuffer, ptr, remaining); + conn->outCount = remaining; + + return result; +} + + +/* + * pqFlush: send any data waiting in the output buffer + * + * Return 0 on success, -1 on failure and 1 when not all data could be sent + * because the socket would block and the connection is non-blocking. + */ +int +pqFlush(PGconn *conn) +{ + if (conn->Pfdebug) + fflush(conn->Pfdebug); + + if (conn->outCount > 0) + return pqSendSome(conn, conn->outCount); + + return 0; +} + + +/* + * pqWait: wait until we can read or write the connection socket + * + * JAB: If SSL enabled and used and forRead, buffered bytes short-circuit the + * call to select(). + * + * We also stop waiting and return if the kernel flags an exception condition + * on the socket. The actual error condition will be detected and reported + * when the caller tries to read or write the socket. + */ +int +pqWait(int forRead, int forWrite, PGconn *conn) +{ + return pqWaitTimed(forRead, forWrite, conn, (time_t) -1); +} + +/* + * pqWaitTimed: wait, but not past finish_time. + * + * If finish_time is exceeded then we return failure (EOF). This is like + * the response for a kernel exception because we don't want the caller + * to try to read/write in that case. + * + * finish_time = ((time_t) -1) disables the wait limit. + */ +int +pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) +{ + int result; + + result = pqSocketCheck(conn, forRead, forWrite, finish_time); + + if (result < 0) + return EOF; /* errorMessage is already set */ + + if (result == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("timeout expired\n")); + return EOF; + } + + return 0; +} + +/* + * pqReadReady: is select() saying the file is ready to read? + * Returns -1 on failure, 0 if not ready, 1 if ready. + */ +int +pqReadReady(PGconn *conn) +{ + return pqSocketCheck(conn, 1, 0, (time_t) 0); +} + +/* + * pqWriteReady: is select() saying the file is ready to write? + * Returns -1 on failure, 0 if not ready, 1 if ready. + */ +int +pqWriteReady(PGconn *conn) +{ + return pqSocketCheck(conn, 0, 1, (time_t) 0); +} + +/* + * Checks a socket, using poll or select, for data to be read, written, + * or both. Returns >0 if one or more conditions are met, 0 if it timed + * out, -1 if an error occurred. + * + * If SSL is in use, the SSL buffer is checked prior to checking the socket + * for read data directly. + */ +static int +pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) +{ + int result; + + if (!conn) + return -1; + if (conn->sock < 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("socket not open\n")); + return -1; + } + +#ifdef USE_SSL + /* Check for SSL library buffering read bytes */ + if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0) + { + /* short-circuit the select */ + return 1; + } +#endif + + /* We will retry as long as we get EINTR */ + do + result = pqSocketPoll(conn->sock, forRead, forWrite, end_time); + while (result < 0 && SOCK_ERRNO == EINTR); + + if (result < 0) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("select() failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + } + + return result; +} + + +/* + * Check a file descriptor for read and/or write data, possibly waiting. + * If neither forRead nor forWrite are set, immediately return a timeout + * condition (without waiting). Return >0 if condition is met, 0 + * if a timeout occurred, -1 if an error or interrupt occurred. + * + * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking) + * if end_time is 0 (or indeed, any time before now). + */ +static int +pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) +{ + /* We use poll(2) if available, otherwise select(2) */ +#ifdef HAVE_POLL + struct pollfd input_fd; + int timeout_ms; + + if (!forRead && !forWrite) + return 0; + + input_fd.fd = sock; + input_fd.events = POLLERR; + input_fd.revents = 0; + + if (forRead) + input_fd.events |= POLLIN; + if (forWrite) + input_fd.events |= POLLOUT; + + /* Compute appropriate timeout interval */ + if (end_time == ((time_t) -1)) + timeout_ms = -1; + else + { + time_t now = time(NULL); + + if (end_time > now) + timeout_ms = (end_time - now) * 1000; + else + timeout_ms = 0; + } + + return poll(&input_fd, 1, timeout_ms); +#else /* !HAVE_POLL */ + + fd_set input_mask; + fd_set output_mask; + fd_set except_mask; + struct timeval timeout; + struct timeval *ptr_timeout; + + if (!forRead && !forWrite) + return 0; + + FD_ZERO(&input_mask); + FD_ZERO(&output_mask); + FD_ZERO(&except_mask); + if (forRead) + FD_SET(sock, &input_mask); + + if (forWrite) + FD_SET(sock, &output_mask); + FD_SET(sock, &except_mask); + + /* Compute appropriate timeout interval */ + if (end_time == ((time_t) -1)) + ptr_timeout = NULL; + else + { + time_t now = time(NULL); + + if (end_time > now) + timeout.tv_sec = end_time - now; + else + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ptr_timeout = &timeout; + } + + return select(sock + 1, &input_mask, &output_mask, + &except_mask, ptr_timeout); +#endif /* HAVE_POLL */ +} + + +/* + * A couple of "miscellaneous" multibyte related functions. They used + * to be in fe-print.c but that file is doomed. + */ + +/* + * returns the byte length of the word beginning s, using the + * specified encoding. + */ +int +PQmblen(const char *s, int encoding) +{ + return pg_encoding_mblen(encoding, s); +} + +/* + * returns the display length of the word beginning s, using the + * specified encoding. + */ +int +PQdsplen(const char *s, int encoding) +{ + return pg_encoding_dsplen(encoding, s); +} + +/* + * Get encoding id from environment variable PGCLIENTENCODING. + */ +int +PQenv2encoding(void) +{ + char *str; + int encoding = PG_SQL_ASCII; + + str = getenv("PGCLIENTENCODING"); + if (str && *str != '\0') + { + encoding = pg_char_to_encoding(str); + if (encoding < 0) + encoding = PG_SQL_ASCII; + } + return encoding; +} + + +#ifdef ENABLE_NLS + +char * +libpq_gettext(const char *msgid) +{ + static bool already_bound = false; + + if (!already_bound) + { + /* dgettext() preserves errno, but bindtextdomain() doesn't */ +#ifdef WIN32 + int save_errno = GetLastError(); +#else + int save_errno = errno; +#endif + const char *ldir; + + already_bound = true; + /* No relocatable lookup here because the binary could be anywhere */ + ldir = getenv("PGLOCALEDIR"); + if (!ldir) + ldir = LOCALEDIR; + bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir); +#ifdef WIN32 + SetLastError(save_errno); +#else + errno = save_errno; +#endif + } + + return dgettext(PG_TEXTDOMAIN("libpq"), msgid); +} + +#endif /* ENABLE_NLS */ diff --git a/src/libpq/fe-print.c b/src/libpq/fe-print.c new file mode 100644 index 00000000..5fa3be00 --- /dev/null +++ b/src/libpq/fe-print.c @@ -0,0 +1,765 @@ +/*------------------------------------------------------------------------- + * + * 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 + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#else +#ifndef WIN32 +#include +#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, "

%s

\n", po->caption); + else + fprintf(fout, + "

" + "Query retrieved %d rows * %d fields" + "

\n", + nTups, nFields); + } + for (i = 0; i < nTups; i++) + { + if (po->expanded) + { + if (po->html3) + fprintf(fout, + "\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("
%d
\n", fout); + } + if (!po->expanded && (po->align || po->html3)) + { + if (po->html3) + { + if (po->header) + { + if (po->caption) + fprintf(fout, + "\n", + po->tableOpt ? po->tableOpt : "", + po->caption); + else + fprintf(fout, + "
%s
\n", + po->tableOpt ? po->tableOpt : "", nTups, nFields); + } + else + fprintf(fout, "
" + "Retrieved %d rows * %d fields" + "
", 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("
\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, + "%s" + "%s\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("", 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, "%s", + 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("\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("", 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, "%s", + 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("", 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 = malloc(width + 1); + if (!tborder) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + exit(1); + } + for (i = 0; i <= width; i++) + tborder[i] = '-'; + tborder[i] = '\0'; + fprintf(fout, "%s\n", tborder); + } + + for (i = 0; i < nFields; i++) + { + if (PrintAttNames) + { + fprintf(fout, formatString, + TerseOutput ? "" : "|", + PQfname(res, i)); + } + } + + if (PrintAttNames) + { + if (TerseOutput) + fprintf(fout, "\n"); + else + fprintf(fout, "|\n%s\n", tborder); + } + + for (i = 0; i < nTups; i++) + { + for (j = 0; j < nFields; j++) + { + const char *pval = PQgetvalue(res, i, j); + + fprintf(fout, formatString, + TerseOutput ? "" : "|", + pval ? pval : ""); + } + if (TerseOutput) + fprintf(fout, "\n"); + else + fprintf(fout, "|\n%s\n", tborder); + } + } + + 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); +} diff --git a/src/libpq/fe-protocol2.c b/src/libpq/fe-protocol2.c new file mode 100644 index 00000000..77c4d5ac --- /dev/null +++ b/src/libpq/fe-protocol2.c @@ -0,0 +1,1507 @@ +/*------------------------------------------------------------------------- + * + * fe-protocol2.c + * functions that are specific to frontend/backend protocol version 2 + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-protocol2.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + + +static int getRowDescriptions(PGconn *conn); +static int getAnotherTuple(PGconn *conn, bool binary); +static int pqGetErrorNotice2(PGconn *conn, bool isError); +static void checkXactStatus(PGconn *conn, const char *cmdTag); +static int getNotify(PGconn *conn); + + +/* + * pqSetenvPoll + * + * Polls the process of passing the values of a standard set of environment + * variables to the backend. + */ +PostgresPollingStatusType +pqSetenvPoll(PGconn *conn) +{ + PGresult *res; + + 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 */ + case SETENV_STATE_CLIENT_ENCODING_WAIT: + case SETENV_STATE_OPTION_WAIT: + case SETENV_STATE_QUERY1_WAIT: + case SETENV_STATE_QUERY2_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. */ + case SETENV_STATE_CLIENT_ENCODING_SEND: + case SETENV_STATE_OPTION_SEND: + case SETENV_STATE_QUERY1_SEND: + case SETENV_STATE_QUERY2_SEND: + break; + + /* Should we raise an error if called when not active? */ + case SETENV_STATE_IDLE: + return PGRES_POLLING_OK; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "invalid setenv state %c, " + "probably indicative of memory corruption\n" + ), + conn->setenv_state); + goto error_return; + } + + /* We will loop here until there is nothing left to do in this call. */ + for (;;) + { + switch (conn->setenv_state) + { + /* + * The _CLIENT_ENCODING_SEND code is slightly different from + * _OPTION_SEND below (e.g., no getenv() call), which is why a + * different state is used. + */ + case SETENV_STATE_CLIENT_ENCODING_SEND: + { + char setQuery[100]; /* note length limit in + * sprintf below */ + const char *val = conn->client_encoding_initial; + + if (val) + { + if (pg_strcasecmp(val, "default") == 0) + sprintf(setQuery, "SET client_encoding = DEFAULT"); + else + sprintf(setQuery, "SET client_encoding = '%.60s'", + val); +#ifdef CONNECTDEBUG + fprintf(stderr, + "Sending client_encoding with %s\n", + setQuery); +#endif + if (!PQsendQuery(conn, setQuery)) + goto error_return; + + conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT; + } + else + conn->setenv_state = SETENV_STATE_OPTION_SEND; + break; + } + + case SETENV_STATE_OPTION_SEND: + { + /* + * Send SET commands for stuff directed by Environment + * Options. Note: we assume that SET commands won't start + * transaction blocks, even in a 7.3 server with + * autocommit off. + */ + char setQuery[100]; /* note length limit in + * sprintf below */ + + if (conn->next_eo->envName) + { + const char *val; + + if ((val = getenv(conn->next_eo->envName))) + { + if (pg_strcasecmp(val, "default") == 0) + sprintf(setQuery, "SET %s = DEFAULT", + conn->next_eo->pgName); + else + sprintf(setQuery, "SET %s = '%.60s'", + conn->next_eo->pgName, val); +#ifdef CONNECTDEBUG + fprintf(stderr, + "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 move on to querying */ + conn->setenv_state = SETENV_STATE_QUERY1_SEND; + } + break; + } + + case SETENV_STATE_CLIENT_ENCODING_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + PQclear(res); + goto error_return; + } + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so send the next option */ + conn->setenv_state = SETENV_STATE_OPTION_SEND; + } + break; + } + + 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; + } + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so send the next option */ + conn->next_eo++; + conn->setenv_state = SETENV_STATE_OPTION_SEND; + } + break; + } + + case SETENV_STATE_QUERY1_SEND: + { + /* + * Issue query to get information we need. Here we must + * use begin/commit in case autocommit is off by default + * in a 7.3 server. + * + * Note: version() exists in all protocol-2.0-supporting + * backends. In 7.3 it would be safer to write + * pg_catalog.version(), but we can't do that without + * causing problems on older versions. + */ + if (!PQsendQuery(conn, "begin; select version(); end")) + goto error_return; + + conn->setenv_state = SETENV_STATE_QUERY1_WAIT; + return PGRES_POLLING_READING; + } + + case SETENV_STATE_QUERY1_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + char *val; + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + /* ignore begin/commit command results */ + PQclear(res); + continue; + } + + if (PQresultStatus(res) != PGRES_TUPLES_OK || + PQntuples(res) != 1) + { + PQclear(res); + goto error_return; + } + + /* + * Extract server version and save as if + * ParameterStatus + */ + val = PQgetvalue(res, 0, 0); + if (val && strncmp(val, "PostgreSQL ", 11) == 0) + { + char *ptr; + + /* strip off PostgreSQL part */ + val += 11; + + /* + * strip off platform part (scribbles on result, + * naughty naughty) + */ + ptr = strchr(val, ' '); + if (ptr) + *ptr = '\0'; + + pqSaveParameterStatus(conn, "server_version", + val); + } + + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, move to next */ + conn->setenv_state = SETENV_STATE_QUERY2_SEND; + } + break; + } + + case SETENV_STATE_QUERY2_SEND: + { + const char *query; + + /* + * pg_client_encoding does not exist in pre-7.2 servers. + * So we need to be prepared for an error here. Do *not* + * start a transaction block, except in 7.3 servers where + * we need to prevent autocommit-off from starting a + * transaction anyway. + */ + if (conn->sversion >= 70300 && + conn->sversion < 70400) + query = "begin; select pg_catalog.pg_client_encoding(); end"; + else + query = "select pg_client_encoding()"; + if (!PQsendQuery(conn, query)) + goto error_return; + + conn->setenv_state = SETENV_STATE_QUERY2_WAIT; + return PGRES_POLLING_READING; + } + + case SETENV_STATE_QUERY2_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + const char *val; + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + /* ignore begin/commit command results */ + PQclear(res); + continue; + } + + if (PQresultStatus(res) == PGRES_TUPLES_OK && + PQntuples(res) == 1) + { + /* Extract client encoding and save it */ + val = PQgetvalue(res, 0, 0); + if (val && *val) /* null should not happen, but */ + pqSaveParameterStatus(conn, "client_encoding", + val); + } + else + { + /* + * Error: presumably function not available, so + * use PGCLIENTENCODING or SQL_ASCII as the + * fallback. + */ + val = getenv("PGCLIENTENCODING"); + if (val && *val) + pqSaveParameterStatus(conn, "client_encoding", + val); + else + pqSaveParameterStatus(conn, "client_encoding", + "SQL_ASCII"); + } + + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so we're done */ + conn->setenv_state = SETENV_STATE_IDLE; + return PGRES_POLLING_OK; + } + break; + } + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid state %c, " + "probably indicative of memory corruption\n"), + conn->setenv_state); + goto error_return; + } + } + + /* Unreachable */ + +error_return: + conn->setenv_state = SETENV_STATE_IDLE; + return PGRES_POLLING_FAILED; +} + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +void +pqParseInput2(PGconn *conn) +{ + char id; + + /* + * Loop to parse successive complete messages available in the buffer. + */ + for (;;) + { + /* + * Quit if in COPY_OUT state: we expect raw data from the server until + * PQendcopy is called. Don't try to parse it according to the normal + * protocol. (This is bogus. The data lines ought to be part of the + * protocol and have identifying leading characters.) + */ + if (conn->asyncStatus == PGASYNC_COPY_OUT) + return; + + /* + * OK to try to read a message type code. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return; + + /* + * NOTIFY and NOTICE messages can happen in any state besides COPY + * OUT; always process them right away. + * + * Most other messages should only be processed while in BUSY state. + * (In particular, in READY state we hold off further parsing until + * the application collects the current PGresult.) + * + * However, if the state is IDLE then we got trouble; we need to deal + * with the unexpected message somehow. + */ + if (id == 'A') + { + if (getNotify(conn)) + return; + } + else if (id == 'N') + { + if (pqGetErrorNotice2(conn, false)) + return; + } + else if (conn->asyncStatus != PGASYNC_BUSY) + { + /* If not IDLE state, just wait ... */ + if (conn->asyncStatus != PGASYNC_IDLE) + return; + + /* + * Unexpected message in IDLE state; need to recover somehow. + * ERROR messages are displayed using the notice processor; + * anything else is just dropped on the floor after displaying a + * suitable warning notice. (An ERROR is very possibly the + * backend telling us why it is about to close the connection, so + * we don't want to just discard it...) + */ + if (id == 'E') + { + if (pqGetErrorNotice2(conn, false /* treat as notice */ )) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "message type 0x%02x arrived from server while idle", + id); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + break; + } + } + else + { + /* + * In BUSY state, we can process everything. + */ + switch (id) + { + case 'C': /* command complete */ + if (pqGets(&conn->workBuffer, conn)) + return; + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + return; + } + strncpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + checkXactStatus(conn, conn->workBuffer.data); + conn->asyncStatus = PGASYNC_READY; + break; + case 'E': /* error return */ + if (pqGetErrorNotice2(conn, true)) + return; + conn->asyncStatus = PGASYNC_READY; + break; + case 'Z': /* backend is ready for new query */ + conn->asyncStatus = PGASYNC_IDLE; + break; + case 'I': /* empty query */ + /* read and throw away the closing '\0' */ + if (pqGetc(&id, conn)) + return; + if (id != '\0') + pqInternalNotice(&conn->noticeHooks, + "unexpected character %c following empty query response (\"I\" message)", + id); + if (conn->result == NULL) + conn->result = PQmakeEmptyPGresult(conn, + PGRES_EMPTY_QUERY); + conn->asyncStatus = PGASYNC_READY; + break; + case 'K': /* secret key data from the backend */ + + /* + * This is expected only during backend startup, but it's + * just as easy to handle it as part of the main loop. + * Save the data and continue processing. + */ + if (pqGetInt(&(conn->be_pid), 4, conn)) + return; + if (pqGetInt(&(conn->be_key), 4, conn)) + return; + break; + case 'P': /* synchronous (normal) portal */ + if (pqGets(&conn->workBuffer, conn)) + return; + /* We pretty much ignore this message type... */ + break; + case 'T': /* row descriptions (start of query results) */ + if (conn->result == NULL) + { + /* First 'T' in a query sequence */ + if (getRowDescriptions(conn)) + return; + } + else + { + /* + * A new 'T' message is treated as the start of + * another PGresult. (It is not clear that this is + * really possible with the current backend.) We stop + * parsing until the application accepts the current + * result. + */ + conn->asyncStatus = PGASYNC_READY; + return; + } + break; + case 'D': /* ASCII data tuple */ + if (conn->result != NULL) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, FALSE)) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "server sent data (\"D\" message) without prior row description (\"T\" message)"); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + return; + } + break; + case 'B': /* Binary data tuple */ + if (conn->result != NULL) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, TRUE)) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "server sent binary data (\"B\" message) without prior row description (\"T\" message)"); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + return; + } + break; + case 'G': /* Start Copy In */ + conn->asyncStatus = PGASYNC_COPY_IN; + break; + case 'H': /* Start Copy Out */ + conn->asyncStatus = PGASYNC_COPY_OUT; + break; + + /* + * Don't need to process CopyBothResponse here because it + * never arrives from the server during protocol 2.0. + */ + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "unexpected response from server; first received character was \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + conn->asyncStatus = PGASYNC_READY; + return; + } /* switch on protocol character */ + } + /* Successfully consumed this message */ + conn->inStart = conn->inCursor; + } +} + +/* + * parseInput subroutine to read a 'T' (row descriptions) message. + * We build a PGresult structure containing the attribute data. + * Returns: 0 if completed message, EOF if not enough data yet. + * + * Note that if we run out of data, we have to release the partially + * constructed PGresult, and rebuild it again next time. Fortunately, + * that shouldn't happen often, since 'T' messages usually fit in a packet. + */ +static int +getRowDescriptions(PGconn *conn) +{ + PGresult *result = NULL; + int nfields; + int i; + + result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + goto failure; + + /* parseInput already read the 'T' label. */ + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + goto failure; + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + /* get type info */ + for (i = 0; i < nfields; i++) + { + int typid; + int typlen; + int atttypmod; + + if (pqGets(&conn->workBuffer, conn) || + pqGetInt(&typid, 4, conn) || + pqGetInt(&typlen, 2, conn) || + pqGetInt(&atttypmod, 4, conn)) + goto failure; + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce the result to signed form. + */ + typlen = (int) ((int16) typlen); + + result->attDescs[i].name = pqResultStrdup(result, + conn->workBuffer.data); + if (!result->attDescs[i].name) + goto failure; + result->attDescs[i].tableid = 0; + result->attDescs[i].columnid = 0; + result->attDescs[i].format = 0; + result->attDescs[i].typid = typid; + result->attDescs[i].typlen = typlen; + result->attDescs[i].atttypmod = atttypmod; + } + + /* Success! */ + conn->result = result; + return 0; + +failure: + if (result) + PQclear(result); + return EOF; +} + +/* + * parseInput subroutine to read a 'B' or 'D' (row data) message. + * We add another tuple to the existing PGresult structure. + * Returns: 0 if completed message, EOF if error or not enough data yet. + * + * Note that if we run out of data, we have to suspend and reprocess + * the message after more data is received. We keep a partially constructed + * tuple in conn->curTuple, and avoid reallocating already-allocated storage. + */ +static int +getAnotherTuple(PGconn *conn, bool binary) +{ + PGresult *result = conn->result; + int nfields = result->numAttributes; + PGresAttValue *tup; + + /* the backend sends us a bitmap of which attributes are null */ + char std_bitmap[64]; /* used unless it doesn't fit */ + char *bitmap = std_bitmap; + int i; + size_t nbytes; /* the number of bytes in bitmap */ + char bmap; /* One byte of the bitmap */ + int bitmap_index; /* Its index */ + int bitcnt; /* number of bits examined in current byte */ + int vlen; /* length of the current field value */ + + result->binary = binary; + + /* Allocate tuple space if first time for this data message */ + if (conn->curTuple == NULL) + { + conn->curTuple = (PGresAttValue *) + pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); + if (conn->curTuple == NULL) + goto outOfMemory; + MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); + + /* + * If it's binary, fix the column format indicators. We assume the + * backend will consistently send either B or D, not a mix. + */ + if (binary) + { + for (i = 0; i < nfields; i++) + result->attDescs[i].format = 1; + } + } + tup = conn->curTuple; + + /* Get the null-value bitmap */ + nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + /* malloc() only for unusually large field counts... */ + if (nbytes > sizeof(std_bitmap)) + { + bitmap = (char *) malloc(nbytes); + if (!bitmap) + goto outOfMemory; + } + + if (pqGetnchar(bitmap, nbytes, conn)) + goto EOFexit; + + /* Scan the fields */ + bitmap_index = 0; + bmap = bitmap[bitmap_index]; + bitcnt = 0; + + for (i = 0; i < nfields; i++) + { + if (!(bmap & 0200)) + { + /* if the field value is absent, make it a null string */ + tup[i].value = result->null_field; + tup[i].len = NULL_LEN; + } + else + { + /* get the value length (the first four bytes are for length) */ + if (pqGetInt(&vlen, 4, conn)) + goto EOFexit; + if (!binary) + vlen = vlen - 4; + if (vlen < 0) + vlen = 0; + if (tup[i].value == NULL) + { + tup[i].value = (char *) pqResultAlloc(result, vlen + 1, binary); + if (tup[i].value == NULL) + goto outOfMemory; + } + tup[i].len = vlen; + /* read in the value */ + if (vlen > 0) + if (pqGetnchar((char *) (tup[i].value), vlen, conn)) + goto EOFexit; + /* we have to terminate this ourselves */ + tup[i].value[vlen] = '\0'; + } + /* advance the bitmap stuff */ + bitcnt++; + if (bitcnt == BITS_PER_BYTE) + { + bitmap_index++; + bmap = bitmap[bitmap_index]; + bitcnt = 0; + } + else + bmap <<= 1; + } + + /* Success! Store the completed tuple in the result */ + if (!pqAddTuple(result, tup)) + goto outOfMemory; + /* and reset for a new message */ + conn->curTuple = NULL; + + if (bitmap != std_bitmap) + free(bitmap); + return 0; + +outOfMemory: + /* Replace partially constructed result with an error result */ + + /* + * we do NOT use pqSaveErrorResult() here, because of the likelihood that + * there's not enough memory to concatenate messages... + */ + pqClearAsyncResult(conn); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory for query result\n")); + + /* + * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can + * do to recover... + */ + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + conn->asyncStatus = PGASYNC_READY; + /* Discard the failed message --- good idea? */ + conn->inStart = conn->inEnd; + +EOFexit: + if (bitmap != NULL && bitmap != std_bitmap) + free(bitmap); + return EOF; +} + + +/* + * Attempt to read an Error or Notice response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'E' or 'N' message type has already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +pqGetErrorNotice2(PGconn *conn, bool isError) +{ + PGresult *res = NULL; + PQExpBufferData workBuf; + char *startp; + char *splitp; + + /* + * Since the message might be pretty long, we create a temporary + * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended + * for stuff that is expected to be short. + */ + initPQExpBuffer(&workBuf); + if (pqGets(&workBuf, conn)) + goto failure; + + /* + * Make a PGresult to hold the message. We temporarily lie about the + * result status, so that PQmakeEmptyPGresult doesn't uselessly copy + * conn->errorMessage. + */ + res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (!res) + goto failure; + res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; + res->errMsg = pqResultStrdup(res, workBuf.data); + if (!res->errMsg) + goto failure; + + /* + * Break the message into fields. We can't do very much here, but we can + * split the severity code off, and remove trailing newlines. Also, we use + * the heuristic that the primary message extends only to the first + * newline --- anything after that is detail message. (In some cases it'd + * be better classed as hint, but we can hardly be expected to guess that + * here.) + */ + while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') + workBuf.data[--workBuf.len] = '\0'; + splitp = strstr(workBuf.data, ": "); + if (splitp) + { + /* what comes before the colon is severity */ + *splitp = '\0'; + pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); + startp = splitp + 3; + } + else + { + /* can't find a colon? oh well... */ + startp = workBuf.data; + } + splitp = strchr(startp, '\n'); + if (splitp) + { + /* what comes before the newline is primary message */ + *splitp++ = '\0'; + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); + /* the rest is detail; strip any leading whitespace */ + while (*splitp && isspace((unsigned char) *splitp)) + splitp++; + pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); + } + else + { + /* single-line message, so all primary */ + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); + } + + /* + * Either save error as current async result, or just emit the notice. + * Also, if it's an error and we were in a transaction block, assume the + * server has now gone to error-in-transaction state. + */ + if (isError) + { + pqClearAsyncResult(conn); + conn->result = res; + resetPQExpBuffer(&conn->errorMessage); + appendPQExpBufferStr(&conn->errorMessage, res->errMsg); + if (conn->xactStatus == PQTRANS_INTRANS) + conn->xactStatus = PQTRANS_INERROR; + } + else + { + if (res->noticeHooks.noticeRec != NULL) + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); + } + + termPQExpBuffer(&workBuf); + return 0; + +failure: + if (res) + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; +} + +/* + * checkXactStatus - attempt to track transaction-block status of server + * + * This is called each time we receive a command-complete message. By + * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do + * a passable job of tracking the server's xact status. BUT: this does + * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that + * feature ever a mistake.) Caveat user. + * + * The tags known here are all those used as far back as 7.0; is it worth + * adding those from even-older servers? + */ +static void +checkXactStatus(PGconn *conn, const char *cmdTag) +{ + if (strcmp(cmdTag, "BEGIN") == 0) + conn->xactStatus = PQTRANS_INTRANS; + else if (strcmp(cmdTag, "COMMIT") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "ROLLBACK") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */ + conn->xactStatus = PQTRANS_INTRANS; + + /* + * Normally we get into INERROR state by detecting an Error message. + * However, if we see one of these tags then we know for sure the server + * is in abort state ... + */ + else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */ + conn->xactStatus = PQTRANS_INERROR; +} + +/* + * Attempt to read a Notify response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'A' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed Notify message. + * returns EOF if not enough data. + */ +static int +getNotify(PGconn *conn) +{ + int be_pid; + int nmlen; + PGnotify *newNotify; + + if (pqGetInt(&be_pid, 4, conn)) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + return EOF; + + /* + * Store the relation name right after the PQnotify structure so it can + * all be freed at once. We don't use NAMEDATALEN because we don't want + * to tie this interface to a specific server name length. + */ + nmlen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1); + if (newNotify) + { + newNotify->relname = (char *) newNotify + sizeof(PGnotify); + strcpy(newNotify->relname, conn->workBuffer.data); + /* fake up an empty-string extra field */ + newNotify->extra = newNotify->relname + nmlen; + newNotify->be_pid = be_pid; + newNotify->next = NULL; + if (conn->notifyTail) + conn->notifyTail->next = newNotify; + else + conn->notifyHead = newNotify; + conn->notifyTail = newNotify; + } + + return 0; +} + + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData2(PGconn *conn, char **buffer, int async) +{ + bool found; + int msgLength; + + for (;;) + { + /* + * Do we have a complete line of data? + */ + conn->inCursor = conn->inStart; + found = false; + while (conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + + if (c == '\n') + { + found = true; + break; + } + } + if (!found) + goto nodata; + msgLength = conn->inCursor - conn->inStart; + + /* + * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and + * let caller read status with PQgetResult(). + */ + if (msgLength == 3 && + strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0) + { + conn->inStart = conn->inCursor; + conn->asyncStatus = PGASYNC_BUSY; + return -1; + } + + /* + * Pass the line back to the caller. + */ + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor; + + return msgLength; + +nodata: + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + } +} + + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * See fe-exec.c for documentation. + */ +int +pqGetline2(PGconn *conn, char *s, int maxlen) +{ + int result = 1; /* return value if buffer overflows */ + + if (conn->sock < 0) + { + *s = '\0'; + return EOF; + } + + /* + * Since this is a purely synchronous routine, we don't bother to maintain + * conn->inCursor; there is no need to back up. + */ + while (maxlen > 1) + { + if (conn->inStart < conn->inEnd) + { + char c = conn->inBuffer[conn->inStart++]; + + if (c == '\n') + { + result = 0; /* success exit */ + break; + } + *s++ = c; + maxlen--; + } + else + { + /* need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + result = EOF; + break; + } + } + } + *s = '\0'; + + return result; +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * See fe-exec.c for documentation. + */ +int +pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize) +{ + int avail; + + if (conn->asyncStatus != PGASYNC_COPY_OUT) + return -1; /* we are not doing a copy... */ + + /* + * Move data from libpq's buffer to the caller's. We want to accept data + * only in units of whole lines, not partial lines. This ensures that we + * can recognize the terminator line "\\.\n". (Otherwise, if it happened + * to cross a packet/buffer boundary, we might hand the first one or two + * characters off to the caller, which we shouldn't.) + */ + + conn->inCursor = conn->inStart; + + avail = bufsize; + while (avail > 0 && conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + + *buffer++ = c; + --avail; + if (c == '\n') + { + /* Got a complete line; mark the data removed from libpq */ + conn->inStart = conn->inCursor; + /* Is it the endmarker line? */ + if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.') + return -1; + /* No, return the data line to the caller */ + return bufsize - avail; + } + } + + /* + * We don't have a complete line. We'd prefer to leave it in libpq's + * buffer until the rest arrives, but there is a special case: what if the + * line is longer than the buffer the caller is offering us? In that case + * we'd better hand over a partial line, else we'd get into an infinite + * loop. Do this in a way that ensures we can't misrecognize a terminator + * line later: leave last 3 characters in libpq buffer. + */ + if (avail == 0 && bufsize > 3) + { + conn->inStart = conn->inCursor - 3; + return bufsize - 3; + } + return 0; +} + +/* + * PQendcopy + * + * See fe-exec.c for documentation. + */ +int +pqEndcopy2(PGconn *conn) +{ + PGresult *result; + + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_OUT) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return 1; + } + + /* + * make sure no data is waiting to be sent, abort if we are non-blocking + * and the flush fails + */ + if (pqFlush(conn) && pqIsnonblocking(conn)) + return 1; + + /* non blocking connections may have to abort at this point. */ + if (pqIsnonblocking(conn) && PQisBusy(conn)) + return 1; + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* Wait for the completion response */ + result = PQgetResult(conn); + + /* Expecting a successful result */ + if (result && result->resultStatus == PGRES_COMMAND_OK) + { + PQclear(result); + return 0; + } + + /* + * Trouble. For backwards-compatibility reasons, we issue the error + * message as if it were a notice (would be nice to get rid of this + * silliness, but too many apps probably don't handle errors from + * PQendcopy reasonably). Note that the app can still obtain the error + * status from the PGconn object. + */ + if (conn->errorMessage.len > 0) + { + /* We have to strip the trailing newline ... pain in neck... */ + char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; + + if (svLast == '\n') + conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; + pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); + conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; + } + + PQclear(result); + + /* + * The worst case is that we've lost sync with the backend entirely due to + * application screwup of the copy in/out protocol. To recover, reset the + * connection (talk about using a sledgehammer...) + */ + pqInternalNotice(&conn->noticeHooks, + "lost synchronization with server, resetting connection"); + + /* + * Users doing non-blocking connections need to handle the reset + * themselves, they'll need to check the connection status if we return an + * error. + */ + if (pqIsnonblocking(conn)) + PQresetStart(conn); + else + PQreset(conn); + + return 1; +} + + +/* + * PQfn - Send a function call to the POSTGRES backend. + * + * See fe-exec.c for documentation. + */ +PGresult * +pqFunctionCall2(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs) +{ + bool needInput = false; + ExecStatusType status = PGRES_FATAL_ERROR; + char id; + int i; + + /* PQfn already validated connection state */ + + if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ + pqPuts(" ", conn) < 0 || /* dummy string */ + pqPutInt(fnid, 4, conn) != 0 || /* function id */ + pqPutInt(nargs, 4, conn) != 0) /* # of args */ + { + pqHandleSendFailure(conn); + return NULL; + } + + for (i = 0; i < nargs; ++i) + { /* len.int4 + contents */ + if (pqPutInt(args[i].len, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + if (args[i].isint) + { + if (pqPutInt(args[i].u.integer, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + else + { + if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + } + + if (pqPutMsgEnd(conn) < 0 || + pqFlush(conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + for (;;) + { + if (needInput) + { + /* Wait for some data to arrive (or for the channel to close) */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + break; + } + + /* + * Scan the message. If we run out of data, loop around to try again. + */ + conn->inCursor = conn->inStart; + needInput = true; + + if (pqGetc(&id, conn)) + continue; + + /* + * We should see V or E response to the command, but might get N + * and/or A notices first. We also need to swallow the final Z before + * returning. + */ + switch (id) + { + case 'V': /* function result */ + if (pqGetc(&id, conn)) + continue; + if (id == 'G') + { + /* function returned nonempty value */ + if (pqGetInt(actual_result_len, 4, conn)) + continue; + if (result_is_int) + { + if (pqGetInt(result_buf, 4, conn)) + continue; + } + else + { + if (pqGetnchar((char *) result_buf, + *actual_result_len, + conn)) + continue; + } + if (pqGetc(&id, conn)) /* get the last '0' */ + continue; + } + if (id == '0') + { + /* correctly finished function result message */ + status = PGRES_COMMAND_OK; + } + else + { + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + conn->inStart = conn->inCursor; + return pqPrepareAsyncResult(conn); + } + break; + case 'E': /* error return */ + if (pqGetErrorNotice2(conn, true)) + continue; + status = PGRES_FATAL_ERROR; + break; + case 'A': /* notify message */ + /* handle notify and go back to processing return values */ + if (getNotify(conn)) + continue; + break; + case 'N': /* notice */ + /* handle notice and go back to processing return values */ + if (pqGetErrorNotice2(conn, false)) + continue; + break; + case 'Z': /* backend is ready for new query */ + /* consume the message and exit */ + conn->inStart = conn->inCursor; + /* if we saved a result object (probably an error), use it */ + if (conn->result) + return pqPrepareAsyncResult(conn); + return PQmakeEmptyPGresult(conn, status); + default: + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + conn->inStart = conn->inCursor; + return pqPrepareAsyncResult(conn); + } + /* Completed this message, keep going */ + conn->inStart = conn->inCursor; + needInput = false; + } + + /* + * We fall out of the loop only upon failing to read data. + * conn->errorMessage has been set by pqWait or pqReadData. We want to + * append it to any already-received error message. + */ + pqSaveErrorResult(conn); + return pqPrepareAsyncResult(conn); +} + + +/* + * Construct startup packet + * + * Returns a malloc'd packet buffer, or NULL if out of memory + */ +char * +pqBuildStartupPacket2(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options) +{ + StartupPacket *startpacket; + + *packetlen = sizeof(StartupPacket); + startpacket = (StartupPacket *) malloc(sizeof(StartupPacket)); + if (!startpacket) + return NULL; + + MemSet(startpacket, 0, sizeof(StartupPacket)); + + startpacket->protoVersion = htonl(conn->pversion); + + strncpy(startpacket->user, conn->pguser, SM_USER); + strncpy(startpacket->database, conn->dbName, SM_DATABASE); + strncpy(startpacket->tty, conn->pgtty, SM_TTY); + + if (conn->pgoptions) + strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS); + + return (char *) startpacket; +} diff --git a/src/libpq/fe-protocol3.c b/src/libpq/fe-protocol3.c new file mode 100644 index 00000000..45a84d8e --- /dev/null +++ b/src/libpq/fe-protocol3.c @@ -0,0 +1,1955 @@ +/*------------------------------------------------------------------------- + * + * fe-protocol3.c + * functions that are specific to frontend/backend protocol version 3 + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-protocol3.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + + +/* + * This macro lists the backend message types that could be "long" (more + * than a couple of kilobytes). + */ +#define VALID_LONG_MESSAGE_TYPE(id) \ + ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \ + (id) == 'E' || (id) == 'N' || (id) == 'A') + + +static void handleSyncLoss(PGconn *conn, char id, int msgLength); +static int getRowDescriptions(PGconn *conn); +static int getParamDescriptions(PGconn *conn); +static int getAnotherTuple(PGconn *conn, int msgLength); +static int getParameterStatus(PGconn *conn); +static int getNotify(PGconn *conn); +static int getCopyStart(PGconn *conn, ExecStatusType copytype); +static int getReadyForQuery(PGconn *conn); +static void reportErrorPosition(PQExpBuffer msg, const char *query, + int loc, int encoding); +static int build_startup_packet(const PGconn *conn, char *packet, + const PQEnvironmentOption *options); + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +void +pqParseInput3(PGconn *conn) +{ + char id; + int msgLength; + int avail; + + /* + * Loop to parse successive complete messages available in the buffer. + */ + for (;;) + { + /* + * Try to read a message. First get the type code and length. Return + * if not enough data. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return; + if (pqGetInt(&msgLength, 4, conn)) + return; + + /* + * Try to validate message type/length here. A length less than 4 is + * definitely broken. Large lengths should only be believed for a few + * message types. + */ + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + return; + } + if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + { + handleSyncLoss(conn, id, msgLength); + return; + } + + /* + * Can't process if message body isn't all here yet. + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before returning, enlarge the input buffer if needed to hold + * the whole message. This is better than leaving it to + * pqReadData because we can avoid multiple cycles of realloc() + * when the message is large; also, we can implement a reasonable + * recovery strategy if we are unable to make the buffer big + * enough. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + } + return; + } + + /* + * NOTIFY and NOTICE messages can happen in any state; always process + * them right away. + * + * Most other messages should only be processed while in BUSY state. + * (In particular, in READY state we hold off further parsing until + * the application collects the current PGresult.) + * + * However, if the state is IDLE then we got trouble; we need to deal + * with the unexpected message somehow. + * + * ParameterStatus ('S') messages are a special case: in IDLE state we + * must process 'em (this case could happen if a new value was adopted + * from config file due to SIGHUP), but otherwise we hold off until + * BUSY state. + */ + if (id == 'A') + { + if (getNotify(conn)) + return; + } + else if (id == 'N') + { + if (pqGetErrorNotice3(conn, false)) + return; + } + else if (conn->asyncStatus != PGASYNC_BUSY) + { + /* If not IDLE state, just wait ... */ + if (conn->asyncStatus != PGASYNC_IDLE) + return; + + /* + * Unexpected message in IDLE state; need to recover somehow. + * ERROR messages are displayed using the notice processor; + * ParameterStatus is handled normally; anything else is just + * dropped on the floor after displaying a suitable warning + * notice. (An ERROR is very possibly the backend telling us why + * it is about to close the connection, so we don't want to just + * discard it...) + */ + if (id == 'E') + { + if (pqGetErrorNotice3(conn, false /* treat as notice */ )) + return; + } + else if (id == 'S') + { + if (getParameterStatus(conn)) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "message type 0x%02x arrived from server while idle", + id); + /* Discard the unexpected message */ + conn->inCursor += msgLength; + } + } + else + { + /* + * In BUSY state, we can process everything. + */ + switch (id) + { + case 'C': /* command complete */ + if (pqGets(&conn->workBuffer, conn)) + return; + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + return; + } + strncpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + conn->asyncStatus = PGASYNC_READY; + break; + case 'E': /* error return */ + if (pqGetErrorNotice3(conn, true)) + return; + conn->asyncStatus = PGASYNC_READY; + break; + case 'Z': /* backend is ready for new query */ + if (getReadyForQuery(conn)) + return; + conn->asyncStatus = PGASYNC_IDLE; + break; + case 'I': /* empty query */ + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_EMPTY_QUERY); + if (!conn->result) + return; + } + conn->asyncStatus = PGASYNC_READY; + break; + case '1': /* Parse Complete */ + /* If we're doing PQprepare, we're done; else ignore */ + if (conn->queryclass == PGQUERY_PREPARE) + { + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + return; + } + conn->asyncStatus = PGASYNC_READY; + } + break; + case '2': /* Bind Complete */ + case '3': /* Close Complete */ + /* Nothing to do for these message types */ + break; + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + return; + break; + case 'K': /* secret key data from the backend */ + + /* + * This is expected only during backend startup, but it's + * just as easy to handle it as part of the main loop. + * Save the data and continue processing. + */ + if (pqGetInt(&(conn->be_pid), 4, conn)) + return; + if (pqGetInt(&(conn->be_key), 4, conn)) + return; + break; + case 'T': /* Row Description */ + if (conn->result == NULL || + conn->queryclass == PGQUERY_DESCRIBE) + { + /* First 'T' in a query sequence */ + if (getRowDescriptions(conn)) + return; + + /* + * If we're doing a Describe, we're ready to pass the + * result back to the client. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + conn->asyncStatus = PGASYNC_READY; + } + else + { + /* + * A new 'T' message is treated as the start of + * another PGresult. (It is not clear that this is + * really possible with the current backend.) We stop + * parsing until the application accepts the current + * result. + */ + conn->asyncStatus = PGASYNC_READY; + return; + } + break; + case 'n': /* No Data */ + + /* + * NoData indicates that we will not be seeing a + * RowDescription message because the statement or portal + * inquired about doesn't return rows. + * + * If we're doing a Describe, we have to pass something + * back to the client, so set up a COMMAND_OK result, + * instead of TUPLES_OK. Otherwise we can just ignore + * this message. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + { + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + return; + } + conn->asyncStatus = PGASYNC_READY; + } + break; + case 't': /* Parameter Description */ + if (getParamDescriptions(conn)) + return; + break; + case 'D': /* Data Row */ + if (conn->result != NULL && + conn->result->resultStatus == PGRES_TUPLES_OK) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, msgLength)) + return; + } + else if (conn->result != NULL && + conn->result->resultStatus == PGRES_FATAL_ERROR) + { + /* + * We've already choked for some reason. Just discard + * tuples till we get to the end of the query. + */ + conn->inCursor += msgLength; + } + else + { + /* Set up to report error at end of query */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); + pqSaveErrorResult(conn); + /* Discard the unexpected message */ + conn->inCursor += msgLength; + } + break; + case 'G': /* Start Copy In */ + if (getCopyStart(conn, PGRES_COPY_IN)) + return; + conn->asyncStatus = PGASYNC_COPY_IN; + break; + case 'H': /* Start Copy Out */ + if (getCopyStart(conn, PGRES_COPY_OUT)) + return; + conn->asyncStatus = PGASYNC_COPY_OUT; + conn->copy_already_done = 0; + break; + case 'W': /* Start Copy Both */ + if (getCopyStart(conn, PGRES_COPY_BOTH)) + return; + conn->asyncStatus = PGASYNC_COPY_BOTH; + conn->copy_already_done = 0; + break; + case 'd': /* Copy Data */ + + /* + * If we see Copy Data, just silently drop it. This would + * only occur if application exits COPY OUT mode too + * early. + */ + conn->inCursor += msgLength; + break; + case 'c': /* Copy Done */ + + /* + * If we see Copy Done, just silently drop it. This is + * the normal case during PQendcopy. We will keep + * swallowing data, expecting to see command-complete for + * the COPY command. + */ + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "unexpected response from server; first received character was \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + /* not sure if we will see more, so go to ready state */ + conn->asyncStatus = PGASYNC_READY; + /* Discard the unexpected message */ + conn->inCursor += msgLength; + break; + } /* switch on protocol character */ + } + /* Successfully consumed this message */ + if (conn->inCursor == conn->inStart + 5 + msgLength) + { + /* Normal case: parsing agrees with specified length */ + conn->inStart = conn->inCursor; + } + else + { + /* Trouble --- report it */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_READY; + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + } + } +} + +/* + * handleSyncLoss: clean up after loss of message-boundary sync + * + * There isn't really a lot we can do here except abandon the connection. + */ +static void +handleSyncLoss(PGconn *conn, char id, int msgLength) +{ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "lost synchronization with server: got message type \"%c\", length %d\n"), + id, msgLength); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ + + pqsecure_close(conn); + closesocket(conn->sock); + conn->sock = -1; + conn->status = CONNECTION_BAD; /* No more connection to backend */ +} + +/* + * parseInput subroutine to read a 'T' (row descriptions) message. + * We'll build a new PGresult structure (unless called for a Describe + * command for a prepared statement) containing the attribute data. + * Returns: 0 if completed message, EOF if not enough data yet. + * + * Note that if we run out of data, we have to release the partially + * constructed PGresult, and rebuild it again next time. Fortunately, + * that shouldn't happen often, since 'T' messages usually fit in a packet. + */ +static int +getRowDescriptions(PGconn *conn) +{ + PGresult *result; + int nfields; + int i; + + /* + * When doing Describe for a prepared statement, there'll already be a + * PGresult created by getParamDescriptions, and we should fill data into + * that. Otherwise, create a new, empty PGresult. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + { + if (conn->result) + result = conn->result; + else + result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + } + else + result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + goto failure; + + /* parseInput already read the 'T' label and message length. */ + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + goto failure; + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + /* result->binary is true only if ALL columns are binary */ + result->binary = (nfields > 0) ? 1 : 0; + + /* get type info */ + for (i = 0; i < nfields; i++) + { + int tableid; + int columnid; + int typid; + int typlen; + int atttypmod; + int format; + + if (pqGets(&conn->workBuffer, conn) || + pqGetInt(&tableid, 4, conn) || + pqGetInt(&columnid, 2, conn) || + pqGetInt(&typid, 4, conn) || + pqGetInt(&typlen, 2, conn) || + pqGetInt(&atttypmod, 4, conn) || + pqGetInt(&format, 2, conn)) + { + goto failure; + } + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce these results to signed form. + */ + columnid = (int) ((int16) columnid); + typlen = (int) ((int16) typlen); + format = (int) ((int16) format); + + result->attDescs[i].name = pqResultStrdup(result, + conn->workBuffer.data); + if (!result->attDescs[i].name) + goto failure; + result->attDescs[i].tableid = tableid; + result->attDescs[i].columnid = columnid; + result->attDescs[i].format = format; + result->attDescs[i].typid = typid; + result->attDescs[i].typlen = typlen; + result->attDescs[i].atttypmod = atttypmod; + + if (format != 1) + result->binary = 0; + } + + /* Success! */ + conn->result = result; + return 0; + +failure: + + /* + * Discard incomplete result, unless it's from getParamDescriptions. + * + * Note that if we hit a bufferload boundary while handling the + * describe-statement case, we'll forget any PGresult space we just + * allocated, and then reallocate it on next try. This will bloat the + * PGresult a little bit but the space will be freed at PQclear, so it + * doesn't seem worth trying to be smarter. + */ + if (result != conn->result) + PQclear(result); + return EOF; +} + +/* + * parseInput subroutine to read a 't' (ParameterDescription) message. + * We'll build a new PGresult structure containing the parameter data. + * Returns: 0 if completed message, EOF if not enough data yet. + * + * Note that if we run out of data, we have to release the partially + * constructed PGresult, and rebuild it again next time. Fortunately, + * that shouldn't happen often, since 't' messages usually fit in a packet. + */ +static int +getParamDescriptions(PGconn *conn) +{ + PGresult *result; + int nparams; + int i; + + result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + if (!result) + goto failure; + + /* parseInput already read the 't' label and message length. */ + /* the next two bytes are the number of parameters */ + if (pqGetInt(&(result->numParameters), 2, conn)) + goto failure; + nparams = result->numParameters; + + /* allocate space for the parameter descriptors */ + if (nparams > 0) + { + result->paramDescs = (PGresParamDesc *) + pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE); + if (!result->paramDescs) + goto failure; + MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc)); + } + + /* get parameter info */ + for (i = 0; i < nparams; i++) + { + int typid; + + if (pqGetInt(&typid, 4, conn)) + goto failure; + result->paramDescs[i].typid = typid; + } + + /* Success! */ + conn->result = result; + return 0; + +failure: + PQclear(result); + return EOF; +} + +/* + * parseInput subroutine to read a 'D' (row data) message. + * We add another tuple to the existing PGresult structure. + * Returns: 0 if completed message, EOF if error or not enough data yet. + * + * Note that if we run out of data, we have to suspend and reprocess + * the message after more data is received. We keep a partially constructed + * tuple in conn->curTuple, and avoid reallocating already-allocated storage. + */ +static int +getAnotherTuple(PGconn *conn, int msgLength) +{ + PGresult *result = conn->result; + int nfields = result->numAttributes; + PGresAttValue *tup; + int tupnfields; /* # fields from tuple */ + int vlen; /* length of the current field value */ + int i; + + /* Allocate tuple space if first time for this data message */ + if (conn->curTuple == NULL) + { + conn->curTuple = (PGresAttValue *) + pqResultAlloc(result, nfields * sizeof(PGresAttValue), TRUE); + if (conn->curTuple == NULL) + goto outOfMemory; + MemSet(conn->curTuple, 0, nfields * sizeof(PGresAttValue)); + } + tup = conn->curTuple; + + /* Get the field count and make sure it's what we expect */ + if (pqGetInt(&tupnfields, 2, conn)) + return EOF; + + if (tupnfields != nfields) + { + /* Replace partially constructed result with an error result */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unexpected field count in \"D\" message\n")); + pqSaveErrorResult(conn); + /* Discard the failed message by pretending we read it */ + conn->inCursor = conn->inStart + 5 + msgLength; + return 0; + } + + /* Scan the fields */ + for (i = 0; i < nfields; i++) + { + /* get the value length */ + if (pqGetInt(&vlen, 4, conn)) + return EOF; + if (vlen == -1) + { + /* null field */ + tup[i].value = result->null_field; + tup[i].len = NULL_LEN; + continue; + } + if (vlen < 0) + vlen = 0; + if (tup[i].value == NULL) + { + bool isbinary = (result->attDescs[i].format != 0); + + tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary); + if (tup[i].value == NULL) + goto outOfMemory; + } + tup[i].len = vlen; + /* read in the value */ + if (vlen > 0) + if (pqGetnchar((char *) (tup[i].value), vlen, conn)) + return EOF; + /* we have to terminate this ourselves */ + tup[i].value[vlen] = '\0'; + } + + /* Success! Store the completed tuple in the result */ + if (!pqAddTuple(result, tup)) + goto outOfMemory; + /* and reset for a new message */ + conn->curTuple = NULL; + + return 0; + +outOfMemory: + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory for query result\n")); + pqSaveErrorResult(conn); + + /* Discard the failed message by pretending we read it */ + conn->inCursor = conn->inStart + 5 + msgLength; + return 0; +} + + +/* + * Attempt to read an Error or Notice response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'E' or 'N' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +int +pqGetErrorNotice3(PGconn *conn, bool isError) +{ + PGresult *res = NULL; + PQExpBufferData workBuf; + char id; + const char *val; + const char *querytext = NULL; + int querypos = 0; + + /* + * Since the fields might be pretty long, we create a temporary + * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended + * for stuff that is expected to be short. We shouldn't use + * conn->errorMessage either, since this might be only a notice. + */ + initPQExpBuffer(&workBuf); + + /* + * Make a PGresult to hold the accumulated fields. We temporarily lie + * about the result status, so that PQmakeEmptyPGresult doesn't uselessly + * copy conn->errorMessage. + */ + res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (!res) + goto fail; + res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; + + /* + * Read the fields and save into res. + */ + for (;;) + { + if (pqGetc(&id, conn)) + goto fail; + if (id == '\0') + break; /* terminator found */ + if (pqGets(&workBuf, conn)) + goto fail; + pqSaveMessageField(res, id, workBuf.data); + } + + /* + * Now build the "overall" error message for PQresultErrorMessage. + * + * Also, save the SQLSTATE in conn->last_sqlstate. + */ + resetPQExpBuffer(&workBuf); + val = PQresultErrorField(res, PG_DIAG_SEVERITY); + if (val) + appendPQExpBuffer(&workBuf, "%s: ", val); + val = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (val) + { + if (strlen(val) < sizeof(conn->last_sqlstate)) + strcpy(conn->last_sqlstate, val); + if (conn->verbosity == PQERRORS_VERBOSE) + appendPQExpBuffer(&workBuf, "%s: ", val); + } + val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); + if (val) + appendPQExpBufferStr(&workBuf, val); + val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); + if (val) + { + if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL) + { + /* emit position as a syntax cursor display */ + querytext = conn->last_query; + querypos = atoi(val); + } + else + { + /* emit position as text addition to primary message */ + /* translator: %s represents a digit string */ + appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), + val); + } + } + else + { + val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); + if (val) + { + querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); + if (conn->verbosity != PQERRORS_TERSE && querytext != NULL) + { + /* emit position as a syntax cursor display */ + querypos = atoi(val); + } + else + { + /* emit position as text addition to primary message */ + /* translator: %s represents a digit string */ + appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), + val); + } + } + } + appendPQExpBufferChar(&workBuf, '\n'); + if (conn->verbosity != PQERRORS_TERSE) + { + if (querytext && querypos > 0) + reportErrorPosition(&workBuf, querytext, querypos, + conn->client_encoding); + val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_CONTEXT); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val); + } + if (conn->verbosity == PQERRORS_VERBOSE) + { + const char *valf; + const char *vall; + + valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); + vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); + val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); + if (val || valf || vall) + { + appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: ")); + if (val) + appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val); + if (valf && vall) /* unlikely we'd have just one */ + appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"), + valf, vall); + appendPQExpBufferChar(&workBuf, '\n'); + } + } + + /* + * Either save error as current async result, or just emit the notice. + */ + if (isError) + { + res->errMsg = pqResultStrdup(res, workBuf.data); + if (!res->errMsg) + goto fail; + pqClearAsyncResult(conn); + conn->result = res; + appendPQExpBufferStr(&conn->errorMessage, workBuf.data); + } + else + { + /* We can cheat a little here and not copy the message. */ + res->errMsg = workBuf.data; + if (res->noticeHooks.noticeRec != NULL) + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); + } + + termPQExpBuffer(&workBuf); + return 0; + +fail: + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; +} + +/* + * Add an error-location display to the error message under construction. + * + * The cursor location is measured in logical characters; the query string + * is presumed to be in the specified encoding. + */ +static void +reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) +{ +#define DISPLAY_SIZE 60 /* screen width limit, in screen cols */ +#define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */ + + char *wquery; + int slen, + cno, + i, + *qidx, + *scridx, + qoffset, + scroffset, + ibeg, + iend, + loc_line; + bool mb_encoding, + beg_trunc, + end_trunc; + + /* Convert loc from 1-based to 0-based; no-op if out of range */ + loc--; + if (loc < 0) + return; + + /* Need a writable copy of the query */ + wquery = strdup(query); + if (wquery == NULL) + return; /* fail silently if out of memory */ + + /* + * Each character might occupy multiple physical bytes in the string, and + * in some Far Eastern character sets it might take more than one screen + * column as well. We compute the starting byte offset and starting + * screen column of each logical character, and store these in qidx[] and + * scridx[] respectively. + */ + + /* we need a safe allocation size... */ + slen = strlen(wquery) + 1; + + qidx = (int *) malloc(slen * sizeof(int)); + if (qidx == NULL) + { + free(wquery); + return; + } + scridx = (int *) malloc(slen * sizeof(int)); + if (scridx == NULL) + { + free(qidx); + free(wquery); + return; + } + + /* We can optimize a bit if it's a single-byte encoding */ + mb_encoding = (pg_encoding_max_length(encoding) != 1); + + /* + * Within the scanning loop, cno is the current character's logical + * number, qoffset is its offset in wquery, and scroffset is its starting + * logical screen column (all indexed from 0). "loc" is the logical + * character number of the error location. We scan to determine loc_line + * (the 1-based line number containing loc) and ibeg/iend (first character + * number and last+1 character number of the line containing loc). Note + * that qidx[] and scridx[] are filled only as far as iend. + */ + qoffset = 0; + scroffset = 0; + loc_line = 1; + ibeg = 0; + iend = -1; /* -1 means not set yet */ + + for (cno = 0; wquery[qoffset] != '\0'; cno++) + { + char ch = wquery[qoffset]; + + qidx[cno] = qoffset; + scridx[cno] = scroffset; + + /* + * Replace tabs with spaces in the writable copy. (Later we might + * want to think about coping with their variable screen width, but + * not today.) + */ + if (ch == '\t') + wquery[qoffset] = ' '; + + /* + * If end-of-line, count lines and mark positions. Each \r or \n + * counts as a line except when \r \n appear together. + */ + else if (ch == '\r' || ch == '\n') + { + if (cno < loc) + { + if (ch == '\r' || + cno == 0 || + wquery[qidx[cno - 1]] != '\r') + loc_line++; + /* extract beginning = last line start before loc. */ + ibeg = cno + 1; + } + else + { + /* set extract end. */ + iend = cno; + /* done scanning. */ + break; + } + } + + /* Advance */ + if (mb_encoding) + { + int w; + + w = pg_encoding_dsplen(encoding, &wquery[qoffset]); + /* treat any non-tab control chars as width 1 */ + if (w <= 0) + w = 1; + scroffset += w; + qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]); + } + else + { + /* We assume wide chars only exist in multibyte encodings */ + scroffset++; + qoffset++; + } + } + /* Fix up if we didn't find an end-of-line after loc */ + if (iend < 0) + { + iend = cno; /* query length in chars, +1 */ + qidx[iend] = qoffset; + scridx[iend] = scroffset; + } + + /* Print only if loc is within computed query length */ + if (loc <= cno) + { + /* If the line extracted is too long, we truncate it. */ + beg_trunc = false; + end_trunc = false; + if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + { + /* + * We first truncate right if it is enough. This code might be + * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide + * character right there, but that should be okay. + */ + if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT) + { + while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + iend--; + end_trunc = true; + } + else + { + /* Truncate right if not too close to loc. */ + while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend]) + { + iend--; + end_trunc = true; + } + + /* Truncate left if still too long. */ + while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + { + ibeg++; + beg_trunc = true; + } + } + } + + /* truncate working copy at desired endpoint */ + wquery[qidx[iend]] = '\0'; + + /* Begin building the finished message. */ + i = msg->len; + appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line); + if (beg_trunc) + appendPQExpBufferStr(msg, "..."); + + /* + * While we have the prefix in the msg buffer, compute its screen + * width. + */ + scroffset = 0; + for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i])) + { + int w = pg_encoding_dsplen(encoding, &msg->data[i]); + + if (w <= 0) + w = 1; + scroffset += w; + } + + /* Finish up the LINE message line. */ + appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]); + if (end_trunc) + appendPQExpBufferStr(msg, "..."); + appendPQExpBufferChar(msg, '\n'); + + /* Now emit the cursor marker line. */ + scroffset += scridx[loc] - scridx[ibeg]; + for (i = 0; i < scroffset; i++) + appendPQExpBufferChar(msg, ' '); + appendPQExpBufferChar(msg, '^'); + appendPQExpBufferChar(msg, '\n'); + } + + /* Clean up. */ + free(scridx); + free(qidx); + free(wquery); +} + + +/* + * Attempt to read a ParameterStatus message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'S' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +getParameterStatus(PGconn *conn) +{ + PQExpBufferData valueBuf; + + /* Get the parameter name */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* Get the parameter value (could be large) */ + initPQExpBuffer(&valueBuf); + if (pqGets(&valueBuf, conn)) + { + termPQExpBuffer(&valueBuf); + return EOF; + } + /* And save it */ + pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data); + termPQExpBuffer(&valueBuf); + return 0; +} + + +/* + * Attempt to read a Notify response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'A' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed Notify message. + * returns EOF if not enough data. + */ +static int +getNotify(PGconn *conn) +{ + int be_pid; + char *svname; + int nmlen; + int extralen; + PGnotify *newNotify; + + if (pqGetInt(&be_pid, 4, conn)) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* must save name while getting extra string */ + svname = strdup(conn->workBuffer.data); + if (!svname) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + { + free(svname); + return EOF; + } + + /* + * Store the strings right after the PQnotify structure so it can all be + * freed at once. We don't use NAMEDATALEN because we don't want to tie + * this interface to a specific server name length. + */ + nmlen = strlen(svname); + extralen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); + if (newNotify) + { + newNotify->relname = (char *) newNotify + sizeof(PGnotify); + strcpy(newNotify->relname, svname); + newNotify->extra = newNotify->relname + nmlen + 1; + strcpy(newNotify->extra, conn->workBuffer.data); + newNotify->be_pid = be_pid; + newNotify->next = NULL; + if (conn->notifyTail) + conn->notifyTail->next = newNotify; + else + conn->notifyHead = newNotify; + conn->notifyTail = newNotify; + } + + free(svname); + return 0; +} + +/* + * getCopyStart - process CopyInResponse, CopyOutResponse or + * CopyBothResponse message + * + * parseInput already read the message type and length. + */ +static int +getCopyStart(PGconn *conn, ExecStatusType copytype) +{ + PGresult *result; + int nfields; + int i; + + result = PQmakeEmptyPGresult(conn, copytype); + if (!result) + goto failure; + + if (pqGetc(&conn->copy_is_binary, conn)) + goto failure; + result->binary = conn->copy_is_binary; + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + goto failure; + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + for (i = 0; i < nfields; i++) + { + int format; + + if (pqGetInt(&format, 2, conn)) + goto failure; + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce these results to signed form. + */ + format = (int) ((int16) format); + result->attDescs[i].format = format; + } + + /* Success! */ + conn->result = result; + return 0; + +failure: + PQclear(result); + return EOF; +} + +/* + * getReadyForQuery - process ReadyForQuery message + */ +static int +getReadyForQuery(PGconn *conn) +{ + char xact_status; + + if (pqGetc(&xact_status, conn)) + return EOF; + switch (xact_status) + { + case 'I': + conn->xactStatus = PQTRANS_IDLE; + break; + case 'T': + conn->xactStatus = PQTRANS_INTRANS; + break; + case 'E': + conn->xactStatus = PQTRANS_INERROR; + break; + default: + conn->xactStatus = PQTRANS_UNKNOWN; + break; + } + + return 0; +} + +/* + * getCopyDataMessage - fetch next CopyData message, process async messages + * + * Returns length word of CopyData message (> 0), or 0 if no complete + * message available, -1 if end of copy, -2 if error. + */ +static int +getCopyDataMessage(PGconn *conn) +{ + char id; + int msgLength; + int avail; + + for (;;) + { + /* + * Do we have the next input message? To make life simpler for async + * callers, we keep returning 0 until the next message is fully + * available, even if it is not Copy Data. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return 0; + if (pqGetInt(&msgLength, 4, conn)) + return 0; + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + return -2; + } + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength - 4) + { + /* + * Before returning, enlarge the input buffer if needed to hold + * the whole message. See notes in parseInput. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength - 4, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + return -2; + } + return 0; + } + + /* + * If it's a legitimate async message type, process it. (NOTIFY + * messages are not currently possible here, but we handle them for + * completeness.) Otherwise, if it's anything except Copy Data, + * report end-of-copy. + */ + switch (id) + { + case 'A': /* NOTIFY */ + if (getNotify(conn)) + return 0; + break; + case 'N': /* NOTICE */ + if (pqGetErrorNotice3(conn, false)) + return 0; + break; + case 'S': /* ParameterStatus */ + if (getParameterStatus(conn)) + return 0; + break; + case 'd': /* Copy Data, pass it back to caller */ + return msgLength; + default: /* treat as end of copy */ + return -1; + } + + /* Drop the processed message and loop around for another */ + conn->inStart = conn->inCursor; + } +} + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * or COPY BOTH + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData3(PGconn *conn, char **buffer, int async) +{ + int msgLength; + + for (;;) + { + /* + * Collect the next input message. To make life simpler for async + * callers, we keep returning 0 until the next message is fully + * available, even if it is not Copy Data. + */ + msgLength = getCopyDataMessage(conn); + if (msgLength < 0) + { + /* + * On end-of-copy, exit COPY_OUT or COPY_BOTH mode and let caller + * read status with PQgetResult(). The normal case is that it's + * Copy Done, but we let parseInput read that. If error, we + * expect the state was already changed. + */ + if (msgLength == -1) + conn->asyncStatus = PGASYNC_BUSY; + return msgLength; /* end-of-copy or error */ + } + if (msgLength == 0) + { + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + continue; + } + + /* + * Drop zero-length messages (shouldn't happen anyway). Otherwise + * pass the data back to the caller. + */ + msgLength -= 4; + if (msgLength > 0) + { + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor + msgLength; + + return msgLength; + } + + /* Empty, so drop it and loop around for another */ + conn->inStart = conn->inCursor; + } +} + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * See fe-exec.c for documentation. + */ +int +pqGetline3(PGconn *conn, char *s, int maxlen) +{ + int status; + + if (conn->sock < 0 || + conn->asyncStatus != PGASYNC_COPY_OUT || + conn->copy_is_binary) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PQgetline: not doing text COPY OUT\n")); + *s = '\0'; + return EOF; + } + + while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0) + { + /* need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + *s = '\0'; + return EOF; + } + } + + if (status < 0) + { + /* End of copy detected; gin up old-style terminator */ + strcpy(s, "\\."); + return 0; + } + + /* Add null terminator, and strip trailing \n if present */ + if (s[status - 1] == '\n') + { + s[status - 1] = '\0'; + return 0; + } + else + { + s[status] = '\0'; + return 1; + } +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * See fe-exec.c for documentation. + */ +int +pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize) +{ + int msgLength; + int avail; + + if (conn->asyncStatus != PGASYNC_COPY_OUT) + return -1; /* we are not doing a copy... */ + + /* + * Recognize the next input message. To make life simpler for async + * callers, we keep returning 0 until the next message is fully available + * even if it is not Copy Data. This should keep PQendcopy from blocking. + * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.) + */ + msgLength = getCopyDataMessage(conn); + if (msgLength < 0) + return -1; /* end-of-copy or error */ + if (msgLength == 0) + return 0; /* no data yet */ + + /* + * Move data from libpq's buffer to the caller's. In the case where a + * prior call found the caller's buffer too small, we use + * conn->copy_already_done to remember how much of the row was already + * returned to the caller. + */ + conn->inCursor += conn->copy_already_done; + avail = msgLength - 4 - conn->copy_already_done; + if (avail <= bufsize) + { + /* Able to consume the whole message */ + memcpy(buffer, &conn->inBuffer[conn->inCursor], avail); + /* Mark message consumed */ + conn->inStart = conn->inCursor + avail; + /* Reset state for next time */ + conn->copy_already_done = 0; + return avail; + } + else + { + /* We must return a partial message */ + memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize); + /* The message is NOT consumed from libpq's buffer */ + conn->copy_already_done += bufsize; + return bufsize; + } +} + +/* + * PQendcopy + * + * See fe-exec.c for documentation. + */ +int +pqEndcopy3(PGconn *conn) +{ + PGresult *result; + + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_OUT) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return 1; + } + + /* Send the CopyDone message if needed */ + if (conn->asyncStatus == PGASYNC_COPY_IN) + { + if (pqPutMsgStart('c', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return 1; + + /* + * If we sent the COPY command in extended-query mode, we must issue a + * Sync as well. + */ + if (conn->queryclass != PGQUERY_SIMPLE) + { + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return 1; + } + } + + /* + * make sure no data is waiting to be sent, abort if we are non-blocking + * and the flush fails + */ + if (pqFlush(conn) && pqIsnonblocking(conn)) + return 1; + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* + * Non blocking connections may have to abort at this point. If everyone + * played the game there should be no problem, but in error scenarios the + * expected messages may not have arrived yet. (We are assuming that the + * backend's packetizing will ensure that CommandComplete arrives along + * with the CopyDone; are there corner cases where that doesn't happen?) + */ + if (pqIsnonblocking(conn) && PQisBusy(conn)) + return 1; + + /* Wait for the completion response */ + result = PQgetResult(conn); + + /* Expecting a successful result */ + if (result && result->resultStatus == PGRES_COMMAND_OK) + { + PQclear(result); + return 0; + } + + /* + * Trouble. For backwards-compatibility reasons, we issue the error + * message as if it were a notice (would be nice to get rid of this + * silliness, but too many apps probably don't handle errors from + * PQendcopy reasonably). Note that the app can still obtain the error + * status from the PGconn object. + */ + if (conn->errorMessage.len > 0) + { + /* We have to strip the trailing newline ... pain in neck... */ + char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; + + if (svLast == '\n') + conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; + pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); + conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; + } + + PQclear(result); + + return 1; +} + + +/* + * PQfn - Send a function call to the POSTGRES backend. + * + * See fe-exec.c for documentation. + */ +PGresult * +pqFunctionCall3(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs) +{ + bool needInput = false; + ExecStatusType status = PGRES_FATAL_ERROR; + char id; + int msgLength; + int avail; + int i; + + /* PQfn already validated connection state */ + + if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ + pqPutInt(fnid, 4, conn) < 0 || /* function id */ + pqPutInt(1, 2, conn) < 0 || /* # of format codes */ + pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ + pqPutInt(nargs, 2, conn) < 0) /* # of args */ + { + pqHandleSendFailure(conn); + return NULL; + } + + for (i = 0; i < nargs; ++i) + { /* len.int4 + contents */ + if (pqPutInt(args[i].len, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + if (args[i].len == -1) + continue; /* it's NULL */ + + if (args[i].isint) + { + if (pqPutInt(args[i].u.integer, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + else + { + if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + } + + if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ + { + pqHandleSendFailure(conn); + return NULL; + } + + if (pqPutMsgEnd(conn) < 0 || + pqFlush(conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + for (;;) + { + if (needInput) + { + /* Wait for some data to arrive (or for the channel to close) */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + break; + } + + /* + * Scan the message. If we run out of data, loop around to try again. + */ + needInput = true; + + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + continue; + if (pqGetInt(&msgLength, 4, conn)) + continue; + + /* + * Try to validate message type/length here. A length less than 4 is + * definitely broken. Large lengths should only be believed for a few + * message types. + */ + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + break; + } + if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + { + handleSyncLoss(conn, id, msgLength); + break; + } + + /* + * Can't process if message body isn't all here yet. + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before looping, enlarge the input buffer if needed to hold the + * whole message. See notes in parseInput. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + break; + } + continue; + } + + /* + * We should see V or E response to the command, but might get N + * and/or A notices first. We also need to swallow the final Z before + * returning. + */ + switch (id) + { + case 'V': /* function result */ + if (pqGetInt(actual_result_len, 4, conn)) + continue; + if (*actual_result_len != -1) + { + if (result_is_int) + { + if (pqGetInt(result_buf, *actual_result_len, conn)) + continue; + } + else + { + if (pqGetnchar((char *) result_buf, + *actual_result_len, + conn)) + continue; + } + } + /* correctly finished function result message */ + status = PGRES_COMMAND_OK; + break; + case 'E': /* error return */ + if (pqGetErrorNotice3(conn, true)) + continue; + status = PGRES_FATAL_ERROR; + break; + case 'A': /* notify message */ + /* handle notify and go back to processing return values */ + if (getNotify(conn)) + continue; + break; + case 'N': /* notice */ + /* handle notice and go back to processing return values */ + if (pqGetErrorNotice3(conn, false)) + continue; + break; + case 'Z': /* backend is ready for new query */ + if (getReadyForQuery(conn)) + continue; + /* consume the message and exit */ + conn->inStart += 5 + msgLength; + /* if we saved a result object (probably an error), use it */ + if (conn->result) + return pqPrepareAsyncResult(conn); + return PQmakeEmptyPGresult(conn, status); + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + continue; + break; + default: + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + return pqPrepareAsyncResult(conn); + } + /* Completed this message, keep going */ + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + needInput = false; + } + + /* + * We fall out of the loop only upon failing to read data. + * conn->errorMessage has been set by pqWait or pqReadData. We want to + * append it to any already-received error message. + */ + pqSaveErrorResult(conn); + return pqPrepareAsyncResult(conn); +} + + +/* + * Construct startup packet + * + * Returns a malloc'd packet buffer, or NULL if out of memory + */ +char * +pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options) +{ + char *startpacket; + + *packetlen = build_startup_packet(conn, NULL, options); + startpacket = (char *) malloc(*packetlen); + if (!startpacket) + return NULL; + *packetlen = build_startup_packet(conn, startpacket, options); + return startpacket; +} + +/* + * Build a startup packet given a filled-in PGconn structure. + * + * We need to figure out how much space is needed, then fill it in. + * To avoid duplicate logic, this routine is called twice: the first time + * (with packet == NULL) just counts the space needed, the second time + * (with packet == allocated space) fills it in. Return value is the number + * of bytes used. + */ +static int +build_startup_packet(const PGconn *conn, char *packet, + const PQEnvironmentOption *options) +{ + int packet_len = 0; + const PQEnvironmentOption *next_eo; + const char *val; + + /* Protocol version comes first. */ + if (packet) + { + ProtocolVersion pv = htonl(conn->pversion); + + memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion)); + } + packet_len += sizeof(ProtocolVersion); + + /* Add user name, database name, options */ + +#define ADD_STARTUP_OPTION(optname, optval) \ + do { \ + if (packet) \ + strcpy(packet + packet_len, optname); \ + packet_len += strlen(optname) + 1; \ + if (packet) \ + strcpy(packet + packet_len, optval); \ + packet_len += strlen(optval) + 1; \ + } while(0) + + if (conn->pguser && conn->pguser[0]) + ADD_STARTUP_OPTION("user", conn->pguser); + if (conn->dbName && conn->dbName[0]) + ADD_STARTUP_OPTION("database", conn->dbName); + if (conn->replication && conn->replication[0]) + ADD_STARTUP_OPTION("replication", conn->replication); + if (conn->pgoptions && conn->pgoptions[0]) + ADD_STARTUP_OPTION("options", conn->pgoptions); + if (conn->send_appname) + { + /* Use appname if present, otherwise use fallback */ + val = conn->appname ? conn->appname : conn->fbappname; + if (val && val[0]) + ADD_STARTUP_OPTION("application_name", val); + } + + if (conn->client_encoding_initial && conn->client_encoding_initial[0]) + ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial); + + /* Add any environment-driven GUC settings needed */ + for (next_eo = options; next_eo->envName; next_eo++) + { + if ((val = getenv(next_eo->envName)) != NULL) + { + if (pg_strcasecmp(val, "default") != 0) + ADD_STARTUP_OPTION(next_eo->pgName, val); + } + } + + /* Add trailing terminator */ + if (packet) + packet[packet_len] = '\0'; + packet_len++; + + return packet_len; +} diff --git a/src/libpq/fe-secure.c b/src/libpq/fe-secure.c new file mode 100644 index 00000000..9c6ced6a --- /dev/null +++ b/src/libpq/fe-secure.c @@ -0,0 +1,1600 @@ +/*------------------------------------------------------------------------- + * + * fe-secure.c + * functions related to setting up a secure connection to the backend. + * Secure connections are expected to provide confidentiality, + * message integrity and endpoint authentication. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-secure.c + * + * NOTES + * + * We don't provide informational callbacks here (like + * info_cb() in be-secure.c), since there's no good mechanism to + * display such information to the user. + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include +#include + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "pqsignal.h" +#include "libpq-int.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#include + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#endif + +#ifdef USE_SSL + +#include +#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) +#include +#endif +#ifdef USE_SSL_ENGINE +#include +#endif + + +#ifndef WIN32 +#define USER_CERT_FILE ".postgresql/postgresql.crt" +#define USER_KEY_FILE ".postgresql/postgresql.key" +#define ROOT_CERT_FILE ".postgresql/root.crt" +#define ROOT_CRL_FILE ".postgresql/root.crl" +#else +/* On Windows, the "home" directory is already PostgreSQL-specific */ +#define USER_CERT_FILE "postgresql.crt" +#define USER_KEY_FILE "postgresql.key" +#define ROOT_CERT_FILE "root.crt" +#define ROOT_CRL_FILE "root.crl" +#endif + +static bool verify_peer_name_matches_certificate(PGconn *); +static int verify_cb(int ok, X509_STORE_CTX *ctx); +static int init_ssl_system(PGconn *conn); +static void destroy_ssl_system(void); +static int initialize_SSL(PGconn *conn); +static void destroySSL(void); +static PostgresPollingStatusType open_client_SSL(PGconn *); +static void close_SSL(PGconn *); +static char *SSLerrmessage(void); +static void SSLerrfree(char *buf); + +static bool pq_init_ssl_lib = true; +static bool pq_init_crypto_lib = true; +static SSL_CTX *SSL_context = NULL; + +#ifdef ENABLE_THREAD_SAFETY +static long ssl_open_connections = 0; + +#ifndef WIN32 +static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER; +#else +static pthread_mutex_t ssl_config_mutex = NULL; +static long win32_ssl_create_mutex = 0; +#endif +#endif /* ENABLE_THREAD_SAFETY */ +#endif /* SSL */ + + +/* + * Macros to handle disabling and then restoring the state of SIGPIPE handling. + * On Windows, these are all no-ops since there's no SIGPIPEs. + */ + +#ifndef WIN32 + +#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag) + +#ifdef ENABLE_THREAD_SAFETY + +struct sigpipe_info +{ + sigset_t oldsigmask; + bool sigpipe_pending; + bool got_epipe; +}; + +#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo + +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + (spinfo).got_epipe = false; \ + if (!SIGPIPE_MASKED(conn)) \ + { \ + if (pq_block_sigpipe(&(spinfo).oldsigmask, \ + &(spinfo).sigpipe_pending) < 0) \ + failaction; \ + } \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) \ + do { \ + if (cond) \ + (spinfo).got_epipe = true; \ + } while (0) + +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \ + (spinfo).got_epipe); \ + } while (0) +#else /* !ENABLE_THREAD_SAFETY */ + +#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL + +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + spinfo = pqsignal(SIGPIPE, SIG_IGN); \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) + +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pqsignal(SIGPIPE, spinfo); \ + } while (0) +#endif /* ENABLE_THREAD_SAFETY */ +#else /* WIN32 */ + +#define DECLARE_SIGPIPE_INFO(spinfo) +#define DISABLE_SIGPIPE(conn, spinfo, failaction) +#define REMEMBER_EPIPE(spinfo, cond) +#define RESTORE_SIGPIPE(conn, spinfo) +#endif /* WIN32 */ + +/* ------------------------------------------------------------ */ +/* Procedures common to all secure sessions */ +/* ------------------------------------------------------------ */ + + +/* + * Exported function to allow application to tell us it's already + * initialized OpenSSL. + */ +void +PQinitSSL(int do_init) +{ + PQinitOpenSSL(do_init, do_init); +} + +/* + * Exported function to allow application to tell us it's already + * initialized OpenSSL and/or libcrypto. + */ +void +PQinitOpenSSL(int do_ssl, int do_crypto) +{ +#ifdef USE_SSL +#ifdef ENABLE_THREAD_SAFETY + + /* + * Disallow changing the flags while we have open connections, else we'd + * get completely confused. + */ + if (ssl_open_connections != 0) + return; +#endif + + pq_init_ssl_lib = do_ssl; + pq_init_crypto_lib = do_crypto; +#endif +} + +/* + * Initialize global SSL context + */ +int +pqsecure_initialize(PGconn *conn) +{ + int r = 0; + +#ifdef USE_SSL + r = init_ssl_system(conn); +#endif + + return r; +} + +/* + * Destroy global context + */ +void +pqsecure_destroy(void) +{ +#ifdef USE_SSL + destroySSL(); +#endif +} + +/* + * Begin or continue negotiating a secure session. + */ +PostgresPollingStatusType +pqsecure_open_client(PGconn *conn) +{ +#ifdef USE_SSL + /* First time through? */ + if (conn->ssl == NULL) + { + /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */ + conn->sigpipe_flag = false; + + /* Create a connection-specific SSL object */ + if (!(conn->ssl = SSL_new(SSL_context)) || + !SSL_set_app_data(conn->ssl, conn) || + !SSL_set_fd(conn->ssl, conn->sock)) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not establish SSL connection: %s\n"), + err); + SSLerrfree(err); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + + /* + * Load client certificate, private key, and trusted CA certs. + */ + if (initialize_SSL(conn) != 0) + { + /* initialize_SSL already put a message in conn->errorMessage */ + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + } + + /* Begin or continue the actual handshake */ + return open_client_SSL(conn); +#else + /* shouldn't get here */ + return PGRES_POLLING_FAILED; +#endif +} + +/* + * Close secure session. + */ +void +pqsecure_close(PGconn *conn) +{ +#ifdef USE_SSL + if (conn->ssl) + close_SSL(conn); +#endif +} + +/* + * Read data from a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pqsecure_read(PGconn *conn, void *ptr, size_t len) +{ + ssize_t n; + int result_errno = 0; + char sebuf[256]; + +#ifdef USE_SSL + if (conn->ssl) + { + int err; + + DECLARE_SIGPIPE_INFO(spinfo); + + /* SSL_read can write to the socket, so we need to disable SIGPIPE */ + DISABLE_SIGPIPE(conn, spinfo, return -1); + +rloop: + SOCK_ERRNO_SET(0); + n = SSL_read(conn->ssl, ptr, len); + err = SSL_get_error(conn->ssl, n); + switch (err) + { + case SSL_ERROR_NONE: + if (n < 0) + { + /* Not supposed to happen, so we don't translate the msg */ + printfPQExpBuffer(&conn->errorMessage, + "SSL_read failed but did not provide error information\n"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + } + break; + case SSL_ERROR_WANT_READ: + n = 0; + break; + case SSL_ERROR_WANT_WRITE: + + /* + * Returning 0 here would cause caller to wait for read-ready, + * which is not correct since what SSL wants is wait for + * write-ready. The former could get us stuck in an infinite + * wait, so don't risk it; busy-loop instead. + */ + goto rloop; + case SSL_ERROR_SYSCALL: + if (n < 0) + { + result_errno = SOCK_ERRNO; + REMEMBER_EPIPE(spinfo, result_errno == EPIPE); + if (result_errno == EPIPE || + result_errno == ECONNRESET) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + } + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: EOF detected\n")); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + { + char *errm = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL error: %s\n"), errm); + SSLerrfree(errm); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + case SSL_ERROR_ZERO_RETURN: + /* + * Per OpenSSL documentation, this error code is only returned + * for a clean connection closure, so we should not report it + * as a server crash. + */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection has been closed unexpectedly\n")); + result_errno = ECONNRESET; + n = -1; + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error code: %d\n"), + err); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + + RESTORE_SIGPIPE(conn, spinfo); + } + else +#endif /* USE_SSL */ + { + n = recv(conn->sock, ptr, len, 0); + + if (n < 0) + { + result_errno = SOCK_ERRNO; + + /* Set error message if appropriate */ + switch (result_errno) + { +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + /* no error message, caller is expected to retry */ + break; + +#ifdef ECONNRESET + case ECONNRESET: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + break; +#endif + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not receive data from server: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + break; + } + } + } + + /* ensure we return the intended errno to caller */ + SOCK_ERRNO_SET(result_errno); + + return n; +} + +/* + * Write data to a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pqsecure_write(PGconn *conn, const void *ptr, size_t len) +{ + ssize_t n; + int result_errno = 0; + char sebuf[256]; + + DECLARE_SIGPIPE_INFO(spinfo); + +#ifdef USE_SSL + if (conn->ssl) + { + int err; + + DISABLE_SIGPIPE(conn, spinfo, return -1); + + SOCK_ERRNO_SET(0); + n = SSL_write(conn->ssl, ptr, len); + err = SSL_get_error(conn->ssl, n); + switch (err) + { + case SSL_ERROR_NONE: + if (n < 0) + { + /* Not supposed to happen, so we don't translate the msg */ + printfPQExpBuffer(&conn->errorMessage, + "SSL_write failed but did not provide error information\n"); + /* assume the connection is broken */ + result_errno = ECONNRESET; + } + break; + case SSL_ERROR_WANT_READ: + + /* + * Returning 0 here causes caller to wait for write-ready, + * which is not really the right thing, but it's the best we + * can do. + */ + n = 0; + break; + case SSL_ERROR_WANT_WRITE: + n = 0; + break; + case SSL_ERROR_SYSCALL: + if (n < 0) + { + result_errno = SOCK_ERRNO; + REMEMBER_EPIPE(spinfo, result_errno == EPIPE); + if (result_errno == EPIPE || + result_errno == ECONNRESET) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + } + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: EOF detected\n")); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + { + char *errm = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL error: %s\n"), errm); + SSLerrfree(errm); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + case SSL_ERROR_ZERO_RETURN: + /* + * Per OpenSSL documentation, this error code is only returned + * for a clean connection closure, so we should not report it + * as a server crash. + */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL connection has been closed unexpectedly\n")); + result_errno = ECONNRESET; + n = -1; + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error code: %d\n"), + err); + /* assume the connection is broken */ + result_errno = ECONNRESET; + n = -1; + break; + } + } + else +#endif /* USE_SSL */ + { + int flags = 0; + +#ifdef MSG_NOSIGNAL + if (conn->sigpipe_flag) + flags |= MSG_NOSIGNAL; + +retry_masked: +#endif /* MSG_NOSIGNAL */ + + DISABLE_SIGPIPE(conn, spinfo, return -1); + + n = send(conn->sock, ptr, len, flags); + + if (n < 0) + { + result_errno = SOCK_ERRNO; + + /* + * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't + * available on this machine. So, clear sigpipe_flag so we don't + * try the flag again, and retry the send(). + */ +#ifdef MSG_NOSIGNAL + if (flags != 0 && result_errno == EINVAL) + { + conn->sigpipe_flag = false; + flags = 0; + goto retry_masked; + } +#endif /* MSG_NOSIGNAL */ + + /* Set error message if appropriate */ + switch (result_errno) + { +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + /* no error message, caller is expected to retry */ + break; + + case EPIPE: + /* Set flag for EPIPE */ + REMEMBER_EPIPE(spinfo, true); + /* FALL THRU */ + +#ifdef ECONNRESET + case ECONNRESET: +#endif + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + break; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send data to server: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + break; + } + } + } + + RESTORE_SIGPIPE(conn, spinfo); + + /* ensure we return the intended errno to caller */ + SOCK_ERRNO_SET(result_errno); + + return n; +} + +/* ------------------------------------------------------------ */ +/* SSL specific code */ +/* ------------------------------------------------------------ */ +#ifdef USE_SSL + +/* + * Certificate verification callback + * + * This callback allows us to log intermediate problems during + * verification, but there doesn't seem to be a clean way to get + * our PGconn * structure. So we can't log anything! + * + * This callback also allows us to override the default acceptance + * criteria (e.g., accepting self-signed or expired certs), but + * for now we accept the default checks. + */ +static int +verify_cb(int ok, X509_STORE_CTX *ctx) +{ + return ok; +} + + +/* + * Check if a wildcard certificate matches the server hostname. + * + * The rule for this is: + * 1. We only match the '*' character as wildcard + * 2. We match only wildcards at the start of the string + * 3. The '*' character does *not* match '.', meaning that we match only + * a single pathname component. + * 4. We don't support more than one '*' in a single pattern. + * + * This is roughly in line with RFC2818, but contrary to what most browsers + * appear to be implementing (point 3 being the difference) + * + * Matching is always case-insensitive, since DNS is case insensitive. + */ +static int +wildcard_certificate_match(const char *pattern, const char *string) +{ + int lenpat = strlen(pattern); + int lenstr = strlen(string); + + /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */ + if (lenpat < 3 || + pattern[0] != '*' || + pattern[1] != '.') + return 0; + + if (lenpat > lenstr) + /* If pattern is longer than the string, we can never match */ + return 0; + + if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0) + + /* + * If string does not end in pattern (minus the wildcard), we don't + * match + */ + return 0; + + if (strchr(string, '.') < string + lenstr - lenpat) + + /* + * If there is a dot left of where the pattern started to match, we + * don't match (rule 3) + */ + return 0; + + /* String ended with pattern, and didn't have a dot before, so we match */ + return 1; +} + + +/* + * Verify that common name resolves to peer. + */ +static bool +verify_peer_name_matches_certificate(PGconn *conn) +{ + /* + * If told not to verify the peer name, don't do it. Return true + * indicating that the verification was successful. + */ + if (strcmp(conn->sslmode, "verify-full") != 0) + return true; + + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified for a verified SSL connection\n")); + return false; + } + else + { + /* + * Compare CN to originally given hostname. + * + * XXX: Should support alternate names here + */ + if (pg_strcasecmp(conn->peer_cn, conn->pghost) == 0) + /* Exact name match */ + return true; + else if (wildcard_certificate_match(conn->peer_cn, conn->pghost)) + /* Matched wildcard certificate */ + return true; + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"), + conn->peer_cn, conn->pghost); + return false; + } + } +} + +#ifdef ENABLE_THREAD_SAFETY +/* + * Callback functions for OpenSSL internal locking + */ + +static unsigned long +pq_threadidcallback(void) +{ + /* + * This is not standards-compliant. pthread_self() returns pthread_t, and + * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires + * it, so we have to do it. + */ + return (unsigned long) pthread_self(); +} + +static pthread_mutex_t *pq_lockarray; + +static void +pq_lockingcallback(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) + { + if (pthread_mutex_lock(&pq_lockarray[n])) + PGTHREAD_ERROR("failed to lock mutex"); + } + else + { + if (pthread_mutex_unlock(&pq_lockarray[n])) + PGTHREAD_ERROR("failed to unlock mutex"); + } +} +#endif /* ENABLE_THREAD_SAFETY */ + +/* + * Initialize SSL system, in particular creating the SSL_context object + * that will be shared by all SSL-using connections in this process. + * + * In threadsafe mode, this includes setting up libcrypto callback functions + * to do thread locking. + * + * If the caller has told us (through PQinitOpenSSL) that he's taking care + * of libcrypto, we expect that callbacks are already set, and won't try to + * override it. + * + * The conn parameter is only used to be able to pass back an error + * message - no connection-local setup is made here. + * + * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage). + */ +static int +init_ssl_system(PGconn *conn) +{ +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 + /* Also see similar code in fe-connect.c, default_threadlock() */ + if (ssl_config_mutex == NULL) + { + while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1) + /* loop, another thread own the lock */ ; + if (ssl_config_mutex == NULL) + { + if (pthread_mutex_init(&ssl_config_mutex, NULL)) + return -1; + } + InterlockedExchange(&win32_ssl_create_mutex, 0); + } +#endif + if (pthread_mutex_lock(&ssl_config_mutex)) + return -1; + + if (pq_init_crypto_lib) + { + /* + * If necessary, set up an array to hold locks for libcrypto. + * libcrypto will tell us how big to make this array. + */ + if (pq_lockarray == NULL) + { + int i; + + pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); + if (!pq_lockarray) + { + pthread_mutex_unlock(&ssl_config_mutex); + return -1; + } + for (i = 0; i < CRYPTO_num_locks(); i++) + { + if (pthread_mutex_init(&pq_lockarray[i], NULL)) + { + free(pq_lockarray); + pq_lockarray = NULL; + pthread_mutex_unlock(&ssl_config_mutex); + return -1; + } + } + } + + if (ssl_open_connections++ == 0) + { + /* These are only required for threaded libcrypto applications */ + CRYPTO_set_id_callback(pq_threadidcallback); + CRYPTO_set_locking_callback(pq_lockingcallback); + } + } +#endif /* ENABLE_THREAD_SAFETY */ + + if (!SSL_context) + { + if (pq_init_ssl_lib) + { +#if SSLEAY_VERSION_NUMBER >= 0x00907000L + OPENSSL_config(NULL); +#endif + SSL_library_init(); + SSL_load_error_strings(); + } + + SSL_context = SSL_CTX_new(TLSv1_method()); + if (!SSL_context) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create SSL context: %s\n"), + err); + SSLerrfree(err); +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif + return -1; + } + + /* + * Disable OpenSSL's moving-write-buffer sanity check, because it + * causes unnecessary failures in nonblocking send cases. + */ + SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + } + +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif + return 0; +} + +/* + * This function is needed because if the libpq library is unloaded + * from the application, the callback functions will no longer exist when + * libcrypto is used by other parts of the system. For this reason, + * we unregister the callback functions when the last libpq + * connection is closed. (The same would apply for OpenSSL callbacks + * if we had any.) + * + * Callbacks are only set when we're compiled in threadsafe mode, so + * we only need to remove them in this case. + */ +static void +destroy_ssl_system(void) +{ +#ifdef ENABLE_THREAD_SAFETY + /* Mutex is created in initialize_ssl_system() */ + if (pthread_mutex_lock(&ssl_config_mutex)) + return; + + if (pq_init_crypto_lib && ssl_open_connections > 0) + --ssl_open_connections; + + if (pq_init_crypto_lib && ssl_open_connections == 0) + { + /* No connections left, unregister libcrypto callbacks */ + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + /* + * We don't free the lock array. If we get another connection in this + * process, we will just re-use it with the existing mutexes. + * + * This means we leak a little memory on repeated load/unload of the + * library. + */ + } + + pthread_mutex_unlock(&ssl_config_mutex); +#endif +} + +/* + * Initialize (potentially) per-connection SSL data, namely the + * client certificate, private key, and trusted CA certs. + * + * conn->ssl must already be created. It receives the connection's client + * certificate and private key. Note however that certificates also get + * loaded into the SSL_context object, and are therefore accessible to all + * connections in this process. This should be OK as long as there aren't + * any hash collisions among the certs. + * + * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage). + */ +static int +initialize_SSL(PGconn *conn) +{ + struct stat buf; + char homedir[MAXPGPATH]; + char fnbuf[MAXPGPATH]; + char sebuf[256]; + bool have_homedir; + bool have_cert; + EVP_PKEY *pkey = NULL; + + /* + * We'll need the home directory if any of the relevant parameters are + * defaulted. If pqGetHomeDirectory fails, act as though none of the + * files could be found. + */ + if (!(conn->sslcert && strlen(conn->sslcert) > 0) || + !(conn->sslkey && strlen(conn->sslkey) > 0) || + !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) || + !(conn->sslcrl && strlen(conn->sslcrl) > 0)) + have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir)); + else /* won't need it */ + have_homedir = false; + + /* Read the client certificate file */ + if (conn->sslcert && strlen(conn->sslcert) > 0) + strncpy(fnbuf, conn->sslcert, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE); + else + fnbuf[0] = '\0'; + + if (fnbuf[0] == '\0') + { + /* no home directory, proceed without a client cert */ + have_cert = false; + } + else if (stat(fnbuf, &buf) != 0) + { + /* + * If file is not present, just go on without a client cert; server + * might or might not accept the connection. Any other error, + * however, is grounds for complaint. + */ + if (errno != ENOENT) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open certificate file \"%s\": %s\n"), + fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); + return -1; + } + have_cert = false; + } + else + { + /* + * Cert file exists, so load it. Since OpenSSL doesn't provide the + * equivalent of "SSL_use_certificate_chain_file", we actually have to + * load the file twice. The first call loads any extra certs after + * the first one into chain-cert storage associated with the + * SSL_context. The second call loads the first cert (only) into the + * SSL object, where it will be correctly paired with the private key + * we load below. We do it this way so that each connection + * understands which subject cert to present, in case different + * sslcert settings are used for different connections in the same + * process. + */ + if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read certificate file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + return -1; + } + if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read certificate file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + return -1; + } + /* need to load the associated private key, too */ + have_cert = true; + } + + /* + * Read the SSL key. If a key is specified, treat it as an engine:key + * combination if there is colon present - we don't support files with + * colon in the name. The exception is if the second character is a colon, + * in which case it can be a Windows filename with drive specification. + */ + if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0) + { +#ifdef USE_SSL_ENGINE + if (strchr(conn->sslkey, ':') +#ifdef WIN32 + && conn->sslkey[1] != ':' +#endif + ) + { + /* Colon, but not in second character, treat as engine:key */ + char *engine_str = strdup(conn->sslkey); + char *engine_colon = strchr(engine_str, ':'); + + *engine_colon = '\0'; /* engine_str now has engine name */ + engine_colon++; /* engine_colon now has key name */ + + conn->engine = ENGINE_by_id(engine_str); + if (conn->engine == NULL) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load SSL engine \"%s\": %s\n"), + engine_str, err); + SSLerrfree(err); + free(engine_str); + return -1; + } + + if (ENGINE_init(conn->engine) == 0) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not initialize SSL engine \"%s\": %s\n"), + engine_str, err); + SSLerrfree(err); + ENGINE_free(conn->engine); + conn->engine = NULL; + free(engine_str); + return -1; + } + + pkey = ENGINE_load_private_key(conn->engine, engine_colon, + NULL, NULL); + if (pkey == NULL) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"), + engine_colon, engine_str, err); + SSLerrfree(err); + ENGINE_finish(conn->engine); + ENGINE_free(conn->engine); + conn->engine = NULL; + free(engine_str); + return -1; + } + if (SSL_use_PrivateKey(conn->ssl, pkey) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"), + engine_colon, engine_str, err); + SSLerrfree(err); + ENGINE_finish(conn->engine); + ENGINE_free(conn->engine); + conn->engine = NULL; + free(engine_str); + return -1; + } + + free(engine_str); + + fnbuf[0] = '\0'; /* indicate we're not going to load from a + * file */ + } + else +#endif /* USE_SSL_ENGINE */ + { + /* PGSSLKEY is not an engine, treat it as a filename */ + strncpy(fnbuf, conn->sslkey, sizeof(fnbuf)); + } + } + else if (have_homedir) + { + /* No PGSSLKEY specified, load default file */ + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE); + } + else + fnbuf[0] = '\0'; + + if (have_cert && fnbuf[0] != '\0') + { + /* read the client key from file */ + + if (stat(fnbuf, &buf) != 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate present, but not private key file \"%s\"\n"), + fnbuf); + return -1; + } +#ifndef WIN32 + if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"), + fnbuf); + return -1; + } +#endif + + if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load private key file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + return -1; + } + } + + /* verify that the cert and key go together */ + if (have_cert && + SSL_check_private_key(conn->ssl) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate does not match private key file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + return -1; + } + + /* + * If the root cert file exists, load it so we can perform certificate + * verification. If sslmode is "verify-full" we will also do further + * verification after the connection has been completed. + */ + if (conn->sslrootcert && strlen(conn->sslrootcert) > 0) + strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE); + else + fnbuf[0] = '\0'; + + if (fnbuf[0] != '\0' && + stat(fnbuf, &buf) == 0) + { + X509_STORE *cvstore; + + if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read root certificate file \"%s\": %s\n"), + fnbuf, err); + SSLerrfree(err); + return -1; + } + + if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL) + { + if (conn->sslcrl && strlen(conn->sslcrl) > 0) + strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf)); + else if (have_homedir) + snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE); + else + fnbuf[0] = '\0'; + + /* Set the flags to check against the complete CRL chain */ + if (fnbuf[0] != '\0' && + X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1) + { + /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */ +#ifdef X509_V_FLAG_CRL_CHECK + X509_STORE_set_flags(cvstore, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); +#else + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"), + fnbuf); + SSLerrfree(err); + return -1; +#endif + } + /* if not found, silently ignore; we do not require CRL */ + } + + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb); + } + else + { + /* + * stat() failed; assume root file doesn't exist. If sslmode is + * verify-ca or verify-full, this is an error. Otherwise, continue + * without performing any server cert verification. + */ + if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */ + { + /* + * The only way to reach here with an empty filename is if + * pqGetHomeDirectory failed. That's a sufficiently unusual case + * that it seems worth having a specialized error message for it. + */ + if (fnbuf[0] == '\0') + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get home directory to locate root certificate file\n" + "Either provide the file or change sslmode to disable server certificate verification.\n")); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("root certificate file \"%s\" does not exist\n" + "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf); + return -1; + } + } + + return 0; +} + +static void +destroySSL(void) +{ + destroy_ssl_system(); +} + +/* + * Attempt to negotiate SSL connection. + */ +static PostgresPollingStatusType +open_client_SSL(PGconn *conn) +{ + int r; + + r = SSL_connect(conn->ssl); + if (r <= 0) + { + int err = SSL_get_error(conn->ssl, r); + + switch (err) + { + case SSL_ERROR_WANT_READ: + return PGRES_POLLING_READING; + + case SSL_ERROR_WANT_WRITE: + return PGRES_POLLING_WRITING; + + case SSL_ERROR_SYSCALL: + { + char sebuf[256]; + + if (r == -1) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL SYSCALL error: EOF detected\n")); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + case SSL_ERROR_SSL: + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL error: %s\n"), + err); + SSLerrfree(err); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unrecognized SSL error code: %d\n"), + err); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + } + + /* + * We already checked the server certificate in initialize_SSL() using + * SSL_CTX_set_verify(), if root.crt exists. + */ + + /* pull out server distinguished and common names */ + conn->peer = SSL_get_peer_certificate(conn->ssl); + if (conn->peer == NULL) + { + char *err = SSLerrmessage(); + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate could not be obtained: %s\n"), + err); + SSLerrfree(err); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + + X509_NAME_oneline(X509_get_subject_name(conn->peer), + conn->peer_dn, sizeof(conn->peer_dn)); + conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0'; + + r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), + NID_commonName, conn->peer_cn, SM_USER); + conn->peer_cn[SM_USER] = '\0'; /* buffer is SM_USER+1 chars! */ + if (r == -1) + { + /* Unable to get the CN, set it to blank so it can't be used */ + conn->peer_cn[0] = '\0'; + } + else + { + /* + * Reject embedded NULLs in certificate common name to prevent attacks + * like CVE-2009-4034. + */ + if (r != strlen(conn->peer_cn)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSL certificate's common name contains embedded null\n")); + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + } + + if (!verify_peer_name_matches_certificate(conn)) + { + close_SSL(conn); + return PGRES_POLLING_FAILED; + } + + /* SSL handshake is complete */ + return PGRES_POLLING_OK; +} + +/* + * Close SSL connection. + */ +static void +close_SSL(PGconn *conn) +{ + if (conn->ssl) + { + DECLARE_SIGPIPE_INFO(spinfo); + + DISABLE_SIGPIPE(conn, spinfo, (void) 0); + SSL_shutdown(conn->ssl); + SSL_free(conn->ssl); + conn->ssl = NULL; + pqsecure_destroy(); + /* We have to assume we got EPIPE */ + REMEMBER_EPIPE(spinfo, true); + RESTORE_SIGPIPE(conn, spinfo); + } + + if (conn->peer) + { + X509_free(conn->peer); + conn->peer = NULL; + } + +#ifdef USE_SSL_ENGINE + if (conn->engine) + { + ENGINE_finish(conn->engine); + ENGINE_free(conn->engine); + conn->engine = NULL; + } +#endif +} + +/* + * Obtain reason string for last SSL error + * + * Some caution is needed here since ERR_reason_error_string will + * return NULL if it doesn't recognize the error code. We don't + * want to return NULL ever. + */ +static char ssl_nomem[] = "out of memory allocating error description"; + +#define SSL_ERR_LEN 128 + +static char * +SSLerrmessage(void) +{ + unsigned long errcode; + const char *errreason; + char *errbuf; + + errbuf = malloc(SSL_ERR_LEN); + if (!errbuf) + return ssl_nomem; + errcode = ERR_get_error(); + if (errcode == 0) + { + snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported")); + return errbuf; + } + errreason = ERR_reason_error_string(errcode); + if (errreason != NULL) + { + strlcpy(errbuf, errreason, SSL_ERR_LEN); + return errbuf; + } + snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode); + return errbuf; +} + +static void +SSLerrfree(char *buf) +{ + if (buf != ssl_nomem) + free(buf); +} + +/* + * Return pointer to OpenSSL object. + */ +void * +PQgetssl(PGconn *conn) +{ + if (!conn) + return NULL; + return conn->ssl; +} +#else /* !USE_SSL */ + +void * +PQgetssl(PGconn *conn) +{ + return NULL; +} +#endif /* USE_SSL */ + + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) + +/* + * Block SIGPIPE for this thread. This prevents send()/write() from exiting + * the application. + */ +int +pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending) +{ + sigset_t sigpipe_sigset; + sigset_t sigset; + + sigemptyset(&sigpipe_sigset); + sigaddset(&sigpipe_sigset, SIGPIPE); + + /* Block SIGPIPE and save previous mask for later reset */ + SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset)); + if (SOCK_ERRNO) + return -1; + + /* We can have a pending SIGPIPE only if it was blocked before */ + if (sigismember(osigset, SIGPIPE)) + { + /* Is there a pending SIGPIPE? */ + if (sigpending(&sigset) != 0) + return -1; + + if (sigismember(&sigset, SIGPIPE)) + *sigpipe_pending = true; + else + *sigpipe_pending = false; + } + else + *sigpipe_pending = false; + + return 0; +} + +/* + * Discard any pending SIGPIPE and reset the signal mask. + * + * Note: we are effectively assuming here that the C library doesn't queue + * up multiple SIGPIPE events. If it did, then we'd accidentally leave + * ours in the queue when an event was already pending and we got another. + * As long as it doesn't queue multiple events, we're OK because the caller + * can't tell the difference. + * + * The caller should say got_epipe = FALSE if it is certain that it + * didn't get an EPIPE error; in that case we'll skip the clear operation + * and things are definitely OK, queuing or no. If it got one or might have + * gotten one, pass got_epipe = TRUE. + * + * We do not want this to change errno, since if it did that could lose + * the error code from a preceding send(). We essentially assume that if + * we were able to do pq_block_sigpipe(), this can't fail. + */ +void +pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe) +{ + int save_errno = SOCK_ERRNO; + int signo; + sigset_t sigset; + + /* Clear SIGPIPE only if none was pending */ + if (got_epipe && !sigpipe_pending) + { + if (sigpending(&sigset) == 0 && + sigismember(&sigset, SIGPIPE)) + { + sigset_t sigpipe_sigset; + + sigemptyset(&sigpipe_sigset); + sigaddset(&sigpipe_sigset, SIGPIPE); + + sigwait(&sigpipe_sigset, &signo); + } + } + + /* Restore saved block mask */ + pthread_sigmask(SIG_SETMASK, osigset, NULL); + + SOCK_ERRNO_SET(save_errno); +} + +#endif /* ENABLE_THREAD_SAFETY && !WIN32 */ diff --git a/src/libpq/getaddrinfo.c b/src/libpq/getaddrinfo.c new file mode 100644 index 00000000..bbb044b8 --- /dev/null +++ b/src/libpq/getaddrinfo.c @@ -0,0 +1,417 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.c + * Support getaddrinfo() on platforms that don't have it. + * + * We also supply getnameinfo() here, assuming that the platform will have + * it if and only if it has getaddrinfo(). If this proves false on some + * platform, we'll need to split this file and provide a separate configure + * test for getnameinfo(). + * + * Windows may or may not have these routines, so we handle Windows specially + * by dynamically checking for their existence. If they already exist, we + * use the Windows native routines, but if not, we use our own. + * + * + * Copyright (c) 2003-2011, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/getaddrinfo.c + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include +#include +#include +#include + +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" /* needed for struct sockaddr_storage */ + + +#ifdef WIN32 +/* + * The native routines may or may not exist on the Windows platform we are on, + * so we dynamically look up the routines, and call them via function pointers. + * Here we need to declare what the function pointers look like + */ +typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename, + const char *servname, + const struct addrinfo * hints, + struct addrinfo ** res); + +typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai); + +typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa, + int salen, + char *host, int hostlen, + char *serv, int servlen, + int flags); + +/* static pointers to the native routines, so we only do the lookup once. */ +static getaddrinfo_ptr_t getaddrinfo_ptr = NULL; +static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL; +static getnameinfo_ptr_t getnameinfo_ptr = NULL; + + +static bool +haveNativeWindowsIPv6routines(void) +{ + void *hLibrary = NULL; + static bool alreadyLookedForIpv6routines = false; + + if (alreadyLookedForIpv6routines) + return (getaddrinfo_ptr != NULL); + + /* + * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines + * are present in the WinSock 2 library (ws2_32.dll). Try that first + */ + + hLibrary = LoadLibraryA("ws2_32"); + + if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL) + { + /* + * Well, ws2_32 doesn't exist, or more likely doesn't have + * getaddrinfo. + */ + if (hLibrary != NULL) + FreeLibrary(hLibrary); + + /* + * In Windows 2000, there was only the IPv6 Technology Preview look in + * the IPv6 WinSock library (wship6.dll). + */ + + hLibrary = LoadLibraryA("wship6"); + } + + /* If hLibrary is null, we couldn't find a dll with functions */ + if (hLibrary != NULL) + { + /* We found a dll, so now get the addresses of the routines */ + + getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary, + "getaddrinfo"); + freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary, + "freeaddrinfo"); + getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary, + "getnameinfo"); + + /* + * If any one of the routines is missing, let's play it safe and + * ignore them all + */ + if (getaddrinfo_ptr == NULL || + freeaddrinfo_ptr == NULL || + getnameinfo_ptr == NULL) + { + FreeLibrary(hLibrary); + hLibrary = NULL; + getaddrinfo_ptr = NULL; + freeaddrinfo_ptr = NULL; + getnameinfo_ptr = NULL; + } + } + + alreadyLookedForIpv6routines = true; + return (getaddrinfo_ptr != NULL); +} +#endif + + +/* + * get address info for ipv4 sockets. + * + * Bugs: - only one addrinfo is set even though hintp is NULL or + * ai_socktype is 0 + * - AI_CANONNAME is not supported. + * - servname can only be a number, not text. + */ +int +getaddrinfo(const char *node, const char *service, + const struct addrinfo * hintp, + struct addrinfo ** res) +{ + struct addrinfo *ai; + struct sockaddr_in sin, + *psin; + struct addrinfo hints; + +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getaddrinfo_ptr) (node, service, hintp, res); +#endif + + if (hintp == NULL) + { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + } + else + memcpy(&hints, hintp, sizeof(hints)); + + if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) + return EAI_FAMILY; + + if (hints.ai_socktype == 0) + hints.ai_socktype = SOCK_STREAM; + + if (!node && !service) + return EAI_NONAME; + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + + if (node) + { + if (node[0] == '\0') + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else if (hints.ai_flags & AI_NUMERICHOST) + { + if (!inet_aton(node, &sin.sin_addr)) + return EAI_FAIL; + } + else + { + struct hostent *hp; + +#ifdef FRONTEND + struct hostent hpstr; + char buf[BUFSIZ]; + int herrno = 0; + + pqGethostbyname(node, &hpstr, buf, sizeof(buf), + &hp, &herrno); +#else + hp = gethostbyname(node); +#endif + if (hp == NULL) + { + switch (h_errno) + { + case HOST_NOT_FOUND: + case NO_DATA: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + case NO_RECOVERY: + default: + return EAI_FAIL; + } + } + if (hp->h_addrtype != AF_INET) + return EAI_FAIL; + + memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length); + } + } + else + { + if (hints.ai_flags & AI_PASSIVE) + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + if (service) + sin.sin_port = htons((unsigned short) atoi(service)); + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + sin.sin_len = sizeof(sin); +#endif + + ai = malloc(sizeof(*ai)); + if (!ai) + return EAI_MEMORY; + + psin = malloc(sizeof(*psin)); + if (!psin) + { + free(ai); + return EAI_MEMORY; + } + + memcpy(psin, &sin, sizeof(*psin)); + + ai->ai_flags = 0; + ai->ai_family = AF_INET; + ai->ai_socktype = hints.ai_socktype; + ai->ai_protocol = hints.ai_protocol; + ai->ai_addrlen = sizeof(*psin); + ai->ai_addr = (struct sockaddr *) psin; + ai->ai_canonname = NULL; + ai->ai_next = NULL; + + *res = ai; + + return 0; +} + + +void +freeaddrinfo(struct addrinfo * res) +{ + if (res) + { +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + { + (*freeaddrinfo_ptr) (res); + return; + } +#endif + + if (res->ai_addr) + free(res->ai_addr); + free(res); + } +} + + +const char * +gai_strerror(int errcode) +{ +#ifdef HAVE_HSTRERROR + int hcode; + + switch (errcode) + { + case EAI_NONAME: + hcode = HOST_NOT_FOUND; + break; + case EAI_AGAIN: + hcode = TRY_AGAIN; + break; + case EAI_FAIL: + default: + hcode = NO_RECOVERY; + break; + } + + return hstrerror(hcode); +#else /* !HAVE_HSTRERROR */ + + switch (errcode) + { + case EAI_NONAME: + return "Unknown host"; + case EAI_AGAIN: + return "Host name lookup failure"; + /* Errors below are probably WIN32 only */ +#ifdef EAI_BADFLAGS + case EAI_BADFLAGS: + return "Invalid argument"; +#endif +#ifdef EAI_FAMILY + case EAI_FAMILY: + return "Address family not supported"; +#endif +#ifdef EAI_MEMORY + case EAI_MEMORY: + return "Not enough memory"; +#endif +#ifdef EAI_NODATA +#if EAI_NODATA != EAI_NONAME +#if !defined(WIN64) && !defined(WIN32_ONLY_COMPILER) /* MSVC/WIN64 duplicate */ + case EAI_NODATA: + return "No host data of that type was found"; +#endif +#endif +#endif +#ifdef EAI_SERVICE + case EAI_SERVICE: + return "Class type not found"; +#endif +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + return "Socket type not supported"; +#endif + default: + return "Unknown server error"; + } +#endif /* HAVE_HSTRERROR */ +} + +/* + * Convert an ipv4 address to a hostname. + * + * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV + * It will never resolv a hostname. + * - No IPv6 support. + */ +int +getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags) +{ +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getnameinfo_ptr) (sa, salen, node, nodelen, + service, servicelen, flags); +#endif + + /* Invalid arguments. */ + if (sa == NULL || (node == NULL && service == NULL)) + return EAI_FAIL; + + /* We don't support those. */ + if ((node && !(flags & NI_NUMERICHOST)) + || (service && !(flags & NI_NUMERICSERV))) + return EAI_FAIL; + +#ifdef HAVE_IPV6 + if (sa->sa_family == AF_INET6) + return EAI_FAMILY; +#endif + + if (node) + { + if (sa->sa_family == AF_INET) + { + if (inet_net_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr, + sa->sa_family == AF_INET ? 32 : 128, + node, nodelen) == NULL) + return EAI_MEMORY; + } + else + return EAI_MEMORY; + } + + if (service) + { + int ret = -1; + + if (sa->sa_family == AF_INET) + { + ret = snprintf(service, servicelen, "%d", + ntohs(((struct sockaddr_in *) sa)->sin_port)); + } + if (ret == -1 || ret > servicelen) + return EAI_MEMORY; + } + + return 0; +} diff --git a/src/libpq/getaddrinfo.h b/src/libpq/getaddrinfo.h new file mode 100644 index 00000000..21c04132 --- /dev/null +++ b/src/libpq/getaddrinfo.h @@ -0,0 +1,161 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.h + * Support getaddrinfo() on platforms that don't have it. + * + * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO, + * whether or not the library routine getaddrinfo() can be found. This + * policy is needed because on some platforms a manually installed libbind.a + * may provide getaddrinfo(), yet the system headers may not provide the + * struct definitions needed to call it. To avoid conflict with the libbind + * definition in such cases, we rename our routines to pg_xxx() via macros. + * + * This code will also work on platforms where struct addrinfo is defined + * in the system headers but no getaddrinfo() can be located. + * + * Copyright (c) 2003-2011, PostgreSQL Global Development Group + * + * src/include/getaddrinfo.h + * + *------------------------------------------------------------------------- + */ +#ifndef GETADDRINFO_H +#define GETADDRINFO_H + +#include +#include + + +/* Various macros that ought to be in , but might not be */ + +#ifndef EAI_FAIL +#ifndef WIN32 +#define EAI_BADFLAGS (-1) +#define EAI_NONAME (-2) +#define EAI_AGAIN (-3) +#define EAI_FAIL (-4) +#define EAI_FAMILY (-6) +#define EAI_SOCKTYPE (-7) +#define EAI_SERVICE (-8) +#define EAI_MEMORY (-10) +#define EAI_SYSTEM (-11) +#else /* WIN32 */ +#ifdef WIN32_ONLY_COMPILER +#ifndef WSA_NOT_ENOUGH_MEMORY +#define WSA_NOT_ENOUGH_MEMORY (WSAENOBUFS) +#endif +#ifndef __BORLANDC__ +#define WSATYPE_NOT_FOUND (WSABASEERR+109) +#endif +#endif +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NODATA WSANO_DATA +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif /* !WIN32 */ +#endif /* !EAI_FAIL */ + +#ifndef AI_PASSIVE +#define AI_PASSIVE 0x0001 +#endif + +#ifndef AI_NUMERICHOST +/* + * some platforms don't support AI_NUMERICHOST; define as zero if using + * the system version of getaddrinfo... + */ +#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO) +#define AI_NUMERICHOST 0 +#else +#define AI_NUMERICHOST 0x0004 +#endif +#endif + +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 1 +#endif +#ifndef NI_NUMERICSERV +#define NI_NUMERICSERV 2 +#endif + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + + +#ifndef HAVE_STRUCT_ADDRINFO + +#ifndef WIN32 +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +#else +/* + * The order of the structure elements on Win32 doesn't match the + * order specified in the standard, but we have to match it for + * IPv6 to work. + */ +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#endif +#endif /* HAVE_STRUCT_ADDRINFO */ + + +#ifndef HAVE_GETADDRINFO + +/* Rename private copies per comments above */ +#ifdef getaddrinfo +#undef getaddrinfo +#endif +#define getaddrinfo pg_getaddrinfo + +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif +#define freeaddrinfo pg_freeaddrinfo + +#ifdef gai_strerror +#undef gai_strerror +#endif +#define gai_strerror pg_gai_strerror + +#ifdef getnameinfo +#undef getnameinfo +#endif +#define getnameinfo pg_getnameinfo + +extern int getaddrinfo(const char *node, const char *service, + const struct addrinfo * hints, struct addrinfo ** res); +extern void freeaddrinfo(struct addrinfo * res); +extern const char *gai_strerror(int errcode); +extern int getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags); +#endif /* HAVE_GETADDRINFO */ + +#endif /* GETADDRINFO_H */ diff --git a/src/libpq/inet_aton.c b/src/libpq/inet_aton.c new file mode 100644 index 00000000..473f51f8 --- /dev/null +++ b/src/libpq/inet_aton.c @@ -0,0 +1,147 @@ +/* src/port/inet_aton.c + * + * This inet_aton() function was taken from the GNU C library and + * incorporated into Postgres for those systems which do not have this + * routine in their standard C libraries. + * + * The function was been extracted whole from the file inet_aton.c in + * Release 5.3.12 of the Linux C library, which is derived from the + * GNU C library, by Bryan Henderson in October 1996. The copyright + * notice from that file is below. + */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "c.h" + +#include +#include + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr * addr) +{ + unsigned int val; + int base, + n; + char c; + u_int parts[4]; + u_int *pp = parts; + + for (;;) + { + /* + * Collect number up to ``.''. Values are specified as for C: 0x=hex, + * 0=octal, other=decimal. + */ + val = 0; + base = 10; + if (*cp == '0') + { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') + { + if (isdigit((unsigned char) c)) + { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isxdigit((unsigned char) c)) + { + val = (val << 4) + + (c + 10 - (islower((unsigned char) c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') + { + /* + * Internet format: a.b.c.d a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return 0; + *pp++ = val, cp++; + } + else + break; + } + + /* + * Check for trailing junk. + */ + while (*cp) + if (!isspace((unsigned char) *cp++)) + return 0; + + /* + * Concoct the address according to the number of parts specified. + */ + n = pp - parts + 1; + switch (n) + { + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return 0; + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return 1; +} diff --git a/src/libpq/inet_net_ntop.c b/src/libpq/inet_net_ntop.c new file mode 100644 index 00000000..047895e4 --- /dev/null +++ b/src/libpq/inet_net_ntop.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * src/backend/utils/adt/inet_net_ntop.c + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $"; +#endif + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include +#include + +#ifndef FRONTEND +#include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif + + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + +#ifdef SPRINTF_CHAR +#define SPRINTF(x) strlen(sprintf/**/x) +#else +#define SPRINTF(x) ((size_t)sprintf x) +#endif + +static char *inet_net_ntop_ipv4(const u_char *src, int bits, + char *dst, size_t size); +static char *inet_net_ntop_ipv6(const u_char *src, int bits, + char *dst, size_t size); + + +/* + * char * + * inet_net_ntop(af, src, bits, dst, size) + * convert host/network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_net_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +char * +inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) +{ + /* + * We need to cover both the address family constants used by the PG inet + * type (PGSQL_AF_INET and PGSQL_AF_INET6) and those used by the system + * libraries (AF_INET and AF_INET6). We can safely assume PGSQL_AF_INET + * == AF_INET, but the INET6 constants are very likely to be different. If + * AF_INET6 isn't defined, silently ignore it. + */ + switch (af) + { + case PGSQL_AF_INET: + return (inet_net_ntop_ipv4(src, bits, dst, size)); + case PGSQL_AF_INET6: +#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6 + case AF_INET6: +#endif + return (inet_net_ntop_ipv6(src, bits, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } +} + +/* + * static char * + * inet_net_ntop_ipv4(src, bits, dst, size) + * convert IPv4 network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), October 1998 + */ +static char * +inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int len = 4; + int b; + + if (bits < 0 || bits > 32) + { + errno = EINVAL; + return (NULL); + } + + /* Always format all four octets, regardless of mask length. */ + for (b = len; b > 0; b--) + { + if (size <= sizeof ".255") + goto emsgsize; + t = dst; + if (dst != odst) + *dst++ = '.'; + dst += SPRINTF((dst, "%u", *src++)); + size -= (size_t) (dst - t); + } + + /* don't print masklen if 32 bits */ + if (bits != 32) + { + if (size <= sizeof "/32") + goto emsgsize; + dst += SPRINTF((dst, "/%u", bits)); + } + + return (odst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +static int +decoct(const u_char *src, int bytes, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int b; + + for (b = 1; b <= bytes; b++) + { + if (size <= sizeof "255.") + return (0); + t = dst; + dst += SPRINTF((dst, "%u", *src++)); + if (b != bytes) + { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t) (dst - t); + } + return (dst - odst); +} + +static char * +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough to + * contain a value of the specified size. On some systems, like Crays, + * there is no such thing as an integer variable with 16 bits. Keep this + * in mind if you think this function should have been coded to use + * pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; + char *tp; + struct + { + int base, + len; + } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + if ((bits < -1) || (bits > 128)) + { + errno = EINVAL; + return (NULL); + } + + /* + * Preprocess: Copy the input (bytewise) array into a wordwise array. Find + * the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + best.len = 0; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else + { + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) + { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) + { + int n; + + n = decoct(src + 12, 4, tp, sizeof tmp - (tp - tmp)); + if (n == 0) + { + errno = EMSGSIZE; + return (NULL); + } + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp = '\0'; + + if (bits != -1 && bits != 128) + tp += SPRINTF((tp, "/%u", bits)); + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t) (tp - tmp) > size) + { + errno = EMSGSIZE; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} diff --git a/src/libpq/ip.c b/src/libpq/ip.c new file mode 100644 index 00000000..cc6c2aba --- /dev/null +++ b/src/libpq/ip.c @@ -0,0 +1,897 @@ +/*------------------------------------------------------------------------- + * + * ip.c + * IPv6-aware network access. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/libpq/ip.c + * + * This file and the IPV6 implementation were initially provided by + * Nigel Kukard , Linux Based Systems Design + * http://www.lbsd.net. + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#include + +#include "libpq/ip.h" + + +static int range_sockaddr_AF_INET(const struct sockaddr_in * addr, + const struct sockaddr_in * netaddr, + const struct sockaddr_in * netmask); + +#ifdef HAVE_IPV6 +static int range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr, + const struct sockaddr_in6 * netaddr, + const struct sockaddr_in6 * netmask); +#endif + +#ifdef HAVE_UNIX_SOCKETS +static int getaddrinfo_unix(const char *path, + const struct addrinfo * hintsp, + struct addrinfo ** result); + +static int getnameinfo_unix(const struct sockaddr_un * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags); +#endif + + +/* + * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets + */ +int +pg_getaddrinfo_all(const char *hostname, const char *servname, + const struct addrinfo * hintp, struct addrinfo ** result) +{ + int rc; + + /* not all versions of getaddrinfo() zero *result on failure */ + *result = NULL; + +#ifdef HAVE_UNIX_SOCKETS + if (hintp->ai_family == AF_UNIX) + return getaddrinfo_unix(servname, hintp, result); +#endif + + /* NULL has special meaning to getaddrinfo(). */ + rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname, + servname, hintp, result); + + return rc; +} + + +/* + * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix + * + * Note: the ai_family field of the original hint structure must be passed + * so that we can tell whether the addrinfo struct was built by the system's + * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions + * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's + * not safe to look at ai_family in the addrinfo itself. + */ +void +pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai) +{ +#ifdef HAVE_UNIX_SOCKETS + if (hint_ai_family == AF_UNIX) + { + /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */ + while (ai != NULL) + { + struct addrinfo *p = ai; + + ai = ai->ai_next; + free(p->ai_addr); + free(p); + } + } + else +#endif /* HAVE_UNIX_SOCKETS */ + { + /* struct was built by getaddrinfo() */ + if (ai != NULL) + freeaddrinfo(ai); + } +} + + +/* + * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets + * + * The API of this routine differs from the standard getnameinfo() definition + * in two ways: first, the addr parameter is declared as sockaddr_storage + * rather than struct sockaddr, and second, the node and service fields are + * guaranteed to be filled with something even on failure return. + */ +int +pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags) +{ + int rc; + +#ifdef HAVE_UNIX_SOCKETS + if (addr && addr->ss_family == AF_UNIX) + rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen, + node, nodelen, + service, servicelen, + flags); + else +#endif + rc = getnameinfo((const struct sockaddr *) addr, salen, + node, nodelen, + service, servicelen, + flags); + + if (rc != 0) + { + if (node) + strlcpy(node, "???", nodelen); + if (service) + strlcpy(service, "???", servicelen); + } + + return rc; +} + + +#if defined(HAVE_UNIX_SOCKETS) + +/* ------- + * getaddrinfo_unix - get unix socket info using IPv6-compatible API + * + * Bugs: only one addrinfo is set even though hintsp is NULL or + * ai_socktype is 0 + * AI_CANONNAME is not supported. + * ------- + */ +static int +getaddrinfo_unix(const char *path, const struct addrinfo * hintsp, + struct addrinfo ** result) +{ + struct addrinfo hints; + struct addrinfo *aip; + struct sockaddr_un *unp; + + *result = NULL; + + MemSet(&hints, 0, sizeof(hints)); + + if (strlen(path) >= sizeof(unp->sun_path)) + return EAI_FAIL; + + if (hintsp == NULL) + { + hints.ai_family = AF_UNIX; + hints.ai_socktype = SOCK_STREAM; + } + else + memcpy(&hints, hintsp, sizeof(hints)); + + if (hints.ai_socktype == 0) + hints.ai_socktype = SOCK_STREAM; + + if (hints.ai_family != AF_UNIX) + { + /* shouldn't have been called */ + return EAI_FAIL; + } + + aip = calloc(1, sizeof(struct addrinfo)); + if (aip == NULL) + return EAI_MEMORY; + + unp = calloc(1, sizeof(struct sockaddr_un)); + if (unp == NULL) + { + free(aip); + return EAI_MEMORY; + } + + aip->ai_family = AF_UNIX; + aip->ai_socktype = hints.ai_socktype; + aip->ai_protocol = hints.ai_protocol; + aip->ai_next = NULL; + aip->ai_canonname = NULL; + *result = aip; + + unp->sun_family = AF_UNIX; + aip->ai_addr = (struct sockaddr *) unp; + aip->ai_addrlen = sizeof(struct sockaddr_un); + + strcpy(unp->sun_path, path); + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + unp->sun_len = sizeof(struct sockaddr_un); +#endif + + return 0; +} + +/* + * Convert an address to a hostname. + */ +static int +getnameinfo_unix(const struct sockaddr_un * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags) +{ + int ret = -1; + + /* Invalid arguments. */ + if (sa == NULL || sa->sun_family != AF_UNIX || + (node == NULL && service == NULL)) + return EAI_FAIL; + + /* We don't support those. */ + if ((node && !(flags & NI_NUMERICHOST)) + || (service && !(flags & NI_NUMERICSERV))) + return EAI_FAIL; + + if (node) + { + ret = snprintf(node, nodelen, "%s", "[local]"); + if (ret == -1 || ret > nodelen) + return EAI_MEMORY; + } + + if (service) + { + ret = snprintf(service, servicelen, "%s", sa->sun_path); + if (ret == -1 || ret > servicelen) + return EAI_MEMORY; + } + + return 0; +} +#endif /* HAVE_UNIX_SOCKETS */ + + +/* + * pg_range_sockaddr - is addr within the subnet specified by netaddr/netmask ? + * + * Note: caller must already have verified that all three addresses are + * in the same address family; and AF_UNIX addresses are not supported. + */ +int +pg_range_sockaddr(const struct sockaddr_storage * addr, + const struct sockaddr_storage * netaddr, + const struct sockaddr_storage * netmask) +{ + if (addr->ss_family == AF_INET) + return range_sockaddr_AF_INET((struct sockaddr_in *) addr, + (struct sockaddr_in *) netaddr, + (struct sockaddr_in *) netmask); +#ifdef HAVE_IPV6 + else if (addr->ss_family == AF_INET6) + return range_sockaddr_AF_INET6((struct sockaddr_in6 *) addr, + (struct sockaddr_in6 *) netaddr, + (struct sockaddr_in6 *) netmask); +#endif + else + return 0; +} + +static int +range_sockaddr_AF_INET(const struct sockaddr_in * addr, + const struct sockaddr_in * netaddr, + const struct sockaddr_in * netmask) +{ + if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) & + netmask->sin_addr.s_addr) == 0) + return 1; + else + return 0; +} + + +#ifdef HAVE_IPV6 + +static int +range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr, + const struct sockaddr_in6 * netaddr, + const struct sockaddr_in6 * netmask) +{ + int i; + + for (i = 0; i < 16; i++) + { + if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) & + netmask->sin6_addr.s6_addr[i]) != 0) + return 0; + } + + return 1; +} +#endif /* HAVE_IPV6 */ + +/* + * pg_sockaddr_cidr_mask - make a network mask of the appropriate family + * and required number of significant bits + * + * numbits can be null, in which case the mask is fully set. + * + * The resulting mask is placed in *mask, which had better be big enough. + * + * Return value is 0 if okay, -1 if not. + */ +int +pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family) +{ + long bits; + char *endptr; + + if (numbits == NULL) + { + bits = (family == AF_INET) ? 32 : 128; + } + else + { + bits = strtol(numbits, &endptr, 10); + if (*numbits == '\0' || *endptr != '\0') + return -1; + } + + switch (family) + { + case AF_INET: + { + struct sockaddr_in mask4; + long maskl; + + if (bits < 0 || bits > 32) + return -1; + memset(&mask4, 0, sizeof(mask4)); + /* avoid "x << 32", which is not portable */ + if (bits > 0) + maskl = (0xffffffffUL << (32 - (int) bits)) + & 0xffffffffUL; + else + maskl = 0; + mask4.sin_addr.s_addr = htonl(maskl); + memcpy(mask, &mask4, sizeof(mask4)); + break; + } + +#ifdef HAVE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 mask6; + int i; + + if (bits < 0 || bits > 128) + return -1; + memset(&mask6, 0, sizeof(mask6)); + for (i = 0; i < 16; i++) + { + if (bits <= 0) + mask6.sin6_addr.s6_addr[i] = 0; + else if (bits >= 8) + mask6.sin6_addr.s6_addr[i] = 0xff; + else + { + mask6.sin6_addr.s6_addr[i] = + (0xff << (8 - (int) bits)) & 0xff; + } + bits -= 8; + } + memcpy(mask, &mask6, sizeof(mask6)); + break; + } +#endif + default: + return -1; + } + + mask->ss_family = family; + return 0; +} + + +#ifdef HAVE_IPV6 + +/* + * pg_promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using + * the standard convention for IPv4 addresses mapped into IPv6 world + * + * The passed addr is modified in place; be sure it is large enough to + * hold the result! Note that we only worry about setting the fields + * that pg_range_sockaddr will look at. + */ +void +pg_promote_v4_to_v6_addr(struct sockaddr_storage * addr) +{ + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + uint32 ip4addr; + + memcpy(&addr4, addr, sizeof(addr4)); + ip4addr = ntohl(addr4.sin_addr.s_addr); + + memset(&addr6, 0, sizeof(addr6)); + + addr6.sin6_family = AF_INET6; + + addr6.sin6_addr.s6_addr[10] = 0xff; + addr6.sin6_addr.s6_addr[11] = 0xff; + addr6.sin6_addr.s6_addr[12] = (ip4addr >> 24) & 0xFF; + addr6.sin6_addr.s6_addr[13] = (ip4addr >> 16) & 0xFF; + addr6.sin6_addr.s6_addr[14] = (ip4addr >> 8) & 0xFF; + addr6.sin6_addr.s6_addr[15] = (ip4addr) & 0xFF; + + memcpy(addr, &addr6, sizeof(addr6)); +} + +/* + * pg_promote_v4_to_v6_mask --- convert an AF_INET netmask to AF_INET6, using + * the standard convention for IPv4 addresses mapped into IPv6 world + * + * This must be different from pg_promote_v4_to_v6_addr because we want to + * set the high-order bits to 1's not 0's. + * + * The passed addr is modified in place; be sure it is large enough to + * hold the result! Note that we only worry about setting the fields + * that pg_range_sockaddr will look at. + */ +void +pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr) +{ + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + uint32 ip4addr; + int i; + + memcpy(&addr4, addr, sizeof(addr4)); + ip4addr = ntohl(addr4.sin_addr.s_addr); + + memset(&addr6, 0, sizeof(addr6)); + + addr6.sin6_family = AF_INET6; + + for (i = 0; i < 12; i++) + addr6.sin6_addr.s6_addr[i] = 0xff; + + addr6.sin6_addr.s6_addr[12] = (ip4addr >> 24) & 0xFF; + addr6.sin6_addr.s6_addr[13] = (ip4addr >> 16) & 0xFF; + addr6.sin6_addr.s6_addr[14] = (ip4addr >> 8) & 0xFF; + addr6.sin6_addr.s6_addr[15] = (ip4addr) & 0xFF; + + memcpy(addr, &addr6, sizeof(addr6)); +} +#endif /* HAVE_IPV6 */ + + +/* + * Run the callback function for the addr/mask, after making sure the + * mask is sane for the addr. + */ +static void +run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data, + struct sockaddr * addr, struct sockaddr * mask) +{ + struct sockaddr_storage fullmask; + + if (!addr) + return; + + /* Check that the mask is valid */ + if (mask) + { + if (mask->sa_family != addr->sa_family) + { + mask = NULL; + } + else if (mask->sa_family == AF_INET) + { + if (((struct sockaddr_in *) mask)->sin_addr.s_addr == INADDR_ANY) + mask = NULL; + } +#ifdef HAVE_IPV6 + else if (mask->sa_family == AF_INET6) + { + if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) mask)->sin6_addr)) + mask = NULL; + } +#endif + } + + /* If mask is invalid, generate our own fully-set mask */ + if (!mask) + { + pg_sockaddr_cidr_mask(&fullmask, NULL, addr->sa_family); + mask = (struct sockaddr *) & fullmask; + } + + (*callback) (addr, mask, cb_data); +} + +#ifdef WIN32 + +#include +#include + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version is for Win32. Uses the Winsock 2 functions (ie: ws2_32.dll) + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + INTERFACE_INFO *ptr, + *ii = NULL; + unsigned long length, + i; + unsigned long n_ii = 0; + SOCKET sock; + int error; + + sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0); + if (sock == SOCKET_ERROR) + return -1; + + while (n_ii < 1024) + { + n_ii += 64; + ptr = realloc(ii, sizeof(INTERFACE_INFO) * n_ii); + if (!ptr) + { + free(ii); + closesocket(sock); + errno = ENOMEM; + return -1; + } + + ii = ptr; + if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0, + ii, n_ii * sizeof(INTERFACE_INFO), + &length, 0, 0) == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEFAULT || error == WSAENOBUFS) + continue; /* need to make the buffer bigger */ + closesocket(sock); + free(ii); + return -1; + } + + break; + } + + for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i) + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & ii[i].iiAddress, + (struct sockaddr *) & ii[i].iiNetmask); + + closesocket(sock); + free(ii); + return 0; +} +#elif HAVE_GETIFADDRS /* && !WIN32 */ + +#ifdef HAVE_IFADDRS_H +#include +#endif + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses the getifaddrs() interface, which is available on + * BSDs, AIX, and modern Linux. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifaddrs *ifa, + *l; + + if (getifaddrs(&ifa) < 0) + return -1; + + for (l = ifa; l; l = l->ifa_next) + run_ifaddr_callback(callback, cb_data, + l->ifa_addr, l->ifa_netmask); + + freeifaddrs(ifa); + return 0; +} +#else /* !HAVE_GETIFADDRS && !WIN32 */ + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +/* + * SIOCGIFCONF does not return IPv6 addresses on Solaris + * and HP/UX. So we prefer SIOCGLIFCONF if it's available. + * + * On HP/UX, however, it *only* returns IPv6 addresses, + * and the structs are named slightly differently too. + * We'd have to do another call with SIOCGIFCONF to get the + * IPv4 addresses as well. We don't currently bother, just + * fall back to SIOCGIFCONF on HP/UX. + */ + +#if defined(SIOCGLIFCONF) && !defined(__hpux) + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGLIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct lifconf lifc; + struct lifreq *lifr, + lmask; + struct sockaddr *addr, + *mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + pgsocket sock, + fd; + +#ifdef HAVE_IPV6 + pgsocket sock6; +#endif + int i, + total; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&lifc, 0, sizeof(lifc)); + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_buf = buffer = ptr; + lifc.lifc_len = n_buffer; + + if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (lifc.lifc_len < n_buffer - 1024) + break; + } + +#ifdef HAVE_IPV6 + /* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */ + sock6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock6 == -1) + { + free(buffer); + close(sock); + return -1; + } +#endif + + total = lifc.lifc_len / sizeof(struct lifreq); + lifr = lifc.lifc_req; + for (i = 0; i < total; ++i) + { + addr = (struct sockaddr *) & lifr[i].lifr_addr; + memcpy(&lmask, &lifr[i], sizeof(struct lifreq)); +#ifdef HAVE_IPV6 + fd = (addr->sa_family == AF_INET6) ? sock6 : sock; +#else + fd = sock; +#endif + if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0) + mask = NULL; + else + mask = (struct sockaddr *) & lmask.lifr_addr; + run_ifaddr_callback(callback, cb_data, addr, mask); + } + + free(buffer); + close(sock); +#ifdef HAVE_IPV6 + close(sock6); +#endif + return 0; +} +#elif defined(SIOCGIFCONF) + +/* + * Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information + * here, so this is the least preferred method. Note that there is no + * standard way to iterate the struct ifreq returned in the array. + * On some OSs the structures are padded large enough for any address, + * on others you have to calculate the size of the struct ifreq. + */ + +/* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */ +#ifndef _SIZEOF_ADDR_IFREQ + +/* Calculate based on sockaddr.sa_len */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define _SIZEOF_ADDR_IFREQ(ifr) \ + ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ + (sizeof(struct ifreq) - sizeof(struct sockaddr) + \ + (ifr).ifr_addr.sa_len) : sizeof(struct ifreq)) + +/* Padded ifreq structure, simple */ +#else +#define _SIZEOF_ADDR_IFREQ(ifr) \ + sizeof (struct ifreq) +#endif +#endif /* !_SIZEOF_ADDR_IFREQ */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifconf ifc; + struct ifreq *ifr, + *end, + addr, + mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + int sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&ifc, 0, sizeof(ifc)); + ifc.ifc_buf = buffer = ptr; + ifc.ifc_len = n_buffer; + + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (ifc.ifc_len < n_buffer - 1024) + break; + } + + end = (struct ifreq *) (buffer + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < end;) + { + memcpy(&addr, ifr, sizeof(addr)); + memcpy(&mask, ifr, sizeof(mask)); + if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 && + ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0) + run_ifaddr_callback(callback, cb_data, + &addr.ifr_addr, &mask.ifr_addr); + ifr = (struct ifreq *) ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)); + } + + free(buffer); + close(sock); + return 0; +} +#else /* !defined(SIOCGIFCONF) */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version is our fallback if there's no known way to get the + * interface addresses. Just return the standard loopback addresses. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct sockaddr_in addr; + struct sockaddr_storage mask; + +#ifdef HAVE_IPV6 + struct sockaddr_in6 addr6; +#endif + + /* addr 127.0.0.1/8 */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ntohl(0x7f000001); + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "8", AF_INET); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr, + (struct sockaddr *) & mask); + +#ifdef HAVE_IPV6 + /* addr ::1/128 */ + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr.s6_addr[15] = 1; + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "128", AF_INET6); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr6, + (struct sockaddr *) & mask); +#endif + + return 0; +} +#endif /* !defined(SIOCGIFCONF) */ + +#endif /* !HAVE_GETIFADDRS */ diff --git a/src/libpq/libpq-dist.rc b/src/libpq/libpq-dist.rc new file mode 100644 index 00000000..87e1bead --- /dev/null +++ b/src/libpq/libpq-dist.rc @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,1,1,11265 + PRODUCTVERSION 9,1,1,11265 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "PostgreSQL Access Library\0" + VALUE "FileVersion", "9.1.1\0" + VALUE "InternalName", "libpq\0" + VALUE "LegalCopyright", "Copyright (C) 2011\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libpq.dll\0" + VALUE "ProductName", "PostgreSQL\0" + VALUE "ProductVersion", "9.1.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/libpq/libpq-events.c b/src/libpq/libpq-events.c new file mode 100644 index 00000000..885b39bc --- /dev/null +++ b/src/libpq/libpq-events.c @@ -0,0 +1,209 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.c + * functions for supporting the libpq "events" API + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/libpq-events.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "libpq-fe.h" +#include "libpq-int.h" + + +/* + * Registers an event proc with the given PGconn. + * + * The same proc can't be registered more than once in a PGconn. This + * restriction is required because we use the proc address to identify + * the event for purposes such as PQinstanceData(). + * + * The name argument is used within error messages to aid in debugging. + * A name must be supplied, but it needn't be unique. The string is + * copied, so the passed value needn't be long-lived. + * + * The passThrough argument is an application specific pointer and can be set + * to NULL if not required. It is passed through to the event proc whenever + * the event proc is called, and is not otherwise touched by libpq. + * + * The function returns a non-zero if successful. If the function fails, + * zero is returned. + */ +int +PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough) +{ + int i; + PGEventRegister regevt; + + if (!proc || !conn || !name || !*name) + return FALSE; /* bad arguments */ + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return FALSE; /* already registered */ + } + + if (conn->nEvents >= conn->eventArraySize) + { + PGEvent *e; + int newSize; + + newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8; + if (conn->events) + e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent)); + else + e = (PGEvent *) malloc(newSize * sizeof(PGEvent)); + + if (!e) + return FALSE; + + conn->eventArraySize = newSize; + conn->events = e; + } + + conn->events[conn->nEvents].proc = proc; + conn->events[conn->nEvents].name = strdup(name); + if (!conn->events[conn->nEvents].name) + return FALSE; + conn->events[conn->nEvents].passThrough = passThrough; + conn->events[conn->nEvents].data = NULL; + conn->events[conn->nEvents].resultInitialized = FALSE; + conn->nEvents++; + + regevt.conn = conn; + if (!proc(PGEVT_REGISTER, ®evt, passThrough)) + { + conn->nEvents--; + free(conn->events[conn->nEvents].name); + return FALSE; + } + + return TRUE; +} + +/* + * Set some "instance data" for an event within a PGconn. + * Returns nonzero on success, zero on failure. + */ +int +PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data) +{ + int i; + + if (!conn || !proc) + return FALSE; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + { + conn->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQinstanceData(const PGconn *conn, PGEventProc proc) +{ + int i; + + if (!conn || !proc) + return NULL; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return conn->events[i].data; + } + + return NULL; +} + +/* + * Set some "instance data" for an event within a PGresult. + * Returns nonzero on success, zero on failure. + */ +int +PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data) +{ + int i; + + if (!result || !proc) + return FALSE; + + for (i = 0; i < result->nEvents; i++) + { + if (result->events[i].proc == proc) + { + result->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQresultInstanceData(const PGresult *result, PGEventProc proc) +{ + int i; + + if (!result || !proc) + return NULL; + + for (i = 0; i < result->nEvents; i++) + if (result->events[i].proc == proc) + return result->events[i].data; + + return NULL; +} + +/* + * Fire RESULTCREATE events for an application-created PGresult. + * + * The conn argument can be NULL if event procedures won't use it. + */ +int +PQfireResultCreateEvents(PGconn *conn, PGresult *res) +{ + int i; + + if (!res) + return FALSE; + + for (i = 0; i < res->nEvents; i++) + { + if (!res->events[i].resultInitialized) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + return FALSE; + + res->events[i].resultInitialized = TRUE; + } + } + + return TRUE; +} diff --git a/src/libpq/libpq-events.h b/src/libpq/libpq-events.h new file mode 100644 index 00000000..05417d06 --- /dev/null +++ b/src/libpq/libpq-events.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.h + * This file contains definitions that are useful to applications + * that invoke the libpq "events" API, but are not interesting to + * ordinary users of libpq. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-events.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_EVENTS_H +#define LIBPQ_EVENTS_H + +#include "libpq-fe.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Callback Event Ids */ + typedef enum +{ + PGEVT_REGISTER, + PGEVT_CONNRESET, + PGEVT_CONNDESTROY, + PGEVT_RESULTCREATE, + PGEVT_RESULTCOPY, + PGEVT_RESULTDESTROY +} PGEventId; + +typedef struct +{ + PGconn *conn; +} PGEventRegister; + +typedef struct +{ + PGconn *conn; +} PGEventConnReset; + +typedef struct +{ + PGconn *conn; +} PGEventConnDestroy; + +typedef struct +{ + PGconn *conn; + PGresult *result; +} PGEventResultCreate; + +typedef struct +{ + const PGresult *src; + PGresult *dest; +} PGEventResultCopy; + +typedef struct +{ + PGresult *result; +} PGEventResultDestroy; + +typedef int (*PGEventProc) (PGEventId evtId, void *evtInfo, void *passThrough); + +/* Registers an event proc with the given PGconn. */ +extern int PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough); + +/* Sets the PGconn instance data for the provided proc to data. */ +extern int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data); + +/* Gets the PGconn instance data for the provided proc. */ +extern void *PQinstanceData(const PGconn *conn, PGEventProc proc); + +/* Sets the PGresult instance data for the provided proc to data. */ +extern int PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data); + +/* Gets the PGresult instance data for the provided proc. */ +extern void *PQresultInstanceData(const PGresult *result, PGEventProc proc); + +/* Fires RESULTCREATE events for an application-created PGresult. */ +extern int PQfireResultCreateEvents(PGconn *conn, PGresult *res); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_EVENTS_H */ diff --git a/src/libpq/libpq-fe.h b/src/libpq/libpq-fe.h new file mode 100644 index 00000000..d7802753 --- /dev/null +++ b/src/libpq/libpq-fe.h @@ -0,0 +1,576 @@ +/*------------------------------------------------------------------------- + * + * libpq-fe.h + * This file contains definitions for structures and + * externs for functions used by frontend postgres applications. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-fe.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_FE_H +#define LIBPQ_FE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* + * postgres_ext.h defines the backend's externally visible types, + * such as Oid. + */ +#include "postgres_ext.h" + +/* + * Option flags for PQcopyResult + */ +#define PG_COPYRES_ATTRS 0x01 +#define PG_COPYRES_TUPLES 0x02 /* Implies PG_COPYRES_ATTRS */ +#define PG_COPYRES_EVENTS 0x04 +#define PG_COPYRES_NOTICEHOOKS 0x08 + +/* Application-visible enum types */ + +typedef enum +{ + /* + * Although it is okay to add to this list, values which become unused + * should never be removed, nor should constants be redefined - that would + * break compatibility with existing code. + */ + CONNECTION_OK, + CONNECTION_BAD, + /* Non-blocking mode only below here */ + + /* + * The existence of these should never be relied upon - they should only + * be used for user feedback or similar purposes. + */ + CONNECTION_STARTED, /* Waiting for connection to be made. */ + CONNECTION_MADE, /* Connection OK; waiting to send. */ + CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the + * postmaster. */ + CONNECTION_AUTH_OK, /* Received authentication; waiting for + * backend startup. */ + CONNECTION_SETENV, /* Negotiating environment. */ + CONNECTION_SSL_STARTUP, /* Negotiating SSL. */ + CONNECTION_NEEDED /* Internal state: connect() needed */ +} ConnStatusType; + +typedef enum +{ + PGRES_POLLING_FAILED = 0, + PGRES_POLLING_READING, /* These two indicate that one may */ + PGRES_POLLING_WRITING, /* use select before polling again. */ + PGRES_POLLING_OK, + PGRES_POLLING_ACTIVE /* unused; keep for awhile for backwards + * compatibility */ +} PostgresPollingStatusType; + +typedef enum +{ + PGRES_EMPTY_QUERY = 0, /* empty query string was executed */ + PGRES_COMMAND_OK, /* a query command that doesn't return + * anything was executed properly by the + * backend */ + PGRES_TUPLES_OK, /* a query command that returns tuples was + * executed properly by the backend, PGresult + * contains the result tuples */ + PGRES_COPY_OUT, /* Copy Out data transfer in progress */ + PGRES_COPY_IN, /* Copy In data transfer in progress */ + PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the + * backend */ + PGRES_NONFATAL_ERROR, /* notice or warning message */ + PGRES_FATAL_ERROR, /* query failed */ + PGRES_COPY_BOTH /* Copy In/Out data transfer in progress */ +} ExecStatusType; + +typedef enum +{ + PQTRANS_IDLE, /* connection idle */ + PQTRANS_ACTIVE, /* command in progress */ + PQTRANS_INTRANS, /* idle, within transaction block */ + PQTRANS_INERROR, /* idle, within failed transaction */ + PQTRANS_UNKNOWN /* cannot determine status */ +} PGTransactionStatusType; + +typedef enum +{ + PQERRORS_TERSE, /* single-line error messages */ + PQERRORS_DEFAULT, /* recommended style */ + PQERRORS_VERBOSE /* all the facts, ma'am */ +} PGVerbosity; + +typedef enum +{ + PQPING_OK, /* server is accepting connections */ + PQPING_REJECT, /* server is alive but rejecting connections */ + PQPING_NO_RESPONSE, /* could not establish connection */ + PQPING_NO_ATTEMPT /* connection not attempted (bad params) */ +} PGPing; + +/* PGconn encapsulates a connection to the backend. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_conn PGconn; + +/* PGresult encapsulates the result of a query (or more precisely, of a single + * SQL command --- a query string given to PQsendQuery can contain multiple + * commands and thus return multiple PGresult objects). + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_result PGresult; + +/* PGcancel encapsulates the information needed to cancel a running + * query on an existing connection. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_cancel PGcancel; + +/* PGnotify represents the occurrence of a NOTIFY message. + * Ideally this would be an opaque typedef, but it's so simple that it's + * unlikely to change. + * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's, + * whereas in earlier versions it was always your own backend's PID. + */ +typedef struct pgNotify +{ + char *relname; /* notification condition name */ + int be_pid; /* process ID of notifying server process */ + char *extra; /* notification parameter */ + /* Fields below here are private to libpq; apps should not use 'em */ + struct pgNotify *next; /* list link */ +} PGnotify; + +/* Function types for notice-handling callbacks */ +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); +typedef void (*PQnoticeProcessor) (void *arg, const char *message); + +/* Print options for PQprint() */ +typedef char pqbool; + +typedef struct _PQprintOpt +{ + pqbool header; /* print output field headings and row count */ + pqbool align; /* fill align the fields */ + pqbool standard; /* old brain dead format */ + pqbool html3; /* output html tables */ + pqbool expanded; /* expand tables */ + pqbool pager; /* use pager for output if needed */ + char *fieldSep; /* field separator */ + char *tableOpt; /* insert to HTML */ + char *caption; /* HTML
*/ + char **fieldName; /* null terminated array of replacement field + * names */ +} PQprintOpt; + +/* ---------------- + * Structure for the conninfo parameter definitions returned by PQconndefaults + * or PQconninfoParse. + * + * All fields except "val" point at static strings which must not be altered. + * "val" is either NULL or a malloc'd current-value string. PQconninfoFree() + * will release both the val strings and the PQconninfoOption array itself. + * ---------------- + */ +typedef struct _PQconninfoOption +{ + char *keyword; /* The keyword of the option */ + char *envvar; /* Fallback environment variable name */ + char *compiled; /* Fallback compiled in default value */ + char *val; /* Option's current value, or NULL */ + char *label; /* Label for field in connect dialog */ + char *dispchar; /* Indicates how to display this field in a + * connect dialog. Values are: "" Display + * entered value as is "*" Password field - + * hide value "D" Debug option - don't show + * by default */ + int dispsize; /* Field size in characters for dialog */ +} PQconninfoOption; + +/* ---------------- + * PQArgBlock -- structure for PQfn() arguments + * ---------------- + */ +typedef struct +{ + int len; + int isint; + union + { + int *ptr; /* can't use void (dec compiler barfs) */ + int integer; + } u; +} PQArgBlock; + +/* ---------------- + * PGresAttDesc -- Data about a single attribute (column) of a query result + * ---------------- + */ +typedef struct pgresAttDesc +{ + char *name; /* column name */ + Oid tableid; /* source table, if known */ + int columnid; /* source column, if known */ + int format; /* format code for value (text/binary) */ + Oid typid; /* type id */ + int typlen; /* type size */ + int atttypmod; /* type-specific modifier info */ +} PGresAttDesc; + +/* ---------------- + * Exported functions of libpq + * ---------------- + */ + +/* === in fe-connect.c === */ + +/* make a new client connection to the backend */ +/* Asynchronous (non-blocking) */ +extern PGconn *PQconnectStart(const char *conninfo); +extern PGconn *PQconnectStartParams(const char **keywords, + const char **values, int expand_dbname); +extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern PGconn *PQconnectdb(const char *conninfo); +extern PGconn *PQconnectdbParams(const char **keywords, + const char **values, int expand_dbname); +extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, + const char *pgoptions, const char *pgtty, + const char *dbName, + const char *login, const char *pwd); + +#define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \ + PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL) + +/* close the current connection and free the PGconn data structure */ +extern void PQfinish(PGconn *conn); + +/* get info about connection options known to PQconnectdb */ +extern PQconninfoOption *PQconndefaults(void); + +/* parse connection options in same way as PQconnectdb */ +extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); + +/* free the data structure returned by PQconndefaults() or PQconninfoParse() */ +extern void PQconninfoFree(PQconninfoOption *connOptions); + +/* + * close the current connection and restablish a new one with the same + * parameters + */ +/* Asynchronous (non-blocking) */ +extern int PQresetStart(PGconn *conn); +extern PostgresPollingStatusType PQresetPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern void PQreset(PGconn *conn); + +/* request a cancel structure */ +extern PGcancel *PQgetCancel(PGconn *conn); + +/* free a cancel structure */ +extern void PQfreeCancel(PGcancel *cancel); + +/* issue a cancel request */ +extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize); + +/* backwards compatible version of PQcancel; not thread-safe */ +extern int PQrequestCancel(PGconn *conn); + +/* Accessor functions for PGconn objects */ +extern char *PQdb(const PGconn *conn); +extern char *PQuser(const PGconn *conn); +extern char *PQpass(const PGconn *conn); +extern char *PQhost(const PGconn *conn); +extern char *PQport(const PGconn *conn); +extern char *PQtty(const PGconn *conn); +extern char *PQoptions(const PGconn *conn); +extern ConnStatusType PQstatus(const PGconn *conn); +extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn); +extern const char *PQparameterStatus(const PGconn *conn, + const char *paramName); +extern int PQprotocolVersion(const PGconn *conn); +extern int PQserverVersion(const PGconn *conn); +extern char *PQerrorMessage(const PGconn *conn); +extern int PQsocket(const PGconn *conn); +extern int PQbackendPID(const PGconn *conn); +extern int PQconnectionNeedsPassword(const PGconn *conn); +extern int PQconnectionUsedPassword(const PGconn *conn); +extern int PQclientEncoding(const PGconn *conn); +extern int PQsetClientEncoding(PGconn *conn, const char *encoding); + +/* Get the OpenSSL structure associated with a connection. Returns NULL for + * unencrypted connections or if any other TLS library is in use. */ +extern void *PQgetssl(PGconn *conn); + +/* Tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitSSL(int do_init); + +/* More detailed way to tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitOpenSSL(int do_ssl, int do_crypto); + +/* Set verbosity for PQerrorMessage and PQresultErrorMessage */ +extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); + +/* Enable/disable tracing */ +extern void PQtrace(PGconn *conn, FILE *debug_port); +extern void PQuntrace(PGconn *conn); + +/* Override default notice handling routines */ +extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); +extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, + PQnoticeProcessor proc, + void *arg); + +/* + * Used to set callback that prevents concurrent access to + * non-thread safe functions that libpq needs. + * The default implementation uses a libpq internal mutex. + * Only required for multithreaded apps that use kerberos + * both within their app and for postgresql connections. + */ +typedef void (*pgthreadlock_t) (int acquire); + +extern pgthreadlock_t PQregisterThreadLock(pgthreadlock_t newhandler); + +/* === in fe-exec.c === */ + +/* Simple synchronous query */ +extern PGresult *PQexec(PGconn *conn, const char *query); +extern PGresult *PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern PGresult *PQprepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern PGresult *PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + +/* Interface for multiple-result or asynchronous queries */ +extern int PQsendQuery(PGconn *conn, const char *query); +extern int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsendPrepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern int PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern PGresult *PQgetResult(PGconn *conn); + +/* Routines for managing an asynchronous query */ +extern int PQisBusy(PGconn *conn); +extern int PQconsumeInput(PGconn *conn); + +/* LISTEN/NOTIFY support */ +extern PGnotify *PQnotifies(PGconn *conn); + +/* Routines for copy in/out */ +extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes); +extern int PQputCopyEnd(PGconn *conn, const char *errormsg); +extern int PQgetCopyData(PGconn *conn, char **buffer, int async); + +/* Deprecated routines for copy in/out */ +extern int PQgetline(PGconn *conn, char *string, int length); +extern int PQputline(PGconn *conn, const char *string); +extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); +extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); +extern int PQendcopy(PGconn *conn); + +/* Set blocking/nonblocking connection to the backend */ +extern int PQsetnonblocking(PGconn *conn, int arg); +extern int PQisnonblocking(const PGconn *conn); +extern int PQisthreadsafe(void); +extern PGPing PQping(const char *conninfo); +extern PGPing PQpingParams(const char **keywords, + const char **values, int expand_dbname); + +/* Force the write buffer to be written (or at least try) */ +extern int PQflush(PGconn *conn); + +/* + * "Fast path" interface --- not really recommended for application + * use + */ +extern PGresult *PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs); + +/* Accessor functions for PGresult objects */ +extern ExecStatusType PQresultStatus(const PGresult *res); +extern char *PQresStatus(ExecStatusType status); +extern char *PQresultErrorMessage(const PGresult *res); +extern char *PQresultErrorField(const PGresult *res, int fieldcode); +extern int PQntuples(const PGresult *res); +extern int PQnfields(const PGresult *res); +extern int PQbinaryTuples(const PGresult *res); +extern char *PQfname(const PGresult *res, int field_num); +extern int PQfnumber(const PGresult *res, const char *field_name); +extern Oid PQftable(const PGresult *res, int field_num); +extern int PQftablecol(const PGresult *res, int field_num); +extern int PQfformat(const PGresult *res, int field_num); +extern Oid PQftype(const PGresult *res, int field_num); +extern int PQfsize(const PGresult *res, int field_num); +extern int PQfmod(const PGresult *res, int field_num); +extern char *PQcmdStatus(PGresult *res); +extern char *PQoidStatus(const PGresult *res); /* old and ugly */ +extern Oid PQoidValue(const PGresult *res); /* new and improved */ +extern char *PQcmdTuples(PGresult *res); +extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); +extern int PQgetlength(const PGresult *res, int tup_num, int field_num); +extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); +extern int PQnparams(const PGresult *res); +extern Oid PQparamtype(const PGresult *res, int param_num); + +/* Describe prepared statements and portals */ +extern PGresult *PQdescribePrepared(PGconn *conn, const char *stmt); +extern PGresult *PQdescribePortal(PGconn *conn, const char *portal); +extern int PQsendDescribePrepared(PGconn *conn, const char *stmt); +extern int PQsendDescribePortal(PGconn *conn, const char *portal); + +/* Delete a PGresult */ +extern void PQclear(PGresult *res); + +/* For freeing other alloc'd results, such as PGnotify structs */ +extern void PQfreemem(void *ptr); + +/* Exists for backward compatibility. bjm 2003-03-24 */ +#define PQfreeNotify(ptr) PQfreemem(ptr) + +/* Error when no password was given. */ +/* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */ +#define PQnoPasswordSupplied "fe_sendauth: no password supplied\n" + +/* Create and manipulate PGresults */ +extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); +extern PGresult *PQcopyResult(const PGresult *src, int flags); +extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs); +extern void *PQresultAlloc(PGresult *res, size_t nBytes); +extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len); + +/* Quoting strings before inclusion in queries. */ +extern size_t PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error); +extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len); +extern char *PQescapeIdentifier(PGconn *conn, const char *str, size_t len); +extern unsigned char *PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length); +extern unsigned char *PQunescapeBytea(const unsigned char *strtext, + size_t *retbuflen); + +/* These forms are deprecated! */ +extern size_t PQescapeString(char *to, const char *from, size_t length); +extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, + size_t *to_length); + + + +/* === in fe-print.c === */ + +extern void +PQprint(FILE *fout, /* output stream */ + const PGresult *res, + const PQprintOpt *ps); /* option structure */ + +/* + * really old printing routines + */ +extern 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); + +extern void +PQprintTuples(const PGresult *res, + FILE *fout, /* output stream */ + int printAttName, /* print attribute names */ + int terseOutput, /* delimiter bars */ + int width); /* width of column, if 0, use variable width */ + + +/* === in fe-lobj.c === */ + +/* Large-object access routines */ +extern int lo_open(PGconn *conn, Oid lobjId, int mode); +extern int lo_close(PGconn *conn, int fd); +extern int lo_read(PGconn *conn, int fd, char *buf, size_t len); +extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len); +extern int lo_lseek(PGconn *conn, int fd, int offset, int whence); +extern Oid lo_creat(PGconn *conn, int mode); +extern Oid lo_create(PGconn *conn, Oid lobjId); +extern int lo_tell(PGconn *conn, int fd); +extern int lo_truncate(PGconn *conn, int fd, size_t len); +extern int lo_unlink(PGconn *conn, Oid lobjId); +extern Oid lo_import(PGconn *conn, const char *filename); +extern Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId); +extern int lo_export(PGconn *conn, Oid lobjId, const char *filename); + +/* === in fe-misc.c === */ + +/* Get the version of the libpq library in use */ +extern int PQlibVersion(void); + +/* Determine length of multibyte encoded char at *s */ +extern int PQmblen(const char *s, int encoding); + +/* Determine display length of multibyte encoded char at *s */ +extern int PQdsplen(const char *s, int encoding); + +/* Get encoding id from environment variable PGCLIENTENCODING */ +extern int PQenv2encoding(void); + +/* === in fe-auth.c === */ + +extern char *PQencryptPassword(const char *passwd, const char *user); + +/* === in encnames.c === */ + +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_FE_H */ diff --git a/src/libpq/libpq-int.h b/src/libpq/libpq-int.h new file mode 100644 index 00000000..d56ef5d4 --- /dev/null +++ b/src/libpq/libpq-int.h @@ -0,0 +1,618 @@ +/*------------------------------------------------------------------------- + * + * libpq-int.h + * This file contains internal definitions meant to be used only by + * the frontend libpq library, not by applications that call it. + * + * An application can include this file if it wants to bypass the + * official API defined by libpq-fe.h, but code that does so is much + * more likely to break across PostgreSQL releases than code that uses + * only the official API. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-int.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_INT_H +#define LIBPQ_INT_H + +/* We assume libpq-fe.h has already been included. */ +#include "postgres_fe.h" +#include "libpq-events.h" + +#include +#include +#ifndef WIN32 +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#include +#endif + +/* include stuff common to fe and be */ +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" +/* include stuff found in fe only */ +#include "pqexpbuffer.h" + +#ifdef ENABLE_GSS +#if defined(HAVE_GSSAPI_H) +#include +#else +#include +#endif +#endif + +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +#include +#endif +#include +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct +{ + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + +#ifdef USE_SSL +#include +#include + +#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE) +#define USE_SSL_ENGINE +#endif +#endif /* USE_SSL */ + +/* + * POSTGRES backend dependent Constants. + */ +#define CMDSTATUS_LEN 64 /* should match COMPLETION_TAG_BUFSIZE */ + +/* + * PGresult and the subsidiary types PGresAttDesc, PGresAttValue + * represent the result of a query (or more precisely, of a single SQL + * command --- a query string given to PQexec can contain multiple commands). + * Note we assume that a single command can return at most one tuple group, + * hence there is no need for multiple descriptor sets. + */ + +/* Subsidiary-storage management structure for PGresult. + * See space management routines in fe-exec.c for details. + * Note that space[k] refers to the k'th byte starting from the physical + * head of the block --- it's a union, not a struct! + */ +typedef union pgresult_data PGresult_data; + +union pgresult_data +{ + PGresult_data *next; /* link to next block, or NULL */ + char space[1]; /* dummy for accessing block as bytes */ +}; + +/* Data about a single parameter of a prepared statement */ +typedef struct pgresParamDesc +{ + Oid typid; /* type id */ +} PGresParamDesc; + +/* + * Data for a single attribute of a single tuple + * + * We use char* for Attribute values. + * + * The value pointer always points to a null-terminated area; we add a + * null (zero) byte after whatever the backend sends us. This is only + * particularly useful for text values ... with a binary value, the + * value might have embedded nulls, so the application can't use C string + * operators on it. But we add a null anyway for consistency. + * Note that the value itself does not contain a length word. + * + * A NULL attribute is a special case in two ways: its len field is NULL_LEN + * and its value field points to null_field in the owning PGresult. All the + * NULL attributes in a query result point to the same place (there's no need + * to store a null string separately for each one). + */ + +#define NULL_LEN (-1) /* pg_result len for NULL value */ + +typedef struct pgresAttValue +{ + int len; /* length in bytes of the value */ + char *value; /* actual value, plus terminating zero byte */ +} PGresAttValue; + +/* Typedef for message-field list entries */ +typedef struct pgMessageField +{ + struct pgMessageField *next; /* list link */ + char code; /* field code */ + char contents[1]; /* field value (VARIABLE LENGTH) */ +} PGMessageField; + +/* Fields needed for notice handling */ +typedef struct +{ + PQnoticeReceiver noticeRec; /* notice message receiver */ + void *noticeRecArg; + PQnoticeProcessor noticeProc; /* notice message processor */ + void *noticeProcArg; +} PGNoticeHooks; + +typedef struct PGEvent +{ + PGEventProc proc; /* the function to call on events */ + char *name; /* used only for error messages */ + void *passThrough; /* pointer supplied at registration time */ + void *data; /* optional state (instance) data */ + bool resultInitialized; /* T if RESULTCREATE/COPY succeeded */ +} PGEvent; + +struct pg_result +{ + int ntups; + int numAttributes; + PGresAttDesc *attDescs; + PGresAttValue **tuples; /* each PGresTuple is an array of + * PGresAttValue's */ + int tupArrSize; /* allocated size of tuples array */ + int numParameters; + PGresParamDesc *paramDescs; + ExecStatusType resultStatus; + char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the query */ + int binary; /* binary tuple values if binary == 1, + * otherwise text */ + + /* + * These fields are copied from the originating PGconn, so that operations + * on the PGresult don't have to reference the PGconn. + */ + PGNoticeHooks noticeHooks; + PGEvent *events; + int nEvents; + int client_encoding; /* encoding id */ + + /* + * Error information (all NULL if not an error result). errMsg is the + * "overall" error message returned by PQresultErrorMessage. If we have + * per-field info then it is stored in a linked list. + */ + char *errMsg; /* error message, or NULL if no error */ + PGMessageField *errFields; /* message broken into fields */ + + /* All NULL attributes in the query result point to this null string */ + char null_field[1]; + + /* + * Space management information. Note that attDescs and error stuff, if + * not null, point into allocated blocks. But tuples points to a + * separately malloc'd block, so that we can realloc it. + */ + PGresult_data *curBlock; /* most recently allocated block */ + int curOffset; /* start offset of free space in block */ + int spaceLeft; /* number of free bytes remaining in block */ +}; + +/* PGAsyncStatusType defines the state of the query-execution state machine */ +typedef enum +{ + PGASYNC_IDLE, /* nothing's happening, dude */ + PGASYNC_BUSY, /* query in progress */ + PGASYNC_READY, /* result ready for PQgetResult */ + PGASYNC_COPY_IN, /* Copy In data transfer in progress */ + PGASYNC_COPY_OUT, /* Copy Out data transfer in progress */ + PGASYNC_COPY_BOTH /* Copy In/Out data transfer in progress */ +} PGAsyncStatusType; + +/* PGQueryClass tracks which query protocol we are now executing */ +typedef enum +{ + PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */ + PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */ + PGQUERY_PREPARE, /* Parse only (PQprepare) */ + PGQUERY_DESCRIBE /* Describe Statement or Portal */ +} PGQueryClass; + +/* PGSetenvStatusType defines the state of the PQSetenv state machine */ +/* (this is used only for 2.0-protocol connections) */ +typedef enum +{ + SETENV_STATE_CLIENT_ENCODING_SEND, /* About to send an Environment Option */ + SETENV_STATE_CLIENT_ENCODING_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */ + SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_QUERY1_SEND, /* About to send a status query */ + SETENV_STATE_QUERY1_WAIT, /* Waiting for query to complete */ + SETENV_STATE_QUERY2_SEND, /* About to send a status query */ + SETENV_STATE_QUERY2_WAIT, /* Waiting for query to complete */ + SETENV_STATE_IDLE +} PGSetenvStatusType; + +/* Typedef for the EnvironmentOptions[] array */ +typedef struct PQEnvironmentOption +{ + const char *envName, /* name of an environment variable */ + *pgName; /* name of corresponding SET variable */ +} PQEnvironmentOption; + +/* Typedef for parameter-status list entries */ +typedef struct pgParameterStatus +{ + struct pgParameterStatus *next; /* list link */ + char *name; /* parameter name */ + char *value; /* parameter value */ + /* Note: name and value are stored in same malloc block as struct is */ +} pgParameterStatus; + +/* large-object-access data ... allocated only if large-object code is used. */ +typedef struct pgLobjfuncs +{ + Oid fn_lo_open; /* OID of backend function lo_open */ + Oid fn_lo_close; /* OID of backend function lo_close */ + Oid fn_lo_creat; /* OID of backend function lo_creat */ + Oid fn_lo_create; /* OID of backend function lo_create */ + Oid fn_lo_unlink; /* OID of backend function lo_unlink */ + Oid fn_lo_lseek; /* OID of backend function lo_lseek */ + Oid fn_lo_tell; /* OID of backend function lo_tell */ + Oid fn_lo_truncate; /* OID of backend function lo_truncate */ + Oid fn_lo_read; /* OID of backend function LOread */ + Oid fn_lo_write; /* OID of backend function LOwrite */ +} PGlobjfuncs; + +/* + * PGconn stores all the state data associated with a single connection + * to a backend. + */ +struct pg_conn +{ + /* Saved values of connection options */ + char *pghost; /* the machine on which the server is running */ + char *pghostaddr; /* the numeric IP address of the machine on + * which the server is running. Takes + * precedence over above. */ + char *pgport; /* the server's communication port */ + char *pgunixsocket; /* the Unix-domain socket that the server is + * listening on; if NULL, uses a default + * constructed from pgport */ + char *pgtty; /* tty on which the backend messages is + * displayed (OBSOLETE, NOT USED) */ + char *connect_timeout; /* connection timeout (numeric string) */ + char *client_encoding_initial; /* encoding to use */ + char *pgoptions; /* options to start the backend with */ + char *appname; /* application name */ + char *fbappname; /* fallback application name */ + char *dbName; /* database name */ + char *replication; /* connect as the replication standby? */ + char *pguser; /* Postgres username and password, if any */ + char *pgpass; + char *keepalives; /* use TCP keepalives? */ + char *keepalives_idle; /* time between TCP keepalives */ + char *keepalives_interval; /* time between TCP keepalive + * retransmits */ + char *keepalives_count; /* maximum number of TCP keepalive + * retransmits */ + char *sslmode; /* SSL mode (require,prefer,allow,disable) */ + char *sslkey; /* client key filename */ + char *sslcert; /* client certificate filename */ + char *sslrootcert; /* root certificate filename */ + char *sslcrl; /* certificate revocation list filename */ + char *requirepeer; /* required peer credentials for local sockets */ + +#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI) + char *krbsrvname; /* Kerberos service name */ +#endif + + /* Optional file to write trace info to */ + FILE *Pfdebug; + + /* Callback procedures for notice message processing */ + PGNoticeHooks noticeHooks; + + /* Event procs registered via PQregisterEventProc */ + PGEvent *events; /* expandable array of event data */ + int nEvents; /* number of active events */ + int eventArraySize; /* allocated array size */ + + /* Status indicators */ + ConnStatusType status; + PGAsyncStatusType asyncStatus; + PGTransactionStatusType xactStatus; /* never changes to ACTIVE */ + PGQueryClass queryclass; + char *last_query; /* last SQL command, or NULL if unknown */ + char last_sqlstate[6]; /* last reported SQLSTATE */ + bool options_valid; /* true if OK to attempt connection */ + bool nonblocking; /* whether this connection is using nonblock + * sending semantics */ + char copy_is_binary; /* 1 = copy binary, 0 = copy text */ + int copy_already_done; /* # bytes already returned in COPY + * OUT */ + PGnotify *notifyHead; /* oldest unreported Notify msg */ + PGnotify *notifyTail; /* newest unreported Notify msg */ + + /* Connection data */ + int sock; /* Unix FD for socket, -1 if not connected */ + SockAddr laddr; /* Local address */ + SockAddr raddr; /* Remote address */ + ProtocolVersion pversion; /* FE/BE protocol version in use */ + int sversion; /* server version, e.g. 70401 for 7.4.1 */ + bool auth_req_received; /* true if any type of auth req + * received */ + bool password_needed; /* true if server demanded a password */ + bool dot_pgpass_used; /* true if used .pgpass */ + bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ + bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ + + /* Transient state needed while establishing connection */ + struct addrinfo *addrlist; /* list of possible backend addresses */ + struct addrinfo *addr_cur; /* the one currently being tried */ + int addrlist_family; /* needed to know how to free addrlist */ + PGSetenvStatusType setenv_state; /* for 2.0 protocol only */ + const PQEnvironmentOption *next_eo; + bool send_appname; /* okay to send application_name? */ + + /* Miscellaneous stuff */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ + char md5Salt[4]; /* password salt received from backend */ + pgParameterStatus *pstatus; /* ParameterStatus data */ + int client_encoding; /* encoding id */ + bool std_strings; /* standard_conforming_strings */ + PGVerbosity verbosity; /* error/notice message verbosity */ + PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */ + + /* Buffer for data received from backend and not yet processed */ + char *inBuffer; /* currently allocated buffer */ + int inBufSize; /* allocated size of buffer */ + int inStart; /* offset to first unconsumed data in buffer */ + int inCursor; /* next byte to tentatively consume */ + int inEnd; /* offset to first position after avail data */ + + /* Buffer for data not yet sent to backend */ + char *outBuffer; /* currently allocated buffer */ + int outBufSize; /* allocated size of buffer */ + int outCount; /* number of chars waiting in buffer */ + + /* State for constructing messages in outBuffer */ + int outMsgStart; /* offset to msg start (length word); if -1, + * msg has no length word */ + int outMsgEnd; /* offset to msg end (so far) */ + + /* Status for asynchronous result construction */ + PGresult *result; /* result being constructed */ + PGresAttValue *curTuple; /* tuple currently being read */ + +#ifdef USE_SSL + bool allow_ssl_try; /* Allowed to try SSL negotiation */ + bool wait_ssl_try; /* Delay SSL negotiation until after + * attempting normal connection */ + SSL *ssl; /* SSL status, if have SSL connection */ + X509 *peer; /* X509 cert of server */ + char peer_dn[256 + 1]; /* peer distinguished name */ + char peer_cn[SM_USER + 1]; /* peer common name */ +#ifdef USE_SSL_ENGINE + ENGINE *engine; /* SSL engine, if any */ +#else + void *engine; /* dummy field to keep struct the same if + * OpenSSL version changes */ +#endif +#endif /* USE_SSL */ + +#ifdef ENABLE_GSS + gss_ctx_id_t gctx; /* GSS context */ + gss_name_t gtarg_nam; /* GSS target name */ + gss_buffer_desc ginbuf; /* GSS input token */ + gss_buffer_desc goutbuf; /* GSS output token */ +#endif + +#ifdef ENABLE_SSPI +#ifndef ENABLE_GSS + gss_buffer_desc ginbuf; /* GSS input token */ +#else + char *gsslib; /* What GSS librart to use ("gssapi" or + * "sspi") */ +#endif + CredHandle *sspicred; /* SSPI credentials handle */ + CtxtHandle *sspictx; /* SSPI context */ + char *sspitarget; /* SSPI target name */ + int usesspi; /* Indicate if SSPI is in use on the + * connection */ +#endif + + + /* Buffer for current error message */ + PQExpBufferData errorMessage; /* expansible string */ + + /* Buffer for receiving various parts of messages */ + PQExpBufferData workBuffer; /* expansible string */ +}; + +/* PGcancel stores all data necessary to cancel a connection. A copy of this + * data is required to safely cancel a connection running on a different + * thread. + */ +struct pg_cancel +{ + SockAddr raddr; /* Remote address */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ +}; + + +/* String descriptions of the ExecStatusTypes. + * direct use of this array is deprecated; call PQresStatus() instead. + */ +extern char *const pgresStatus[]; + +/* ---------------- + * Internal functions of libpq + * Functions declared here need to be visible across files of libpq, + * but are not intended to be called by applications. We use the + * convention "pqXXX" for internal functions, vs. the "PQxxx" names + * used for application-visible routines. + * ---------------- + */ + +/* === in fe-connect.c === */ + +extern int pqPacketSend(PGconn *conn, char pack_type, + const void *buf, size_t buf_len); +extern bool pqGetHomeDirectory(char *buf, int bufsize); + +#ifdef ENABLE_THREAD_SAFETY +extern pgthreadlock_t pg_g_threadlock; + +#define PGTHREAD_ERROR(msg) \ + do { \ + fprintf(stderr, "%s\n", msg); \ + exit(1); \ + } while (0) + + +#define pglock_thread() pg_g_threadlock(true) +#define pgunlock_thread() pg_g_threadlock(false) +#else +#define pglock_thread() ((void) 0) +#define pgunlock_thread() ((void) 0) +#endif + +/* === in fe-exec.c === */ + +extern void pqSetResultError(PGresult *res, const char *msg); +extern void pqCatenateResultError(PGresult *res, const char *msg); +extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); +extern char *pqResultStrdup(PGresult *res, const char *str); +extern void pqClearAsyncResult(PGconn *conn); +extern void pqSaveErrorResult(PGconn *conn); +extern PGresult *pqPrepareAsyncResult(PGconn *conn); +extern void +pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) +/* This lets gcc check the format string for consistency. */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); +extern int pqAddTuple(PGresult *res, PGresAttValue *tup); +extern void pqSaveMessageField(PGresult *res, char code, + const char *value); +extern void pqSaveParameterStatus(PGconn *conn, const char *name, + const char *value); +extern void pqHandleSendFailure(PGconn *conn); + +/* === in fe-protocol2.c === */ + +extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn); + +extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput2(PGconn *conn); +extern int pqGetCopyData2(PGconn *conn, char **buffer, int async); +extern int pqGetline2(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy2(PGconn *conn); +extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-protocol3.c === */ + +extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput3(PGconn *conn); +extern int pqGetErrorNotice3(PGconn *conn, bool isError); +extern int pqGetCopyData3(PGconn *conn, char **buffer, int async); +extern int pqGetline3(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy3(PGconn *conn); +extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-misc.c === */ + + /* + * "Get" and "Put" routines return 0 if successful, EOF if not. Note that for + * Get, EOF merely means the buffer is exhausted, not that there is + * necessarily any error. + */ +extern int pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqGetc(char *result, PGconn *conn); +extern int pqPutc(char c, PGconn *conn); +extern int pqGets(PQExpBuffer buf, PGconn *conn); +extern int pqGets_append(PQExpBuffer buf, PGconn *conn); +extern int pqPuts(const char *s, PGconn *conn); +extern int pqGetnchar(char *s, size_t len, PGconn *conn); +extern int pqPutnchar(const char *s, size_t len, PGconn *conn); +extern int pqGetInt(int *result, size_t bytes, PGconn *conn); +extern int pqPutInt(int value, size_t bytes, PGconn *conn); +extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn); +extern int pqPutMsgEnd(PGconn *conn); +extern int pqReadData(PGconn *conn); +extern int pqFlush(PGconn *conn); +extern int pqWait(int forRead, int forWrite, PGconn *conn); +extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, + time_t finish_time); +extern int pqReadReady(PGconn *conn); +extern int pqWriteReady(PGconn *conn); + +/* === in fe-secure.c === */ + +extern int pqsecure_initialize(PGconn *); +extern void pqsecure_destroy(void); +extern PostgresPollingStatusType pqsecure_open_client(PGconn *); +extern void pqsecure_close(PGconn *); +extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); +extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len); + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) +extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending); +extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, + bool got_epipe); +#endif + +/* + * this is so that we can check if a connection is non-blocking internally + * without the overhead of a function call + */ +#define pqIsnonblocking(conn) ((conn)->nonblocking) + +#ifdef ENABLE_NLS +extern char * +libpq_gettext(const char *msgid) +__attribute__((format_arg(1))); +#else +#define libpq_gettext(x) (x) +#endif + +/* + * These macros are needed to let error-handling code be portable between + * Unix and Windows. (ugh) + */ +#ifdef WIN32 +#define SOCK_ERRNO (WSAGetLastError()) +#define SOCK_STRERROR winsock_strerror +#define SOCK_ERRNO_SET(e) WSASetLastError(e) +#else +#define SOCK_ERRNO errno +#define SOCK_STRERROR pqStrerror +#define SOCK_ERRNO_SET(e) (errno = (e)) +#endif + +#endif /* LIBPQ_INT_H */ diff --git a/src/libpq/libpq.rc b/src/libpq/libpq.rc new file mode 100644 index 00000000..b8597c4e --- /dev/null +++ b/src/libpq/libpq.rc @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,1,1,11316 + PRODUCTVERSION 9,1,1,11316 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "PostgreSQL Access Library\0" + VALUE "FileVersion", "9.1.1\0" + VALUE "InternalName", "libpq\0" + VALUE "LegalCopyright", "Copyright (C) 2011\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libpq.dll\0" + VALUE "ProductName", "PostgreSQL\0" + VALUE "ProductVersion", "9.1.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/libpq/libpq.rc.in b/src/libpq/libpq.rc.in new file mode 100644 index 00000000..4a8fa498 --- /dev/null +++ b/src/libpq/libpq.rc.in @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,1,1,0 + PRODUCTVERSION 9,1,1,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "PostgreSQL Access Library\0" + VALUE "FileVersion", "9.1.1\0" + VALUE "InternalName", "libpq\0" + VALUE "LegalCopyright", "Copyright (C) 2011\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libpq.dll\0" + VALUE "ProductName", "PostgreSQL\0" + VALUE "ProductVersion", "9.1.1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/libpq/libpq/auth.h b/src/libpq/libpq/auth.h new file mode 100644 index 00000000..884daf59 --- /dev/null +++ b/src/libpq/libpq/auth.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * auth.h + * Definitions for network authentication routines + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/auth.h + * + *------------------------------------------------------------------------- + */ +#ifndef AUTH_H +#define AUTH_H + +#include "libpq/libpq-be.h" + +extern char *pg_krb_server_keyfile; +extern char *pg_krb_srvnam; +extern bool pg_krb_caseins_users; +extern char *pg_krb_server_hostname; +extern char *pg_krb_realm; + +extern void ClientAuthentication(Port *port); + +/* Hook for plugins to get control in ClientAuthentication() */ +typedef void (*ClientAuthentication_hook_type) (Port *, int); +extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook; + +#endif /* AUTH_H */ diff --git a/src/libpq/libpq/be-fsstubs.h b/src/libpq/libpq/be-fsstubs.h new file mode 100644 index 00000000..416da754 --- /dev/null +++ b/src/libpq/libpq/be-fsstubs.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * be-fsstubs.h + * + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/be-fsstubs.h + * + *------------------------------------------------------------------------- + */ +#ifndef BE_FSSTUBS_H +#define BE_FSSTUBS_H + +#include "fmgr.h" + +/* + * LO functions available via pg_proc entries + */ +extern Datum lo_import(PG_FUNCTION_ARGS); +extern Datum lo_import_with_oid(PG_FUNCTION_ARGS); +extern Datum lo_export(PG_FUNCTION_ARGS); + +extern Datum lo_creat(PG_FUNCTION_ARGS); +extern Datum lo_create(PG_FUNCTION_ARGS); + +extern Datum lo_open(PG_FUNCTION_ARGS); +extern Datum lo_close(PG_FUNCTION_ARGS); + +extern Datum loread(PG_FUNCTION_ARGS); +extern Datum lowrite(PG_FUNCTION_ARGS); + +extern Datum lo_lseek(PG_FUNCTION_ARGS); +extern Datum lo_tell(PG_FUNCTION_ARGS); +extern Datum lo_unlink(PG_FUNCTION_ARGS); +extern Datum lo_truncate(PG_FUNCTION_ARGS); + +/* + * compatibility option for access control + */ +extern bool lo_compat_privileges; + +/* + * These are not fmgr-callable, but are available to C code. + * Probably these should have had the underscore-free names, + * but too late now... + */ +extern int lo_read(int fd, char *buf, int len); +extern int lo_write(int fd, const char *buf, int len); + +/* + * Cleanup LOs at xact commit/abort + */ +extern void AtEOXact_LargeObject(bool isCommit); +extern void AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid, + SubTransactionId parentSubid); + +#endif /* BE_FSSTUBS_H */ diff --git a/src/libpq/libpq/crypt.h b/src/libpq/libpq/crypt.h new file mode 100644 index 00000000..2fdfb926 --- /dev/null +++ b/src/libpq/libpq/crypt.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * crypt.h + * Interface to libpq/crypt.c + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/crypt.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_CRYPT_H +#define PG_CRYPT_H + +#include "libpq/libpq-be.h" + +extern int md5_crypt_verify(const Port *port, const char *user, + char *client_pass); + +#endif diff --git a/src/libpq/libpq/hba.h b/src/libpq/libpq/hba.h new file mode 100644 index 00000000..b92dc0d3 --- /dev/null +++ b/src/libpq/libpq/hba.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * + * hba.h + * Interface to hba.c + * + * + * src/include/libpq/hba.h + * + *------------------------------------------------------------------------- + */ +#ifndef HBA_H +#define HBA_H + +#include "nodes/pg_list.h" +#include "libpq/pqcomm.h" + + +typedef enum UserAuth +{ + uaReject, + uaImplicitReject, + uaKrb5, + uaTrust, + uaIdent, + uaPassword, + uaMD5, + uaGSS, + uaSSPI, + uaPAM, + uaLDAP, + uaCert, + uaRADIUS, + uaPeer +} UserAuth; + +typedef enum IPCompareMethod +{ + ipCmpMask, + ipCmpSameHost, + ipCmpSameNet, + ipCmpAll +} IPCompareMethod; + +typedef enum ConnType +{ + ctLocal, + ctHost, + ctHostSSL, + ctHostNoSSL +} ConnType; + +typedef struct +{ + int linenumber; + ConnType conntype; + char *database; + char *role; + struct sockaddr_storage addr; + struct sockaddr_storage mask; + IPCompareMethod ip_cmp_method; + char *hostname; + UserAuth auth_method; + + char *usermap; + char *pamservice; + bool ldaptls; + char *ldapserver; + int ldapport; + char *ldapbinddn; + char *ldapbindpasswd; + char *ldapsearchattribute; + char *ldapbasedn; + char *ldapprefix; + char *ldapsuffix; + bool clientcert; + char *krb_server_hostname; + char *krb_realm; + bool include_realm; + char *radiusserver; + char *radiussecret; + char *radiusidentifier; + int radiusport; +} HbaLine; + +/* kluge to avoid including libpq/libpq-be.h here */ +typedef struct Port hbaPort; + +extern bool load_hba(void); +extern void load_ident(void); +extern int hba_getauthmethod(hbaPort *port); +extern int check_usermap(const char *usermap_name, + const char *pg_role, const char *auth_user, + bool case_sensitive); +extern bool pg_isblank(const char c); + +#endif /* HBA_H */ diff --git a/src/libpq/libpq/ip.h b/src/libpq/libpq/ip.h new file mode 100644 index 00000000..149c1ff9 --- /dev/null +++ b/src/libpq/libpq/ip.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * ip.h + * Definitions for IPv6-aware network access. + * + * These definitions are used by both frontend and backend code. Be careful + * what you include here! + * + * Copyright (c) 2003-2011, PostgreSQL Global Development Group + * + * src/include/libpq/ip.h + * + *------------------------------------------------------------------------- + */ +#ifndef IP_H +#define IP_H + +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" + + +#ifdef HAVE_UNIX_SOCKETS +#define IS_AF_UNIX(fam) ((fam) == AF_UNIX) +#else +#define IS_AF_UNIX(fam) (0) +#endif + +typedef void (*PgIfAddrCallback) (struct sockaddr * addr, + struct sockaddr * netmask, + void *cb_data); + +extern int pg_getaddrinfo_all(const char *hostname, const char *servname, + const struct addrinfo * hintp, + struct addrinfo ** result); +extern void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai); + +extern int pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags); + +extern int pg_range_sockaddr(const struct sockaddr_storage * addr, + const struct sockaddr_storage * netaddr, + const struct sockaddr_storage * netmask); + +extern int pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, + char *numbits, int family); + +#ifdef HAVE_IPV6 +extern void pg_promote_v4_to_v6_addr(struct sockaddr_storage * addr); +extern void pg_promote_v4_to_v6_mask(struct sockaddr_storage * addr); +#endif + +extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data); + +#endif /* IP_H */ diff --git a/src/libpq/libpq/libpq-be.h b/src/libpq/libpq/libpq-be.h new file mode 100644 index 00000000..77e190fd --- /dev/null +++ b/src/libpq/libpq/libpq-be.h @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------- + * + * libpq_be.h + * This file contains definitions for structures and externs used + * by the postmaster during client authentication. + * + * Note that this is backend-internal and is NOT exported to clients. + * Structs that need to be client-visible are in pqcomm.h. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/libpq-be.h + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_BE_H +#define LIBPQ_BE_H + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef USE_SSL +#include +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef ENABLE_GSS +#if defined(HAVE_GSSAPI_H) +#include +#else +#include +#endif /* HAVE_GSSAPI_H */ +/* + * GSSAPI brings in headers that set a lot of things in the global namespace on win32, + * that doesn't match the msvc build. It gives a bunch of compiler warnings that we ignore, + * but also defines a symbol that simply does not exist. Undefine it again. + */ +#ifdef WIN32_ONLY_COMPILER +#undef HAVE_GETADDRINFO +#endif +#endif /* ENABLE_GSS */ + +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +#include +#endif +#include +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct +{ + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + +#include "libpq/hba.h" +#include "libpq/pqcomm.h" +#include "utils/timestamp.h" + + +typedef enum CAC_state +{ + CAC_OK, CAC_STARTUP, CAC_SHUTDOWN, CAC_RECOVERY, CAC_TOOMANY, + CAC_WAITBACKUP +} CAC_state; + + +/* + * GSSAPI specific state information + */ +#if defined(ENABLE_GSS) | defined(ENABLE_SSPI) +typedef struct +{ + gss_buffer_desc outbuf; /* GSSAPI output token buffer */ +#ifdef ENABLE_GSS + gss_cred_id_t cred; /* GSSAPI connection cred's */ + gss_ctx_id_t ctx; /* GSSAPI connection context */ + gss_name_t name; /* GSSAPI client name */ +#endif +} pg_gssinfo; +#endif + +/* + * This is used by the postmaster in its communication with frontends. It + * contains all state information needed during this communication before the + * backend is run. The Port structure is kept in malloc'd memory and is + * still available when a backend is running (see MyProcPort). The data + * it points to must also be malloc'd, or else palloc'd in TopMemoryContext, + * so that it survives into PostgresMain execution! + */ + +typedef struct Port +{ + pgsocket sock; /* File descriptor */ + bool noblock; /* is the socket in non-blocking mode? */ + ProtocolVersion proto; /* FE/BE protocol version */ + SockAddr laddr; /* local addr (postmaster) */ + SockAddr raddr; /* remote addr (client) */ + char *remote_host; /* name (or ip addr) of remote host */ + char *remote_hostname;/* name (not ip addr) of remote host, if + * available */ + int remote_hostname_resolv; /* +1 = remote_hostname is known to + * resolve to client's IP address; -1 + * = remote_hostname is known NOT to + * resolve to client's IP address; 0 = + * we have not done the forward DNS + * lookup yet */ + char *remote_port; /* text rep of remote port */ + CAC_state canAcceptConnections; /* postmaster connection status */ + + /* + * Information that needs to be saved from the startup packet and passed + * into backend execution. "char *" fields are NULL if not set. + * guc_options points to a List of alternating option names and values. + */ + char *database_name; + char *user_name; + char *cmdline_options; + List *guc_options; + + /* + * Information that needs to be held during the authentication cycle. + */ + HbaLine *hba; + char md5Salt[4]; /* Password salt */ + + /* + * Information that really has no business at all being in struct Port, + * but since it gets used by elog.c in the same way as database_name and + * other members of this struct, we may as well keep it here. + */ + TimestampTz SessionStartTime; /* backend start time */ + + /* + * TCP keepalive settings. + * + * default values are 0 if AF_UNIX or not yet known; current values are 0 + * if AF_UNIX or using the default. Also, -1 in a default value means we + * were unable to find out the default (getsockopt failed). + */ + int default_keepalives_idle; + int default_keepalives_interval; + int default_keepalives_count; + int keepalives_idle; + int keepalives_interval; + int keepalives_count; + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + + /* + * If GSSAPI is supported, store GSSAPI information. Oterwise, store a + * NULL pointer to make sure offsets in the struct remain the same. + */ + pg_gssinfo *gss; +#else + void *gss; +#endif + + /* + * SSL structures (keep these last so that USE_SSL doesn't affect + * locations of other fields) + */ +#ifdef USE_SSL + SSL *ssl; + X509 *peer; + char peer_dn[128 + 1]; + char peer_cn[SM_USER + 1]; + unsigned long count; +#endif +} Port; + + +extern ProtocolVersion FrontendProtocol; + +/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ + +extern int pq_getkeepalivesidle(Port *port); +extern int pq_getkeepalivesinterval(Port *port); +extern int pq_getkeepalivescount(Port *port); + +extern int pq_setkeepalivesidle(int idle, Port *port); +extern int pq_setkeepalivesinterval(int interval, Port *port); +extern int pq_setkeepalivescount(int count, Port *port); + +#endif /* LIBPQ_BE_H */ diff --git a/src/libpq/libpq/libpq-fs.h b/src/libpq/libpq/libpq-fs.h new file mode 100644 index 00000000..47053e0c --- /dev/null +++ b/src/libpq/libpq/libpq-fs.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * libpq-fs.h + * definitions for using Inversion file system routines (ie, large objects) + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/libpq-fs.h + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_FS_H +#define LIBPQ_FS_H + +/* + * Read/write mode flags for inversion (large object) calls + */ + +#define INV_WRITE 0x00020000 +#define INV_READ 0x00040000 + +#endif /* LIBPQ_FS_H */ diff --git a/src/libpq/libpq/libpq.h b/src/libpq/libpq/libpq.h new file mode 100644 index 00000000..38f96af5 --- /dev/null +++ b/src/libpq/libpq/libpq.h @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + * + * libpq.h + * POSTGRES LIBPQ buffer structure definitions. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/libpq.h + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_H +#define LIBPQ_H + +#include +#include + +#include "lib/stringinfo.h" +#include "libpq/libpq-be.h" + +/* ---------------- + * PQArgBlock + * Information (pointer to array of this structure) required + * for the PQfn() call. (This probably ought to go somewhere else...) + * ---------------- + */ +typedef struct +{ + int len; + int isint; + union + { + int *ptr; /* can't use void (dec compiler barfs) */ + int integer; + } u; +} PQArgBlock; + +/* + * External functions. + */ + +/* + * prototypes for functions in pqcomm.c + */ +extern int StreamServerPort(int family, char *hostName, + unsigned short portNumber, char *unixSocketName, pgsocket ListenSocket[], + int MaxListen); +extern int StreamConnection(pgsocket server_fd, Port *port); +extern void StreamClose(pgsocket sock); +extern void TouchSocketFile(void); +extern void pq_init(void); +extern void pq_comm_reset(void); +extern int pq_getbytes(char *s, size_t len); +extern int pq_getstring(StringInfo s); +extern int pq_getmessage(StringInfo s, int maxlen); +extern int pq_getbyte(void); +extern int pq_peekbyte(void); +extern int pq_getbyte_if_available(unsigned char *c); +extern int pq_putbytes(const char *s, size_t len); +extern int pq_flush(void); +extern int pq_flush_if_writable(void); +extern bool pq_is_send_pending(void); +extern int pq_putmessage(char msgtype, const char *s, size_t len); +extern void pq_putmessage_noblock(char msgtype, const char *s, size_t len); +extern void pq_startcopyout(void); +extern void pq_endcopyout(bool errorAbort); + +/* + * prototypes for functions in be-secure.c + */ +extern int secure_initialize(void); +extern bool secure_loaded_verify_locations(void); +extern void secure_destroy(void); +extern int secure_open_server(Port *port); +extern void secure_close(Port *port); +extern ssize_t secure_read(Port *port, void *ptr, size_t len); +extern ssize_t secure_write(Port *port, void *ptr, size_t len); + +#endif /* LIBPQ_H */ diff --git a/src/libpq/libpq/md5.h b/src/libpq/libpq/md5.h new file mode 100644 index 00000000..ffd18d03 --- /dev/null +++ b/src/libpq/libpq/md5.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * md5.h + * Interface to libpq/md5.c + * + * These definitions are needed by both frontend and backend code to work + * with MD5-encrypted passwords. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/md5.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_MD5_H +#define PG_MD5_H + +#define MD5_PASSWD_LEN 35 + +#define isMD5(passwd) (strncmp(passwd, "md5", 3) == 0 && \ + strlen(passwd) == MD5_PASSWD_LEN) + + +extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum); +extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf); +extern bool pg_md5_encrypt(const char *passwd, const char *salt, + size_t salt_len, char *buf); + +#endif diff --git a/src/libpq/libpq/pqcomm.h b/src/libpq/libpq/pqcomm.h new file mode 100644 index 00000000..bd0fa250 --- /dev/null +++ b/src/libpq/libpq/pqcomm.h @@ -0,0 +1,191 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.h + * Definitions common to frontends and backends. + * + * NOTE: for historical reasons, this does not correspond to pqcomm.c. + * pqcomm.c's routines are declared in libpq.h. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/pqcomm.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQCOMM_H +#define PQCOMM_H + +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#include + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE + +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#define ss_family __ss_family +#else +#error struct sockaddr_storage does not provide an ss_family member +#endif +#endif + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN +#define ss_len __ss_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#else /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +/* Define a struct sockaddr_storage if we don't have one. */ + +struct sockaddr_storage +{ + union + { + struct sockaddr sa; /* get the system-dependent fields */ + int64 ss_align; /* ensures struct is properly aligned */ + char ss_pad[128]; /* ensures struct has desired size */ + } ss_stuff; +}; + +#define ss_family ss_stuff.sa.sa_family +/* It should have an ss_len field if sockaddr has sa_len. */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define ss_len ss_stuff.sa.sa_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */ + +typedef struct +{ + struct sockaddr_storage addr; + ACCEPT_TYPE_ARG3 salen; +} SockAddr; + +/* Configure the UNIX socket location for the well known port. */ + +#define UNIXSOCK_PATH(path, port, sockdir) \ + snprintf(path, sizeof(path), "%s/.s.PGSQL.%d", \ + ((sockdir) && *(sockdir) != '\0') ? (sockdir) : \ + DEFAULT_PGSOCKET_DIR, \ + (port)) + +/* + * These manipulate the frontend/backend protocol version number. + * + * The major number should be incremented for incompatible changes. The minor + * number should be incremented for compatible changes (eg. additional + * functionality). + * + * If a backend supports version m.n of the protocol it must actually support + * versions m.[0..n]. Backend support for version m-1 can be dropped after a + * `reasonable' length of time. + * + * A frontend isn't required to support anything other than the current + * version. + */ + +#define PG_PROTOCOL_MAJOR(v) ((v) >> 16) +#define PG_PROTOCOL_MINOR(v) ((v) & 0x0000ffff) +#define PG_PROTOCOL(m,n) (((m) << 16) | (n)) + +/* The earliest and latest frontend/backend protocol version supported. */ + +#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) +#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0) + +typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ + +typedef ProtocolVersion MsgType; + + +/* + * Packet lengths are 4 bytes in network byte order. + * + * The initial length is omitted from the packet layouts appearing below. + */ + +typedef uint32 PacketLen; + + +/* + * Old-style startup packet layout with fixed-width fields. This is used in + * protocol 1.0 and 2.0, but not in later versions. Note that the fields + * in this layout are '\0' terminated only if there is room. + */ + +#define SM_DATABASE 64 +#define SM_USER 32 +/* We append database name if db_user_namespace true. */ +#define SM_DATABASE_USER (SM_DATABASE+SM_USER+1) /* +1 for @ */ +#define SM_OPTIONS 64 +#define SM_UNUSED 64 +#define SM_TTY 64 + +typedef struct StartupPacket +{ + ProtocolVersion protoVersion; /* Protocol version */ + char database[SM_DATABASE]; /* Database name */ + /* Db_user_namespace appends dbname */ + char user[SM_USER]; /* User name */ + char options[SM_OPTIONS]; /* Optional additional args */ + char unused[SM_UNUSED]; /* Unused */ + char tty[SM_TTY]; /* Tty for debug output */ +} StartupPacket; + +extern bool Db_user_namespace; + +/* + * In protocol 3.0 and later, the startup packet length is not fixed, but + * we set an arbitrary limit on it anyway. This is just to prevent simple + * denial-of-service attacks via sending enough data to run the server + * out of memory. + */ +#define MAX_STARTUP_PACKET_LENGTH 10000 + + +/* These are the authentication request codes sent by the backend. */ + +#define AUTH_REQ_OK 0 /* User is authenticated */ +#define AUTH_REQ_KRB4 1 /* Kerberos V4. Not supported any more. */ +#define AUTH_REQ_KRB5 2 /* Kerberos V5 */ +#define AUTH_REQ_PASSWORD 3 /* Password */ +#define AUTH_REQ_CRYPT 4 /* crypt password. Not supported any more. */ +#define AUTH_REQ_MD5 5 /* md5 password */ +#define AUTH_REQ_SCM_CREDS 6 /* transfer SCM credentials */ +#define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */ +#define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */ +#define AUTH_REQ_SSPI 9 /* SSPI negotiate without wrap() */ + +typedef uint32 AuthRequest; + + +/* + * A client can also send a cancel-current-operation request to the postmaster. + * This is uglier than sending it directly to the client's backend, but it + * avoids depending on out-of-band communication facilities. + * + * The cancel request code must not match any protocol version number + * we're ever likely to use. This random choice should do. + */ +#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678) + +typedef struct CancelRequestPacket +{ + /* Note that each field is stored in network byte order! */ + MsgType cancelRequestCode; /* code to identify a cancel request */ + uint32 backendPID; /* PID of client's backend */ + uint32 cancelAuthCode; /* secret key to authorize cancel */ +} CancelRequestPacket; + + +/* + * A client can also start by sending a SSL negotiation request, to get a + * secure channel. + */ +#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679) + +#endif /* PQCOMM_H */ diff --git a/src/libpq/libpq/pqformat.h b/src/libpq/libpq/pqformat.h new file mode 100644 index 00000000..663da909 --- /dev/null +++ b/src/libpq/libpq/pqformat.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * pqformat.h + * Definitions for formatting and parsing frontend/backend messages + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/pqformat.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQFORMAT_H +#define PQFORMAT_H + +#include "lib/stringinfo.h" + +extern void pq_beginmessage(StringInfo buf, char msgtype); +extern void pq_sendbyte(StringInfo buf, int byt); +extern void pq_sendbytes(StringInfo buf, const char *data, int datalen); +extern void pq_sendcountedtext(StringInfo buf, const char *str, int slen, + bool countincludesself); +extern void pq_sendtext(StringInfo buf, const char *str, int slen); +extern void pq_sendstring(StringInfo buf, const char *str); +extern void pq_send_ascii_string(StringInfo buf, const char *str); +extern void pq_sendint(StringInfo buf, int i, int b); +extern void pq_sendint64(StringInfo buf, int64 i); +extern void pq_sendfloat4(StringInfo buf, float4 f); +extern void pq_sendfloat8(StringInfo buf, float8 f); +extern void pq_endmessage(StringInfo buf); + +extern void pq_begintypsend(StringInfo buf); +extern bytea *pq_endtypsend(StringInfo buf); + +extern void pq_puttextmessage(char msgtype, const char *str); +extern void pq_putemptymessage(char msgtype); + +extern int pq_getmsgbyte(StringInfo msg); +extern unsigned int pq_getmsgint(StringInfo msg, int b); +extern int64 pq_getmsgint64(StringInfo msg); +extern float4 pq_getmsgfloat4(StringInfo msg); +extern float8 pq_getmsgfloat8(StringInfo msg); +extern const char *pq_getmsgbytes(StringInfo msg, int datalen); +extern void pq_copymsgbytes(StringInfo msg, char *buf, int datalen); +extern char *pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes); +extern const char *pq_getmsgstring(StringInfo msg); +extern void pq_getmsgend(StringInfo msg); + +#endif /* PQFORMAT_H */ diff --git a/src/libpq/libpq/pqsignal.h b/src/libpq/libpq/pqsignal.h new file mode 100644 index 00000000..ed3b7c9a --- /dev/null +++ b/src/libpq/libpq/pqsignal.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.h + * prototypes for the reliable BSD-style signal(2) routine. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/pqsignal.h + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#ifndef PQSIGNAL_H +#define PQSIGNAL_H + +#include + +#ifdef HAVE_SIGPROCMASK +extern sigset_t UnBlockSig, + BlockSig, + StartupBlockSig; + +#define PG_SETMASK(mask) sigprocmask(SIG_SETMASK, mask, NULL) +#else /* not HAVE_SIGPROCMASK */ +extern int UnBlockSig, + BlockSig, + StartupBlockSig; + +#ifndef WIN32 +#define PG_SETMASK(mask) sigsetmask(*((int*)(mask))) +#else +#define PG_SETMASK(mask) pqsigsetmask(*((int*)(mask))) +int pqsigsetmask(int mask); +#endif + +#define sigaddset(set, signum) (*(set) |= (sigmask(signum))) +#define sigdelset(set, signum) (*(set) &= ~(sigmask(signum))) +#endif /* not HAVE_SIGPROCMASK */ + +typedef void (*pqsigfunc) (int); + +extern void pqinitmask(void); + +extern pqsigfunc pqsignal(int signo, pqsigfunc func); + +#endif /* PQSIGNAL_H */ diff --git a/src/libpq/libpqddll.def b/src/libpq/libpqddll.def new file mode 100644 index 00000000..67943d76 --- /dev/null +++ b/src/libpq/libpqddll.def @@ -0,0 +1,163 @@ +; DEF file for MS VC++ +LIBRARY LIBPQD +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 diff --git a/src/libpq/libpqdll.def b/src/libpq/libpqdll.def new file mode 100644 index 00000000..6a5877b8 --- /dev/null +++ b/src/libpq/libpqdll.def @@ -0,0 +1,163 @@ +; DEF file for MS VC++ +LIBRARY LIBPQ +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 diff --git a/src/libpq/mb/pg_wchar.h b/src/libpq/mb/pg_wchar.h new file mode 100644 index 00000000..826c7af5 --- /dev/null +++ b/src/libpq/mb/pg_wchar.h @@ -0,0 +1,466 @@ +/*------------------------------------------------------------------------- + * + * pg_wchar.h + * multibyte-character support + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/mb/pg_wchar.h + * + * NOTES + * This is used both by the backend and by libpq, but should not be + * included by libpq client programs. In particular, a libpq client + * should not assume that the encoding IDs used by the version of libpq + * it's linked to match up with the IDs declared here. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_WCHAR_H +#define PG_WCHAR_H + +/* + * The pg_wchar type + */ +typedef unsigned int pg_wchar; + +/* + * various definitions for EUC + */ +#define SS2 0x8e /* single shift 2 (JIS0201) */ +#define SS3 0x8f /* single shift 3 (JIS0212) */ + +/* + * SJIS validation macros + */ +#define ISSJISHEAD(c) (((c) >= 0x81 && (c) <= 0x9f) || ((c) >= 0xe0 && (c) <= 0xfc)) +#define ISSJISTAIL(c) (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xfc)) + +/* + * Leading byte types or leading prefix byte for MULE internal code. + * See http://www.xemacs.org for more details. (there is a doc titled + * "XEmacs Internals Manual", "MULE Character Sets and Encodings" + * section.) + */ +/* + * Is a leading byte for "official" single byte encodings? + */ +#define IS_LC1(c) ((unsigned char)(c) >= 0x81 && (unsigned char)(c) <= 0x8d) +/* + * Is a prefix byte for "private" single byte encodings? + */ +#define IS_LCPRV1(c) ((unsigned char)(c) == 0x9a || (unsigned char)(c) == 0x9b) +/* + * Is a leading byte for "official" multibyte encodings? + */ +#define IS_LC2(c) ((unsigned char)(c) >= 0x90 && (unsigned char)(c) <= 0x99) +/* + * Is a prefix byte for "private" multibyte encodings? + */ +#define IS_LCPRV2(c) ((unsigned char)(c) == 0x9c || (unsigned char)(c) == 0x9d) + +/*---------------------------------------------------- + * leading characters + *---------------------------------------------------- + */ + +/* + * Official single byte encodings (0x81-0x8e) + */ +#define LC_ISO8859_1 0x81 /* ISO8859 Latin 1 */ +#define LC_ISO8859_2 0x82 /* ISO8859 Latin 2 */ +#define LC_ISO8859_3 0x83 /* ISO8859 Latin 3 */ +#define LC_ISO8859_4 0x84 /* ISO8859 Latin 4 */ +#define LC_TIS620 0x85 /* Thai (not supported yet) */ +#define LC_ISO8859_7 0x86 /* Greek (not supported yet) */ +#define LC_ISO8859_6 0x87 /* Arabic (not supported yet) */ +#define LC_ISO8859_8 0x88 /* Hebrew (not supported yet) */ +#define LC_JISX0201K 0x89 /* Japanese 1 byte kana */ +#define LC_JISX0201R 0x8a /* Japanese 1 byte Roman */ +/* Note that 0x8b seems to be unused as of Emacs 20.7. + * However, there might be a chance that 0x8b could be used + * in later version of Emacs. + */ +#define LC_KOI8_R 0x8b /* Cyrillic KOI8-R */ +#define LC_KOI8_U 0x8b /* Cyrillic KOI8-U */ +#define LC_ISO8859_5 0x8c /* ISO8859 Cyrillic */ +#define LC_ISO8859_9 0x8d /* ISO8859 Latin 5 (not supported yet) */ +/* #define FREE 0x8e free (unused) */ + +/* + * Unused + */ +#define CONTROL_1 0x8f /* control characters (unused) */ + +/* + * Official multibyte byte encodings (0x90-0x99) + * 0x9a-0x9d are free. 0x9e and 0x9f are reserved. + */ +#define LC_JISX0208_1978 0x90 /* Japanese Kanji, old JIS (not supported) */ +/* #define FREE 0x90 free (unused) */ +#define LC_GB2312_80 0x91 /* Chinese */ +#define LC_JISX0208 0x92 /* Japanese Kanji (JIS X 0208) */ +#define LC_KS5601 0x93 /* Korean */ +#define LC_JISX0212 0x94 /* Japanese Kanji (JIS X 0212) */ +#define LC_CNS11643_1 0x95 /* CNS 11643-1992 Plane 1 */ +#define LC_CNS11643_2 0x96 /* CNS 11643-1992 Plane 2 */ +/* #define FREE 0x97 free (unused) */ +#define LC_BIG5_1 0x98 /* Plane 1 Chinese traditional (not supported) */ +#define LC_BIG5_2 0x99 /* Plane 1 Chinese traditional (not supported) */ + +/* + * Private single byte encodings (0xa0-0xef) + */ +#define LC_SISHENG 0xa0/* Chinese SiSheng characters for + * PinYin/ZhuYin (not supported) */ +#define LC_IPA 0xa1/* IPA (International Phonetic Association) + * (not supported) */ +#define LC_VISCII_LOWER 0xa2/* Vietnamese VISCII1.1 lower-case (not + * supported) */ +#define LC_VISCII_UPPER 0xa3/* Vietnamese VISCII1.1 upper-case (not + * supported) */ +#define LC_ARABIC_DIGIT 0xa4 /* Arabic digit (not supported) */ +#define LC_ARABIC_1_COLUMN 0xa5 /* Arabic 1-column (not supported) */ +#define LC_ASCII_RIGHT_TO_LEFT 0xa6 /* ASCII (left half of ISO8859-1) with + * right-to-left direction (not + * supported) */ +#define LC_LAO 0xa7/* Lao characters (ISO10646 0E80..0EDF) (not + * supported) */ +#define LC_ARABIC_2_COLUMN 0xa8 /* Arabic 1-column (not supported) */ + +/* + * Private multibyte encodings (0xf0-0xff) + */ +#define LC_INDIAN_1_COLUMN 0xf0/* Indian charset for 1-column width glypps + * (not supported) */ +#define LC_TIBETAN_1_COLUMN 0xf1 /* Tibetan 1 column glyph (not supported) */ +#define LC_ETHIOPIC 0xf5 /* Ethiopic characters (not supported) */ +#define LC_CNS11643_3 0xf6 /* CNS 11643-1992 Plane 3 */ +#define LC_CNS11643_4 0xf7 /* CNS 11643-1992 Plane 4 */ +#define LC_CNS11643_5 0xf8 /* CNS 11643-1992 Plane 5 */ +#define LC_CNS11643_6 0xf9 /* CNS 11643-1992 Plane 6 */ +#define LC_CNS11643_7 0xfa /* CNS 11643-1992 Plane 7 */ +#define LC_INDIAN_2_COLUMN 0xfb/* Indian charset for 2-column width glypps + * (not supported) */ +#define LC_TIBETAN 0xfc /* Tibetan (not supported) */ +/* #define FREE 0xfd free (unused) */ +/* #define FREE 0xfe free (unused) */ +/* #define FREE 0xff free (unused) */ + +/* + * PostgreSQL encoding identifiers + * + * WARNING: the order of this enum must be same as order of entries + * in the pg_enc2name_tbl[] array (in mb/encnames.c), and + * in the pg_wchar_table[] array (in mb/wchar.c)! + * + * If you add some encoding don't forget to check + * PG_ENCODING_BE_LAST macro. + * + * PG_SQL_ASCII is default encoding and must be = 0. + * + * XXX We must avoid renumbering any backend encoding until libpq's major + * version number is increased beyond 5; it turns out that the backend + * encoding IDs are effectively part of libpq's ABI as far as 8.2 initdb and + * psql are concerned. + */ +typedef enum pg_enc +{ + PG_SQL_ASCII = 0, /* SQL/ASCII */ + PG_EUC_JP, /* EUC for Japanese */ + PG_EUC_CN, /* EUC for Chinese */ + PG_EUC_KR, /* EUC for Korean */ + PG_EUC_TW, /* EUC for Taiwan */ + PG_EUC_JIS_2004, /* EUC-JIS-2004 */ + PG_UTF8, /* Unicode UTF8 */ + PG_MULE_INTERNAL, /* Mule internal code */ + PG_LATIN1, /* ISO-8859-1 Latin 1 */ + PG_LATIN2, /* ISO-8859-2 Latin 2 */ + PG_LATIN3, /* ISO-8859-3 Latin 3 */ + PG_LATIN4, /* ISO-8859-4 Latin 4 */ + PG_LATIN5, /* ISO-8859-9 Latin 5 */ + PG_LATIN6, /* ISO-8859-10 Latin6 */ + PG_LATIN7, /* ISO-8859-13 Latin7 */ + PG_LATIN8, /* ISO-8859-14 Latin8 */ + PG_LATIN9, /* ISO-8859-15 Latin9 */ + PG_LATIN10, /* ISO-8859-16 Latin10 */ + PG_WIN1256, /* windows-1256 */ + PG_WIN1258, /* Windows-1258 */ + PG_WIN866, /* (MS-DOS CP866) */ + PG_WIN874, /* windows-874 */ + PG_KOI8R, /* KOI8-R */ + PG_WIN1251, /* windows-1251 */ + PG_WIN1252, /* windows-1252 */ + PG_ISO_8859_5, /* ISO-8859-5 */ + PG_ISO_8859_6, /* ISO-8859-6 */ + PG_ISO_8859_7, /* ISO-8859-7 */ + PG_ISO_8859_8, /* ISO-8859-8 */ + PG_WIN1250, /* windows-1250 */ + PG_WIN1253, /* windows-1253 */ + PG_WIN1254, /* windows-1254 */ + PG_WIN1255, /* windows-1255 */ + PG_WIN1257, /* windows-1257 */ + PG_KOI8U, /* KOI8-U */ + /* PG_ENCODING_BE_LAST points to the above entry */ + + /* followings are for client encoding only */ + PG_SJIS, /* Shift JIS (Winindows-932) */ + PG_BIG5, /* Big5 (Windows-950) */ + PG_GBK, /* GBK (Windows-936) */ + PG_UHC, /* UHC (Windows-949) */ + PG_GB18030, /* GB18030 */ + PG_JOHAB, /* EUC for Korean JOHAB */ + PG_SHIFT_JIS_2004, /* Shift-JIS-2004 */ + _PG_LAST_ENCODING_ /* mark only */ + +} pg_enc; + +#define PG_ENCODING_BE_LAST PG_KOI8U + +/* + * Please use these tests before access to pg_encconv_tbl[] + * or to other places... + */ +#define PG_VALID_BE_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST) + +#define PG_ENCODING_IS_CLIENT_ONLY(_enc) \ + ((_enc) > PG_ENCODING_BE_LAST && (_enc) < _PG_LAST_ENCODING_) + +#define PG_VALID_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) < _PG_LAST_ENCODING_) + +/* On FE are possible all encodings */ +#define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc) + +/* + * Encoding names with all aliases + */ +typedef struct pg_encname +{ + char *name; + pg_enc encoding; +} pg_encname; + +extern pg_encname pg_encname_tbl[]; +extern unsigned int pg_encname_tbl_sz; + +/* + * Careful: + * + * if (PG_VALID_ENCODING(encoding)) + * pg_enc2name_tbl[ encoding ]; + */ +typedef struct pg_enc2name +{ + char *name; + pg_enc encoding; +#ifdef WIN32 + unsigned codepage; /* codepage for WIN32 */ +#endif +} pg_enc2name; + +extern pg_enc2name pg_enc2name_tbl[]; + +/* + * Encoding names for gettext + */ +typedef struct pg_enc2gettext +{ + pg_enc encoding; + const char *name; +} pg_enc2gettext; + +extern pg_enc2gettext pg_enc2gettext_tbl[]; + +/* + * pg_wchar stuff + */ +typedef int (*mb2wchar_with_len_converter) (const unsigned char *from, + pg_wchar *to, + int len); + +typedef int (*mblen_converter) (const unsigned char *mbstr); + +typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr); + +typedef int (*mbverifier) (const unsigned char *mbstr, int len); + +typedef struct +{ + mb2wchar_with_len_converter mb2wchar_with_len; /* convert a multibyte + * string to a wchar */ + mblen_converter mblen; /* get byte length of a char */ + mbdisplaylen_converter dsplen; /* get display width of a char */ + mbverifier mbverify; /* verify multibyte sequence */ + int maxmblen; /* max bytes for a char in this encoding */ +} pg_wchar_tbl; + +extern pg_wchar_tbl pg_wchar_table[]; + +/* + * UTF-8 to local code conversion map + * Note that we limit the max length of UTF-8 to 4 bytes, + * which is UCS-4 00010000-001FFFFF range. + */ +typedef struct +{ + uint32 utf; /* UTF-8 */ + uint32 code; /* local code */ +} pg_utf_to_local; + +/* + * local code to UTF-8 conversion map + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf; /* UTF-8 */ +} pg_local_to_utf; + +/* + * UTF-8 to local code conversion map(combined characters) + */ +typedef struct +{ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ + uint32 code; /* local code */ +} pg_utf_to_local_combined; + +/* + * local code to UTF-8 conversion map(combined characters) + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ +} pg_local_to_utf_combined; + +/* + * Support macro for encoding conversion functions to validate their + * arguments. (This could be made more compact if we included fmgr.h + * here, but we don't want to do that because this header file is also + * used by frontends.) + */ +#define CHECK_ENCODING_CONVERSION_ARGS(srcencoding,destencoding) \ + check_encoding_conversion_args(PG_GETARG_INT32(0), \ + PG_GETARG_INT32(1), \ + PG_GETARG_INT32(4), \ + (srcencoding), \ + (destencoding)) + + +/* + * These functions are considered part of libpq's exported API and + * are also declared in libpq-fe.h. + */ +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +/* + * Remaining functions are not considered part of libpq's API, though many + * of them do exist inside libpq. + */ +extern pg_encname *pg_char_to_encname_struct(const char *name); + +extern int pg_mb2wchar(const char *from, pg_wchar *to); +extern int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len); +extern int pg_encoding_mb2wchar_with_len(int encoding, + const char *from, pg_wchar *to, int len); +extern int pg_char_and_wchar_strcmp(const char *s1, const pg_wchar *s2); +extern int pg_wchar_strncmp(const pg_wchar *s1, const pg_wchar *s2, size_t n); +extern int pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t n); +extern size_t pg_wchar_strlen(const pg_wchar *wstr); +extern int pg_mblen(const char *mbstr); +extern int pg_dsplen(const char *mbstr); +extern int pg_encoding_mblen(int encoding, const char *mbstr); +extern int pg_encoding_dsplen(int encoding, const char *mbstr); +extern int pg_encoding_verifymb(int encoding, const char *mbstr, int len); +extern int pg_mule_mblen(const unsigned char *mbstr); +extern int pg_mic_mblen(const unsigned char *mbstr); +extern int pg_mbstrlen(const char *mbstr); +extern int pg_mbstrlen_with_len(const char *mbstr, int len); +extern int pg_mbcliplen(const char *mbstr, int len, int limit); +extern int pg_encoding_mbcliplen(int encoding, const char *mbstr, + int len, int limit); +extern int pg_mbcharcliplen(const char *mbstr, int len, int imit); +extern int pg_encoding_max_length(int encoding); +extern int pg_database_encoding_max_length(void); + +extern int PrepareClientEncoding(int encoding); +extern int SetClientEncoding(int encoding); +extern void InitializeClientEncoding(void); +extern int pg_get_client_encoding(void); +extern const char *pg_get_client_encoding_name(void); + +extern void SetDatabaseEncoding(int encoding); +extern int GetDatabaseEncoding(void); +extern const char *GetDatabaseEncodingName(void); +extern int GetPlatformEncoding(void); +extern void pg_bind_textdomain_codeset(const char *domainname); + +extern int pg_valid_client_encoding(const char *name); +extern int pg_valid_server_encoding(const char *name); + +extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string); +extern pg_wchar utf8_to_unicode(const unsigned char *c); +extern int pg_utf_mblen(const unsigned char *); +extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len, + int src_encoding, + int dest_encoding); + +extern char *pg_client_to_server(const char *s, int len); +extern char *pg_server_to_client(const char *s, int len); +extern char *pg_any_to_server(const char *s, int len, int encoding); +extern char *pg_server_to_any(const char *s, int len, int encoding); + +extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc); +extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc); + +extern void LocalToUtf(const unsigned char *iso, unsigned char *utf, + const pg_local_to_utf *map, const pg_local_to_utf_combined *cmap, + int size1, int size2, int encoding, int len); + +extern void UtfToLocal(const unsigned char *utf, unsigned char *iso, + const pg_utf_to_local *map, const pg_utf_to_local_combined *cmap, + int size1, int size2, int encoding, int len); + +extern bool pg_verifymbstr(const char *mbstr, int len, bool noError); +extern bool pg_verify_mbstr(int encoding, const char *mbstr, int len, + bool noError); +extern int pg_verify_mbstr_len(int encoding, const char *mbstr, int len, + bool noError); + +extern void check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding); + +extern void report_invalid_encoding(int encoding, const char *mbstr, int len); +extern void report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len); + +extern void pg_ascii2mic(const unsigned char *l, unsigned char *p, int len); +extern void pg_mic2ascii(const unsigned char *mic, unsigned char *p, int len); +extern void latin2mic(const unsigned char *l, unsigned char *p, int len, + int lc, int encoding); +extern void mic2latin(const unsigned char *mic, unsigned char *p, int len, + int lc, int encoding); +extern void latin2mic_with_table(const unsigned char *l, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); +extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); + +extern bool pg_utf8_islegal(const unsigned char *source, int length); + +#ifdef WIN32 +extern WCHAR *pgwin32_toUTF16(const char *str, int len, int *utf16len); +#endif + +#endif /* PG_WCHAR_H */ diff --git a/src/libpq/md5.c b/src/libpq/md5.c new file mode 100644 index 00000000..d40c37b6 --- /dev/null +++ b/src/libpq/md5.c @@ -0,0 +1,345 @@ +/* + * md5.c + * + * Implements the MD5 Message-Digest Algorithm as specified in + * RFC 1321. This implementation is a simple one, in that it + * needs every input byte to be buffered before doing any + * calculations. I do not expect this file to be used for + * general purpose MD5'ing of large amounts of data, only for + * generating hashed passwords from limited input. + * + * Sverre H. Huseby + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/libpq/md5.c + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include "libpq/md5.h" + + +/* + * PRIVATE FUNCTIONS + */ + + +/* + * The returned array is allocated using malloc. the caller should free it + * when it is no longer needed. + */ +static uint8 * +createPaddedCopyWithLength(uint8 *b, uint32 *l) +{ + uint8 *ret; + uint32 q; + uint32 len, + newLen448; + uint32 len_high, + len_low; /* 64-bit value split into 32-bit sections */ + + len = ((b == NULL) ? 0 : *l); + newLen448 = len + 64 - (len % 64) - 8; + if (newLen448 <= len) + newLen448 += 64; + + *l = newLen448 + 8; + if ((ret = (uint8 *) malloc(sizeof(uint8) * *l)) == NULL) + return NULL; + + if (b != NULL) + memcpy(ret, b, sizeof(uint8) * len); + + /* pad */ + ret[len] = 0x80; + for (q = len + 1; q < newLen448; q++) + ret[q] = 0x00; + + /* append length as a 64 bit bitcount */ + len_low = len; + /* split into two 32-bit values */ + /* we only look at the bottom 32-bits */ + len_high = len >> 29; + len_low <<= 3; + q = newLen448; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q] = (len_high & 0xff); + + return ret; +} + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +static void +doTheRounds(uint32 X[16], uint32 state[4]) +{ + uint32 a, + b, + c, + d; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + /* round 1 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7); /* 1 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[1] + 0xe8c7b756), 12); /* 2 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[2] + 0x242070db), 17); /* 3 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[3] + 0xc1bdceee), 22); /* 4 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[4] + 0xf57c0faf), 7); /* 5 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[5] + 0x4787c62a), 12); /* 6 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[6] + 0xa8304613), 17); /* 7 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[7] + 0xfd469501), 22); /* 8 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[8] + 0x698098d8), 7); /* 9 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[9] + 0x8b44f7af), 12); /* 10 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */ + + /* round 2 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5); /* 17 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[6] + 0xc040b340), 9); /* 18 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[0] + 0xe9b6c7aa), 20); /* 20 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[5] + 0xd62f105d), 5); /* 21 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[4] + 0xe7d3fbc8), 20); /* 24 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[9] + 0x21e1cde6), 5); /* 25 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[3] + 0xf4d50d87), 14); /* 27 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[8] + 0x455a14ed), 20); /* 28 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[2] + 0xfcefa3f8), 9); /* 30 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[7] + 0x676f02d9), 14); /* 31 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */ + + /* round 3 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4); /* 33 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[8] + 0x8771f681), 11); /* 34 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[1] + 0xa4beea44), 4); /* 37 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[4] + 0x4bdecfa9), 11); /* 38 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[7] + 0xf6bb4b60), 16); /* 39 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[0] + 0xeaa127fa), 11); /* 42 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[3] + 0xd4ef3085), 16); /* 43 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[6] + 0x04881d05), 23); /* 44 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[9] + 0xd9d4d039), 4); /* 45 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[2] + 0xc4ac5665), 23); /* 48 */ + + /* round 4 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6); /* 49 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[7] + 0x432aff97), 10); /* 50 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[5] + 0xfc93a039), 21); /* 52 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[3] + 0x8f0ccc92), 10); /* 54 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[1] + 0x85845dd1), 21); /* 56 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[8] + 0x6fa87e4f), 6); /* 57 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[6] + 0xa3014314), 15); /* 59 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[4] + 0xf7537e82), 6); /* 61 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[2] + 0x2ad7d2bb), 15); /* 63 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[9] + 0xeb86d391), 21); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +static int +calculateDigestFromBuffer(uint8 *b, uint32 len, uint8 sum[16]) +{ + register uint32 i, + j, + k, + newI; + uint32 l; + uint8 *input; + register uint32 *wbp; + uint32 workBuff[16], + state[4]; + + l = len; + + state[0] = 0x67452301; + state[1] = 0xEFCDAB89; + state[2] = 0x98BADCFE; + state[3] = 0x10325476; + + if ((input = createPaddedCopyWithLength(b, &l)) == NULL) + return 0; + + for (i = 0;;) + { + if ((newI = i + 16 * 4) > l) + break; + k = i + 3; + for (j = 0; j < 16; j++) + { + wbp = (workBuff + j); + *wbp = input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k]; + k += 7; + } + doTheRounds(workBuff, state); + i = newI; + } + free(input); + + j = 0; + for (i = 0; i < 4; i++) + { + k = state[i]; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + } + return 1; +} + +static void +bytesToHex(uint8 b[16], char *s) +{ + static const char *hex = "0123456789abcdef"; + int q, + w; + + for (q = 0, w = 0; q < 16; q++) + { + s[w++] = hex[(b[q] >> 4) & 0x0F]; + s[w++] = hex[b[q] & 0x0F]; + } + s[w] = '\0'; +} + +/* + * PUBLIC FUNCTIONS + */ + +/* + * pg_md5_hash + * + * Calculates the MD5 sum of the bytes in a buffer. + * + * SYNOPSIS #include "md5.h" + * int pg_md5_hash(const void *buff, size_t len, char *hexsum) + * + * INPUT buff the buffer containing the bytes that you want + * the MD5 sum of. + * len number of bytes in the buffer. + * + * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of + * hexadecimal digits. an MD5 sum is 16 bytes long. + * each byte is represented by two heaxadecimal + * characters. you thus need to provide an array + * of 33 characters, including the trailing '\0'. + * + * RETURNS false on failure (out of memory for internal buffers) or + * true on success. + * + * STANDARDS MD5 is described in RFC 1321. + * + * AUTHOR Sverre H. Huseby + * + */ +bool +pg_md5_hash(const void *buff, size_t len, char *hexsum) +{ + uint8 sum[16]; + + if (!calculateDigestFromBuffer((uint8 *) buff, len, sum)) + return false; + + bytesToHex(sum, hexsum); + return true; +} + +bool +pg_md5_binary(const void *buff, size_t len, void *outbuf) +{ + if (!calculateDigestFromBuffer((uint8 *) buff, len, outbuf)) + return false; + return true; +} + + +/* + * Computes MD5 checksum of "passwd" (a null-terminated string) followed + * by "salt" (which need not be null-terminated). + * + * Output format is "md5" followed by a 32-hex-digit MD5 checksum. + * Hence, the output buffer "buf" must be at least 36 bytes long. + * + * Returns TRUE if okay, FALSE on error (out of memory). + */ +bool +pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, + char *buf) +{ + size_t passwd_len = strlen(passwd); + + /* +1 here is just to avoid risk of unportable malloc(0) */ + char *crypt_buf = malloc(passwd_len + salt_len + 1); + bool ret; + + if (!crypt_buf) + return false; + + /* + * Place salt at the end because it may be known by users trying to crack + * the MD5 output. + */ + memcpy(crypt_buf, passwd, passwd_len); + memcpy(crypt_buf + passwd_len, salt, salt_len); + + strcpy(buf, "md5"); + ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3); + + free(crypt_buf); + + return ret; +} diff --git a/src/libpq/nls.mk b/src/libpq/nls.mk new file mode 100644 index 00000000..9eab6dcb --- /dev/null +++ b/src/libpq/nls.mk @@ -0,0 +1,5 @@ +# src/interfaces/libpq/nls.mk +CATALOG_NAME := libpq +AVAIL_LANGUAGES := cs de es fr it ja ko pl pt_BR ru sv tr zh_CN zh_TW +GETTEXT_FILES := fe-auth.c fe-connect.c fe-exec.c fe-lobj.c fe-misc.c fe-protocol2.c fe-protocol3.c fe-secure.c +GETTEXT_TRIGGERS:= libpq_gettext pqInternalNotice:2 diff --git a/src/libpq/noblock.c b/src/libpq/noblock.c new file mode 100644 index 00000000..93a88ec7 --- /dev/null +++ b/src/libpq/noblock.c @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + * + * noblock.c + * set a file descriptor as non-blocking + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/noblock.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +bool +pg_set_noblock(pgsocket sock) +{ +#if !defined(WIN32) + return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1); +#else + unsigned long ioctlsocket_ret = 1; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} + + +bool +pg_set_block(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0 || fcntl(sock, F_SETFL, (long) (flags & ~O_NONBLOCK))) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 0; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} diff --git a/src/libpq/open.c b/src/libpq/open.c new file mode 100644 index 00000000..9831d1d6 --- /dev/null +++ b/src/libpq/open.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------- + * + * open.c + * Win32 open() replacement + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * src/port/open.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include + + +static int +openFlagsToCreateFileFlags(int openFlags) +{ + switch (openFlags & (O_CREAT | O_TRUNC | O_EXCL)) + { + /* O_EXCL is meaningless without O_CREAT */ + case 0: + case O_EXCL: + return OPEN_EXISTING; + + case O_CREAT: + return OPEN_ALWAYS; + + /* O_EXCL is meaningless without O_CREAT */ + case O_TRUNC: + case O_TRUNC | O_EXCL: + return TRUNCATE_EXISTING; + + case O_CREAT | O_TRUNC: + return CREATE_ALWAYS; + + /* O_TRUNC is meaningless with O_CREAT */ + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + return CREATE_NEW; + } + + /* will never get here */ + return 0; +} + +/* + * - file attribute setting, based on fileMode? + */ +int +pgwin32_open(const char *fileName, int fileFlags,...) +{ + int fd; + HANDLE h = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES sa; + int loops = 0; + + /* Check that we can handle the request */ + assert((fileFlags & ((O_RDONLY | O_WRONLY | O_RDWR) | O_APPEND | + (O_RANDOM | O_SEQUENTIAL | O_TEMPORARY) | + _O_SHORT_LIVED | O_DSYNC | O_DIRECT | + (O_CREAT | O_TRUNC | O_EXCL) | (O_TEXT | O_BINARY))) == fileFlags); + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + while ((h = CreateFile(fileName, + /* cannot use O_RDONLY, as it == 0 */ + (fileFlags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : + ((fileFlags & O_WRONLY) ? GENERIC_WRITE : GENERIC_READ), + /* These flags allow concurrent rename/unlink */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + &sa, + openFlagsToCreateFileFlags(fileFlags), + FILE_ATTRIBUTE_NORMAL | + ((fileFlags & O_RANDOM) ? FILE_FLAG_RANDOM_ACCESS : 0) | + ((fileFlags & O_SEQUENTIAL) ? FILE_FLAG_SEQUENTIAL_SCAN : 0) | + ((fileFlags & _O_SHORT_LIVED) ? FILE_ATTRIBUTE_TEMPORARY : 0) | + ((fileFlags & O_TEMPORARY) ? FILE_FLAG_DELETE_ON_CLOSE : 0) | + ((fileFlags & O_DIRECT) ? FILE_FLAG_NO_BUFFERING : 0) | + ((fileFlags & O_DSYNC) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL)) == INVALID_HANDLE_VALUE) + { + /* + * Sharing violation or locking error can indicate antivirus, backup + * or similar software that's locking the file. Try again for 30 + * seconds before giving up. + */ + DWORD err = GetLastError(); + + if (err == ERROR_SHARING_VIOLATION || + err == ERROR_LOCK_VIOLATION) + { + pg_usleep(100000); + loops++; + +#ifndef FRONTEND + if (loops == 50) + ereport(LOG, + (errmsg("could not open file \"%s\": %s", fileName, + (err == ERROR_SHARING_VIOLATION) ? _("sharing violation") : _("lock violation")), + errdetail("Continuing to retry for 30 seconds."), + errhint("You might have antivirus, backup, or similar software interfering with the database system."))); +#endif + + if (loops < 300) + continue; + } + + _dosmaperr(err); + return -1; + } + + /* _open_osfhandle will, on error, set errno accordingly */ + if ((fd = _open_osfhandle((intptr_t) h, fileFlags & O_APPEND)) < 0) + CloseHandle(h); /* will not affect errno */ + else if (fileFlags & (O_TEXT | O_BINARY) && + _setmode(fd, fileFlags & (O_TEXT | O_BINARY)) < 0) + { + _close(fd); + return -1; + } + + return fd; +} + +FILE * +pgwin32_fopen(const char *fileName, const char *mode) +{ + int openmode = 0; + int fd; + + if (strstr(mode, "r+")) + openmode |= O_RDWR; + else if (strchr(mode, 'r')) + openmode |= O_RDONLY; + if (strstr(mode, "w+")) + openmode |= O_RDWR | O_CREAT | O_TRUNC; + else if (strchr(mode, 'w')) + openmode |= O_WRONLY | O_CREAT | O_TRUNC; + if (strchr(mode, 'a')) + openmode |= O_WRONLY | O_CREAT | O_APPEND; + + if (strchr(mode, 'b')) + openmode |= O_BINARY; + if (strchr(mode, 't')) + openmode |= O_TEXT; + + fd = pgwin32_open(fileName, openmode); + if (fd == -1) + return NULL; + return _fdopen(fd, mode); +} + +#endif diff --git a/src/libpq/pg_config.h.darwin b/src/libpq/pg_config.h.darwin new file mode 100644 index 00000000..8a7a3889 --- /dev/null +++ b/src/libpq/pg_config.h.darwin @@ -0,0 +1,871 @@ +/* src/include/pg_config.h. Generated from pg_config.h.in by configure. */ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* Define to the type of arg 1 of 'accept' */ +#define ACCEPT_TYPE_ARG1 int + +/* Define to the type of arg 2 of 'accept' */ +#define ACCEPT_TYPE_ARG2 struct sockaddr * + +/* Define to the type of arg 3 of 'accept' */ +#define ACCEPT_TYPE_ARG3 socklen_t + +/* Define to the return type of 'accept' */ +#define ACCEPT_TYPE_RETURN int + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* The normal alignment of `double', in bytes. */ +#define ALIGNOF_DOUBLE 8 + +/* The normal alignment of `int', in bytes. */ +#define ALIGNOF_INT 4 + +/* The normal alignment of `long', in bytes. */ +#define ALIGNOF_LONG 8 + +/* The normal alignment of `long long int', in bytes. */ +/* #undef ALIGNOF_LONG_LONG_INT */ + +/* The normal alignment of `short', in bytes. */ +#define ALIGNOF_SHORT 2 + +/* Size of a disk block --- this also limits the size of a tuple. You can set + it bigger if you need bigger tuples (although TOAST should reduce the need + to have large tuples, since fields can be spread across multiple tuples). + BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is + currently 2^15 (32768). This is determined by the 15-bit widths of the + lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h). + Changing BLCKSZ requires an initdb. */ +#define BLCKSZ 8192 + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#define DEF_PGPORT 5432 + +/* Define to the default TCP port number as a string constant. */ +#define DEF_PGPORT_STR "5432" + +/* Define to 1 to enable DTrace support. (--enable-dtrace) */ +/* #undef ENABLE_DTRACE */ + +/* Define to build with GSSAPI support. (--with-gssapi) */ +/* #undef ENABLE_GSS */ + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +/* #undef ENABLE_NLS */ + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#define ENABLE_THREAD_SAFETY 1 + +/* float4 values are passed by value if 'true', by reference if 'false' */ +#define FLOAT4PASSBYVAL true + +/* float8, int8, and related values are passed by value if 'true', by + reference if 'false' */ +#define FLOAT8PASSBYVAL true + +/* Define to 1 if getpwuid_r() takes a 5th argument. */ +#define GETPWUID_R_5ARG /**/ + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +/* #undef GETTIMEOFDAY_1ARG */ + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `append_history' function. */ +/* #undef HAVE_APPEND_HISTORY */ + +/* Define to 1 if you have the `atexit' function. */ +#define HAVE_ATEXIT 1 + +/* Define to 1 if you have the `cbrt' function. */ +#define HAVE_CBRT 1 + +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ + +/* Define to 1 if you have the `crypt' function. */ +#define HAVE_CRYPT 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPT_H */ + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#define HAVE_DECL_FDATASYNC 0 + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#define HAVE_DECL_F_FULLFSYNC 1 + +/* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you + don't. */ +#define HAVE_DECL_POSIX_FADVISE 0 + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 1 + +/* Define to 1 if you have the declaration of `strlcat', and to 0 if you + don't. */ +#define HAVE_DECL_STRLCAT 1 + +/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you + don't. */ +#define HAVE_DECL_STRLCPY 1 + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_SIGLIST 1 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLD_H */ + +/* Define to 1 if you have the `dlopen' function. */ +#define HAVE_DLOPEN 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* Define to 1 if you have the `erand48' function. */ +#define HAVE_ERAND48 1 + +/* Define to 1 if you have the `ERR_set_mark' function. */ +/* #undef HAVE_ERR_SET_MARK */ + +/* Define to 1 if you have the `fcvt' function. */ +#define HAVE_FCVT 1 + +/* Define to 1 if you have the `fdatasync' function. */ +#define HAVE_FDATASYNC 1 + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the `fp_class_d' function. */ +/* #undef HAVE_FP_CLASS_D */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if your compiler understands __func__. */ +#define HAVE_FUNCNAME__FUNC 1 + +/* Define to 1 if your compiler understands __FUNCTION__. */ +/* #undef HAVE_FUNCNAME__FUNCTION */ + +/* Define to 1 if you have the `getaddrinfo' function. */ +#define HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `getifaddrs' function. */ +#define HAVE_GETIFADDRS 1 + +/* Define to 1 if you have the `getopt' function. */ +#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `getpeereid' function. */ +#define HAVE_GETPEEREID 1 + +/* Define to 1 if you have the `getpeerucred' function. */ +/* #undef HAVE_GETPEERUCRED */ + +/* Define to 1 if you have the `getpwuid_r' function. */ +#define HAVE_GETPWUID_R 1 + +/* Define to 1 if you have the `getrlimit' function. */ +#define HAVE_GETRLIMIT 1 + +/* Define to 1 if you have the `getrusage' function. */ +#define HAVE_GETRUSAGE 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the `history_truncate_file' function. */ +#define HAVE_HISTORY_TRUNCATE_FILE 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_IFADDRS_H 1 + +/* Define to 1 if you have the `inet_aton' function. */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if the system has the type `int64'. */ +/* #undef HAVE_INT64 */ + +/* Define to 1 if the system has the type `int8'. */ +/* #undef HAVE_INT8 */ + +/* Define to 1 if the system has the type `intptr_t'. */ +#define HAVE_INTPTR_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the global variable 'int opterr'. */ +#define HAVE_INT_OPTERR 1 + +/* Define to 1 if you have the global variable 'int optreset'. */ +#define HAVE_INT_OPTRESET 1 + +/* Define to 1 if you have the global variable 'int timezone'. */ +#define HAVE_INT_TIMEZONE /**/ + +/* Define to 1 if you have support for IPv6. */ +#define HAVE_IPV6 1 + +/* Define to 1 if you have isinf(). */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_KERNEL_IMAGE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_KERNEL_OS_H */ + +/* Define to 1 if `e_data' is member of `krb5_error'. */ +/* #undef HAVE_KRB5_ERROR_E_DATA */ + +/* Define to 1 if `text.data' is member of `krb5_error'. */ +/* #undef HAVE_KRB5_ERROR_TEXT_DATA */ + +/* Define to 1 if you have krb5_free_unparsed_name */ +/* #undef HAVE_KRB5_FREE_UNPARSED_NAME */ + +/* Define to 1 if `client' is member of `krb5_ticket'. */ +/* #undef HAVE_KRB5_TICKET_CLIENT */ + +/* Define to 1 if `enc_part2' is member of `krb5_ticket'. */ +/* #undef HAVE_KRB5_TICKET_ENC_PART2 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LDAP_H */ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +/* #undef HAVE_LIBCRYPTO */ + +/* Define to 1 if you have the `eay32' library (-leay32). */ +/* #undef HAVE_LIBEAY32 */ + +/* Define to 1 if you have the `ldap' library (-lldap). */ +/* #undef HAVE_LIBLDAP */ + +/* Define to 1 if you have the `ldap_r' library (-lldap_r). */ +/* #undef HAVE_LIBLDAP_R */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `pam' library (-lpam). */ +/* #undef HAVE_LIBPAM */ + +/* Define if you have a function readline library */ +#define HAVE_LIBREADLINE 1 + +/* Define to 1 if you have the `selinux' library (-lselinux). */ +/* #undef HAVE_LIBSELINUX */ + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef HAVE_LIBSSL */ + +/* Define to 1 if you have the `ssleay32' library (-lssleay32). */ +/* #undef HAVE_LIBSSLEAY32 */ + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +/* #undef HAVE_LIBWLDAP32 */ + +/* Define to 1 if you have the `xml2' library (-lxml2). */ +/* #undef HAVE_LIBXML2 */ + +/* Define to 1 if you have the `xslt' library (-lxslt). */ +/* #undef HAVE_LIBXSLT */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +/* #undef HAVE_LL_CONSTANTS */ + +/* Define to 1 if the system has the type `locale_t'. */ +#define HAVE_LOCALE_T 1 + +/* Define to 1 if `long int' works and is 64 bits. */ +#define HAVE_LONG_INT_64 1 + +/* Define to 1 if the system has the type `long long int'. */ +#define HAVE_LONG_LONG_INT 1 + +/* Define to 1 if `long long int' works and is 64 bits. */ +/* #undef HAVE_LONG_LONG_INT_64 */ + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +/* #undef HAVE_MINIDUMP_TYPE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the `on_exit' function. */ +/* #undef HAVE_ON_EXIT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OSSP_UUID_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PAM_PAM_APPL_H */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `posix_fadvise' function. */ +/* #undef HAVE_POSIX_FADVISE */ + +/* Define to 1 if you have the POSIX signal interface. */ +#define HAVE_POSIX_SIGNALS /**/ + +/* Define to 1 if you have the `pstat' function. */ +/* #undef HAVE_PSTAT */ + +/* Define to 1 if the PS_STRINGS thing exists. */ +/* #undef HAVE_PS_STRINGS */ + +/* Define if you have POSIX threads libraries and header files. */ +/* #undef HAVE_PTHREAD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_READLINE_HISTORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_READLINE_READLINE_H 1 + +/* Define to 1 if you have the `readlink' function. */ +#define HAVE_READLINK 1 + +/* Define to 1 if you have the `rint' function. */ +#define HAVE_RINT 1 + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +#define HAVE_RL_COMPLETION_APPEND_CHARACTER 1 + +/* Define to 1 if you have the `rl_completion_matches' function. */ +#define HAVE_RL_COMPLETION_MATCHES 1 + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +#define HAVE_RL_FILENAME_COMPLETION_FUNCTION 1 + +/* Define to 1 if you have the `scandir' function. */ +#define HAVE_SCANDIR 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SECURITY_PAM_APPL_H */ + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `sigprocmask' function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define to 1 if you have sigsetjmp(). */ +#define HAVE_SIGSETJMP 1 + +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have spinlocks. */ +#define HAVE_SPINLOCKS 1 + +/* Define to 1 if you have the `srandom' function. */ +#define HAVE_SRANDOM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strerror_r' function. */ +#define HAVE_STRERROR_R 1 + +/* Define to 1 if cpp supports the ANSI # stringizing operator. */ +#define HAVE_STRINGIZE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +#define HAVE_STRLCAT 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoq' function. */ +/* #undef HAVE_STRTOQ */ + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `strtouq' function. */ +/* #undef HAVE_STRTOUQ */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +/* #undef HAVE_STRUCT_CMSGCRED */ + +/* Define to 1 if the system has the type `struct option'. */ +#define HAVE_STRUCT_OPTION 1 + +/* Define to 1 if `sa_len' is member of `struct sockaddr'. */ +#define HAVE_STRUCT_SOCKADDR_SA_LEN 1 + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `ss_family' is member of `struct sockaddr_storage'. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 + +/* Define to 1 if `ss_len' is member of `struct sockaddr_storage'. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 + +/* Define to 1 if `__ss_family' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if `__ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +#define HAVE_STRUCT_SOCKADDR_UN 1 + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +#define HAVE_STRUCT_TM_TM_ZONE 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SUPPORTDEFS_H */ + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the syslog interface. */ +#define HAVE_SYSLOG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IPC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SEM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SHM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TAS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UCRED_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#define HAVE_TM_ZONE 1 + +/* Define to 1 if you have the `towlower' function. */ +#define HAVE_TOWLOWER 1 + +/* Define to 1 if you have the external array `tzname'. */ +#define HAVE_TZNAME 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UCRED_H */ + +/* Define to 1 if the system has the type `uint64'. */ +/* #undef HAVE_UINT64 */ + +/* Define to 1 if the system has the type `uint8'. */ +/* #undef HAVE_UINT8 */ + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define HAVE_UINTPTR_T 1 + +/* Define to 1 if the system has the type `union semun'. */ +#define HAVE_UNION_SEMUN 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have unix sockets. */ +#define HAVE_UNIX_SOCKETS 1 + +/* Define to 1 if you have the `unsetenv' function. */ +#define HAVE_UNSETENV 1 + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +#define HAVE_UTIMES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UUID_H */ + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `waitpid' function. */ +#define HAVE_WAITPID 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the `wcstombs' function. */ +#define HAVE_WCSTOMBS 1 + +/* Define to 1 if you have the `wcstombs_l' function. */ +#define HAVE_WCSTOMBS_L 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINLDAP_H */ + +/* Define to the appropriate snprintf format for 64-bit ints. */ +#define INT64_FORMAT "%ld" + +/* Define to build with Kerberos 5 support. (--with-krb5) */ +/* #undef KRB5 */ + +/* Define to 1 if `locale_t' requires . */ +#define LOCALE_T_IN_XLOCALE 1 + +/* Define as the maximum alignment requirement of any C data type. */ +#define MAXIMUM_ALIGNOF 8 + +/* Define bytes to use libc memset(). */ +#define MEMSET_LOOP_LIMIT 1024 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "pgsql-bugs@postgresql.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "PostgreSQL" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "PostgreSQL 9.1.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "postgresql" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "9.1.1" + +/* Define to the name of the default PostgreSQL service principal in Kerberos. + (--with-krb-srvnam=NAME) */ +#define PG_KRB_SRVNAM "postgres" + +/* PostgreSQL major version as a string */ +#define PG_MAJORVERSION "9.1" + +/* PostgreSQL version as a string */ +#define PG_VERSION "9.1.1" + +/* PostgreSQL version as a number */ +#define PG_VERSION_NUM 90101 + +/* A string containing the version number, platform, and C compiler */ +#define PG_VERSION_STR "PostgreSQL 9.1.1 on x86_64-apple-darwin10.8.0, compiled by i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3), 64-bit" + +/* Define to 1 to allow profiling output to be saved separately for each + process. */ +/* #undef PROFILE_PID_DIR */ + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus, + the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger + than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be + less than your OS' limit on file size. This is often 2 GB or 4GB in a + 32-bit operating system, unless you have large file support enabled. By + default, we make the limit 1 GB to avoid any possible integer-overflow + problems within the OS. A limit smaller than necessary only means we divide + a large relation into more chunks than necessary, so it seems best to err + in the direction of a small limit. A power-of-2 value is recommended to + save a few cycles in md.c, but is not absolutely required. Changing + RELSEG_SIZE requires an initdb. */ +#define RELSEG_SIZE 131072 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 8 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 8 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r() returns a int. */ +#define STRERROR_R_INT /**/ + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to the appropriate snprintf format for unsigned 64-bit ints. */ +#define UINT64_FORMAT "%lu" + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +/* #undef USE_ASSERT_CHECKING */ + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +/* #undef USE_BONJOUR */ + +/* Define to 1 if you want float4 values to be passed by value. + (--enable-float4-byval) */ +#define USE_FLOAT4_BYVAL 1 + +/* Define to 1 if you want float8, int8, etc values to be passed by value. + (--enable-float8-byval) */ +#define USE_FLOAT8_BYVAL 1 + +/* Define to 1 if "static inline" works without unwanted warnings from + compilations where static inline functions are defined but not called. */ +#define USE_INLINE 1 + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +#define USE_INTEGER_DATETIMES 1 + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +/* #undef USE_LDAP */ + +/* Define to 1 to build with XML support. (--with-libxml) */ +/* #undef USE_LIBXML */ + +/* Define to 1 to use XSLT support when building contrib/xml2. + (--with-libxslt) */ +/* #undef USE_LIBXSLT */ + +/* Define to select named POSIX semaphores. */ +/* #undef USE_NAMED_POSIX_SEMAPHORES */ + +/* Define to 1 to build with PAM support. (--with-pam) */ +/* #undef USE_PAM */ + +/* Use replacement snprintf() functions. */ +/* #undef USE_REPL_SNPRINTF */ + +/* Define to build with (Open)SSL support. (--with-openssl) */ +/* #undef USE_SSL */ + +/* Define to select SysV-style semaphores. */ +#define USE_SYSV_SEMAPHORES 1 + +/* Define to select SysV-style shared memory. */ +#define USE_SYSV_SHARED_MEMORY 1 + +/* Define to select unnamed POSIX semaphores. */ +/* #undef USE_UNNAMED_POSIX_SEMAPHORES */ + +/* Define to select Win32-style semaphores. */ +/* #undef USE_WIN32_SEMAPHORES */ + +/* Define to select Win32-style shared memory. */ +/* #undef USE_WIN32_SHARED_MEMORY */ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Size of a WAL file block. This need have no particular relation to BLCKSZ. + XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O, + XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O + buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb. + */ +#define XLOG_BLCKSZ 8192 + +/* XLOG_SEG_SIZE is the size of a single WAL file. This must be a power of 2 + and larger than XLOG_BLCKSZ (preferably, a great deal larger than + XLOG_BLCKSZ). Changing XLOG_SEG_SIZE requires an initdb. */ +#define XLOG_SEG_SIZE (16 * 1024 * 1024) + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to the type of a signed integer type wide enough to hold a pointer, + if such a type exists, and if the system does not define it. */ +/* #undef intptr_t */ + +/* Define to empty if the C compiler does not understand signed types. */ +/* #undef signed */ + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +/* #undef uintptr_t */ + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +/* #undef volatile */ diff --git a/src/libpq/pg_config.h.win b/src/libpq/pg_config.h.win new file mode 100644 index 00000000..ca7841b6 --- /dev/null +++ b/src/libpq/pg_config.h.win @@ -0,0 +1,871 @@ +/* src/include/pg_config.h. Generated from pg_config.h.in by configure. */ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* Define to the type of arg 1 of 'accept' */ +#define ACCEPT_TYPE_ARG1 SOCKET + +/* Define to the type of arg 2 of 'accept' */ +#define ACCEPT_TYPE_ARG2 struct sockaddr * + +/* Define to the type of arg 3 of 'accept' */ +#define ACCEPT_TYPE_ARG3 int + +/* Define to the return type of 'accept' */ +#define ACCEPT_TYPE_RETURN SOCKET + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* The normal alignment of `double', in bytes. */ +#define ALIGNOF_DOUBLE 8 + +/* The normal alignment of `int', in bytes. */ +#define ALIGNOF_INT 4 + +/* The normal alignment of `long', in bytes. */ +#define ALIGNOF_LONG 4 + +/* The normal alignment of `long long int', in bytes. */ +#define ALIGNOF_LONG_LONG_INT 8 + +/* The normal alignment of `short', in bytes. */ +#define ALIGNOF_SHORT 2 + +/* Size of a disk block --- this also limits the size of a tuple. You can set + it bigger if you need bigger tuples (although TOAST should reduce the need + to have large tuples, since fields can be spread across multiple tuples). + BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is + currently 2^15 (32768). This is determined by the 15-bit widths of the + lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h). + Changing BLCKSZ requires an initdb. */ +#define BLCKSZ 8192 + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#define DEF_PGPORT 5432 + +/* Define to the default TCP port number as a string constant. */ +#define DEF_PGPORT_STR "5432" + +/* Define to 1 to enable DTrace support. (--enable-dtrace) */ +/* #undef ENABLE_DTRACE */ + +/* Define to build with GSSAPI support. (--with-gssapi) */ +/* #undef ENABLE_GSS */ + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +/* #undef ENABLE_NLS */ + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#define ENABLE_THREAD_SAFETY 1 + +/* float4 values are passed by value if 'true', by reference if 'false' */ +#define FLOAT4PASSBYVAL true + +/* float8, int8, and related values are passed by value if 'true', by + reference if 'false' */ +#define FLOAT8PASSBYVAL true + +/* Define to 1 if getpwuid_r() takes a 5th argument. */ +/* #undef GETPWUID_R_5ARG */ + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +/* #undef GETTIMEOFDAY_1ARG */ + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `append_history' function. */ +/* #undef HAVE_APPEND_HISTORY */ + +/* Define to 1 if you have the `atexit' function. */ +#define HAVE_ATEXIT 1 + +/* Define to 1 if you have the `cbrt' function. */ +#define HAVE_CBRT 1 + +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ + +/* Define to 1 if you have the `crypt' function. */ +/* #undef HAVE_CRYPT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPT_H */ + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#define HAVE_DECL_FDATASYNC 0 + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#define HAVE_DECL_F_FULLFSYNC 0 + +/* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you + don't. */ +#define HAVE_DECL_POSIX_FADVISE 0 + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 1 + +/* Define to 1 if you have the declaration of `strlcat', and to 0 if you + don't. */ +#define HAVE_DECL_STRLCAT 0 + +/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you + don't. */ +#define HAVE_DECL_STRLCPY 0 + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_SIGLIST 0 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLD_H */ + +/* Define to 1 if you have the `dlopen' function. */ +/* #undef HAVE_DLOPEN */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* Define to 1 if you have the `erand48' function. */ +/* #undef HAVE_ERAND48 */ + +/* Define to 1 if you have the `ERR_set_mark' function. */ +/* #undef HAVE_ERR_SET_MARK */ + +/* Define to 1 if you have the `fcvt' function. */ +#define HAVE_FCVT 1 + +/* Define to 1 if you have the `fdatasync' function. */ +/* #undef HAVE_FDATASYNC */ + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the `fp_class_d' function. */ +/* #undef HAVE_FP_CLASS_D */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if your compiler understands __func__. */ +#define HAVE_FUNCNAME__FUNC 1 + +/* Define to 1 if your compiler understands __FUNCTION__. */ +/* #undef HAVE_FUNCNAME__FUNCTION */ + +/* Define to 1 if you have the `getaddrinfo' function. */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `getifaddrs' function. */ +/* #undef HAVE_GETIFADDRS */ + +/* Define to 1 if you have the `getopt' function. */ +#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `getpeereid' function. */ +#define HAVE_GETPEEREID 1 + +/* Define to 1 if you have the `getpeerucred' function. */ +/* #undef HAVE_GETPEERUCRED */ + +/* Define to 1 if you have the `getpwuid_r' function. */ +/* #undef HAVE_GETPWUID_R */ + +/* Define to 1 if you have the `getrlimit' function. */ +/* #undef HAVE_GETRLIMIT */ + +/* Define to 1 if you have the `getrusage' function. */ +/* #undef HAVE_GETRUSAGE */ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_GSSAPI_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the `history_truncate_file' function. */ +/* #undef HAVE_HISTORY_TRUNCATE_FILE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IFADDRS_H */ + +/* Define to 1 if you have the `inet_aton' function. */ +/* #undef HAVE_INET_ATON */ + +/* Define to 1 if the system has the type `int64'. */ +/* #undef HAVE_INT64 */ + +/* Define to 1 if the system has the type `int8'. */ +/* #undef HAVE_INT8 */ + +/* Define to 1 if the system has the type `intptr_t'. */ +#define HAVE_INTPTR_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the global variable 'int opterr'. */ +#define HAVE_INT_OPTERR 1 + +/* Define to 1 if you have the global variable 'int optreset'. */ +#define HAVE_INT_OPTRESET 1 + +/* Define to 1 if you have the global variable 'int timezone'. */ +#define HAVE_INT_TIMEZONE /**/ + +/* Define to 1 if you have support for IPv6. */ +#define HAVE_IPV6 1 + +/* Define to 1 if you have isinf(). */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_KERNEL_IMAGE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_KERNEL_OS_H */ + +/* Define to 1 if `e_data' is member of `krb5_error'. */ +/* #undef HAVE_KRB5_ERROR_E_DATA */ + +/* Define to 1 if `text.data' is member of `krb5_error'. */ +/* #undef HAVE_KRB5_ERROR_TEXT_DATA */ + +/* Define to 1 if you have krb5_free_unparsed_name */ +/* #undef HAVE_KRB5_FREE_UNPARSED_NAME */ + +/* Define to 1 if `client' is member of `krb5_ticket'. */ +/* #undef HAVE_KRB5_TICKET_CLIENT */ + +/* Define to 1 if `enc_part2' is member of `krb5_ticket'. */ +/* #undef HAVE_KRB5_TICKET_ENC_PART2 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LANGINFO_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LDAP_H */ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +/* #undef HAVE_LIBCRYPTO */ + +/* Define to 1 if you have the `eay32' library (-leay32). */ +/* #undef HAVE_LIBEAY32 */ + +/* Define to 1 if you have the `ldap' library (-lldap). */ +/* #undef HAVE_LIBLDAP */ + +/* Define to 1 if you have the `ldap_r' library (-lldap_r). */ +/* #undef HAVE_LIBLDAP_R */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `pam' library (-lpam). */ +/* #undef HAVE_LIBPAM */ + +/* Define if you have a function readline library */ +/* #undef HAVE_LIBREADLINE */ + +/* Define to 1 if you have the `selinux' library (-lselinux). */ +/* #undef HAVE_LIBSELINUX */ + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef HAVE_LIBSSL */ + +/* Define to 1 if you have the `ssleay32' library (-lssleay32). */ +/* #undef HAVE_LIBSSLEAY32 */ + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +/* #undef HAVE_LIBWLDAP32 */ + +/* Define to 1 if you have the `xml2' library (-lxml2). */ +/* #undef HAVE_LIBXML2 */ + +/* Define to 1 if you have the `xslt' library (-lxslt). */ +/* #undef HAVE_LIBXSLT */ + +/* Define to 1 if you have the `z' library (-lz). */ +/* #undef HAVE_LIBZ */ + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +#define HAVE_LL_CONSTANTS 1 + +/* Define to 1 if the system has the type `locale_t'. */ +/* #undef HAVE_LOCALE_T */ + +/* Define to 1 if `long int' works and is 64 bits. */ +/* #undef HAVE_LONG_INT_64 */ + +/* Define to 1 if the system has the type `long long int'. */ +#define HAVE_LONG_LONG_INT 1 + +/* Define to 1 if `long long int' works and is 64 bits. */ +#define HAVE_LONG_LONG_INT_64 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +#define HAVE_MINIDUMP_TYPE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NET_IF_H */ + +/* Define to 1 if you have the `on_exit' function. */ +/* #undef HAVE_ON_EXIT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OSSP_UUID_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PAM_PAM_APPL_H */ + +/* Define to 1 if you have the `poll' function. */ +/* #undef HAVE_POLL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_POLL_H */ + +/* Define to 1 if you have the `posix_fadvise' function. */ +/* #undef HAVE_POSIX_FADVISE */ + +/* Define to 1 if you have the POSIX signal interface. */ +/* #undef HAVE_POSIX_SIGNALS */ + +/* Define to 1 if you have the `pstat' function. */ +/* #undef HAVE_PSTAT */ + +/* Define to 1 if the PS_STRINGS thing exists. */ +/* #undef HAVE_PS_STRINGS */ + +/* Define if you have POSIX threads libraries and header files. */ +/* #undef HAVE_PTHREAD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the `readlink' function. */ +/* #undef HAVE_READLINK */ + +/* Define to 1 if you have the `rint' function. */ +#define HAVE_RINT 1 + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +/* #undef HAVE_RL_COMPLETION_APPEND_CHARACTER */ + +/* Define to 1 if you have the `rl_completion_matches' function. */ +/* #undef HAVE_RL_COMPLETION_MATCHES */ + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +/* #undef HAVE_RL_FILENAME_COMPLETION_FUNCTION */ + +/* Define to 1 if you have the `scandir' function. */ +/* #undef HAVE_SCANDIR */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SECURITY_PAM_APPL_H */ + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ + +/* Define to 1 if you have the `setsid' function. */ +/* #undef HAVE_SETSID */ + +/* Define to 1 if you have the `sigprocmask' function. */ +/* #undef HAVE_SIGPROCMASK */ + +/* Define to 1 if you have sigsetjmp(). */ +/* #undef HAVE_SIGSETJMP */ + +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#define HAVE_SIG_ATOMIC_T 1 + +/* Define to 1 if you have the `snprintf' function. */ +/* #undef HAVE_SNPRINTF */ + +/* Define to 1 if you have spinlocks. */ +#define HAVE_SPINLOCKS 1 + +/* Define to 1 if you have the `srandom' function. */ +/* #undef HAVE_SRANDOM */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strerror_r' function. */ +/* #undef HAVE_STRERROR_R */ + +/* Define to 1 if cpp supports the ANSI # stringizing operator. */ +#define HAVE_STRINGIZE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +/* #undef HAVE_STRLCAT */ + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef HAVE_STRLCPY */ + +/* Define to 1 if you have the `strtol' function. */ +#define HAVE_STRTOL 1 + +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoq' function. */ +/* #undef HAVE_STRTOQ */ + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `strtouq' function. */ +/* #undef HAVE_STRTOUQ */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#define HAVE_STRUCT_ADDRINFO 1 + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +/* #undef HAVE_STRUCT_CMSGCRED */ + +/* Define to 1 if the system has the type `struct option'. */ +#define HAVE_STRUCT_OPTION 1 + +/* Define to 1 if `sa_len' is member of `struct sockaddr'. */ +/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 + +/* Define to 1 if `ss_family' is member of `struct sockaddr_storage'. */ +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 + +/* Define to 1 if `ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + +/* Define to 1 if `__ss_family' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if `__ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +/* #undef HAVE_STRUCT_SOCKADDR_UN */ + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +/* #undef HAVE_STRUCT_TM_TM_ZONE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SUPPORTDEFS_H */ + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the `sysconf' function. */ +/* #undef HAVE_SYSCONF */ + +/* Define to 1 if you have the syslog interface. */ +/* #undef HAVE_SYSLOG */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IPC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_POLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_RESOURCE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SEM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SHM_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKIO_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TAS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UCRED_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TERMIOS_H */ + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +/* #undef HAVE_TM_ZONE */ + +/* Define to 1 if you have the `towlower' function. */ +#define HAVE_TOWLOWER 1 + +/* Define to 1 if you have the external array `tzname'. */ +#define HAVE_TZNAME 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UCRED_H */ + +/* Define to 1 if the system has the type `uint64'. */ +/* #undef HAVE_UINT64 */ + +/* Define to 1 if the system has the type `uint8'. */ +/* #undef HAVE_UINT8 */ + +/* Define to 1 if the system has the type `uintptr_t'. */ +#define HAVE_UINTPTR_T 1 + +/* Define to 1 if the system has the type `union semun'. */ +/* #undef HAVE_UNION_SEMUN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have unix sockets. */ +/* #undef HAVE_UNIX_SOCKETS */ + +/* Define to 1 if you have the `unsetenv' function. */ +#define HAVE_UNSETENV 1 + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +/* #undef HAVE_UTIMES */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UUID_H */ + +/* Define to 1 if you have the `vsnprintf' function. */ +/* #undef HAVE_VSNPRINTF */ + +/* Define to 1 if you have the `waitpid' function. */ +/* #undef HAVE_WAITPID */ + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the `wcstombs' function. */ +#define HAVE_WCSTOMBS 1 + +/* Define to 1 if you have the `wcstombs_l' function. */ +/* #undef HAVE_WCSTOMBS_L */ + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINLDAP_H */ + +/* Define to the appropriate snprintf format for 64-bit ints. */ +#define INT64_FORMAT "%lld" + +/* Define to build with Kerberos 5 support. (--with-krb5) */ +/* #undef KRB5 */ + +/* Define to 1 if `locale_t' requires . */ +/* #undef LOCALE_T_IN_XLOCALE */ + +/* Define as the maximum alignment requirement of any C data type. */ +#define MAXIMUM_ALIGNOF 8 + +/* Define bytes to use libc memset(). */ +#define MEMSET_LOOP_LIMIT 1024 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "pgsql-bugs@postgresql.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "PostgreSQL" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "PostgreSQL 9.1.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "postgresql" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "9.1.1" + +/* Define to the name of the default PostgreSQL service principal in Kerberos. + (--with-krb-srvnam=NAME) */ +#define PG_KRB_SRVNAM "postgres" + +/* PostgreSQL major version as a string */ +#define PG_MAJORVERSION "9.1" + +/* PostgreSQL version as a string */ +#define PG_VERSION "9.1.1" + +/* PostgreSQL version as a number */ +#define PG_VERSION_NUM 90101 + +/* A string containing the version number, platform, and C compiler */ +#define PG_VERSION_STR "PostgreSQL 9.1.1 on x86_64-w64-mingw32, compiled by x86_64-w64-mingw32-gcc.exe (GCC) 4.5.2 20100917 (prerelease), 64-bit" + +/* Define to 1 to allow profiling output to be saved separately for each + process. */ +/* #undef PROFILE_PID_DIR */ + +/* Define to the necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus, + the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger + than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be + less than your OS' limit on file size. This is often 2 GB or 4GB in a + 32-bit operating system, unless you have large file support enabled. By + default, we make the limit 1 GB to avoid any possible integer-overflow + problems within the OS. A limit smaller than necessary only means we divide + a large relation into more chunks than necessary, so it seems best to err + in the direction of a small limit. A power-of-2 value is recommended to + save a few cycles in md.c, but is not absolutely required. Changing + RELSEG_SIZE requires an initdb. */ +#define RELSEG_SIZE 131072 + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `off_t', as computed by sizeof. */ +#define SIZEOF_OFF_T 4 + +/* The size of `size_t', as computed by sizeof. */ +#define SIZEOF_SIZE_T 8 + +/* The size of `void *', as computed by sizeof. */ +#define SIZEOF_VOID_P 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r() returns a int. */ +/* #undef STRERROR_R_INT */ + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to the appropriate snprintf format for unsigned 64-bit ints. */ +#define UINT64_FORMAT "%llu" + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +/* #undef USE_ASSERT_CHECKING */ + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +/* #undef USE_BONJOUR */ + +/* Define to 1 if you want float4 values to be passed by value. + (--enable-float4-byval) */ +#define USE_FLOAT4_BYVAL 1 + +/* Define to 1 if you want float8, int8, etc values to be passed by value. + (--enable-float8-byval) */ +#define USE_FLOAT8_BYVAL 1 + +/* Define to 1 if "static inline" works without unwanted warnings from + compilations where static inline functions are defined but not called. */ +#define USE_INLINE 1 + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +#define USE_INTEGER_DATETIMES 1 + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +/* #undef USE_LDAP */ + +/* Define to 1 to build with XML support. (--with-libxml) */ +/* #undef USE_LIBXML */ + +/* Define to 1 to use XSLT support when building contrib/xml2. + (--with-libxslt) */ +/* #undef USE_LIBXSLT */ + +/* Define to select named POSIX semaphores. */ +/* #undef USE_NAMED_POSIX_SEMAPHORES */ + +/* Define to 1 to build with PAM support. (--with-pam) */ +/* #undef USE_PAM */ + +/* Use replacement snprintf() functions. */ +#define USE_REPL_SNPRINTF 1 + +/* Define to build with (Open)SSL support. (--with-openssl) */ +/* #undef USE_SSL */ + +/* Define to select SysV-style semaphores. */ +/* #undef USE_SYSV_SEMAPHORES */ + +/* Define to select SysV-style shared memory. */ +/* #undef USE_SYSV_SHARED_MEMORY */ + +/* Define to select unnamed POSIX semaphores. */ +/* #undef USE_UNNAMED_POSIX_SEMAPHORES */ + +/* Define to select Win32-style semaphores. */ +#define USE_WIN32_SEMAPHORES 1 + +/* Define to select Win32-style shared memory. */ +#define USE_WIN32_SHARED_MEMORY 1 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Size of a WAL file block. This need have no particular relation to BLCKSZ. + XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O, + XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O + buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb. + */ +#define XLOG_BLCKSZ 8192 + +/* XLOG_SEG_SIZE is the size of a single WAL file. This must be a power of 2 + and larger than XLOG_BLCKSZ (preferably, a great deal larger than + XLOG_BLCKSZ). Changing XLOG_SEG_SIZE requires an initdb. */ +#define XLOG_SEG_SIZE (16 * 1024 * 1024) + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to the type of a signed integer type wide enough to hold a pointer, + if such a type exists, and if the system does not define it. */ +/* #undef intptr_t */ + +/* Define to empty if the C compiler does not understand signed types. */ +/* #undef signed */ + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +/* #undef uintptr_t */ + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +/* #undef volatile */ diff --git a/src/libpq/pg_config_manual.h b/src/libpq/pg_config_manual.h new file mode 100644 index 00000000..5f4cf93a --- /dev/null +++ b/src/libpq/pg_config_manual.h @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------ + * PostgreSQL manual configuration settings + * + * This file contains various configuration symbols and limits. In + * all cases, changing them is only useful in very rare situations or + * for developers. If you edit any of these, be sure to do a *full* + * rebuild (and an initdb if noted). + * + * src/include/pg_config_manual.h + *------------------------------------------------------------------------ + */ + +/* + * Maximum length for identifiers (e.g. table names, column names, + * function names). Names actually are limited to one less byte than this, + * because the length must include a trailing zero byte. + * + * Changing this requires an initdb. + */ +#define NAMEDATALEN 64 + +/* + * Maximum number of arguments to a function. + * + * The minimum value is 9 (index cost estimation uses 9-argument functions). + * The maximum possible value is around 600 (limited by index tuple size in + * pg_proc's index; BLCKSZ larger than 8K would allow more). Values larger + * than needed will waste memory and processing time, but do not directly + * cost disk space. + * + * Changing this does not require an initdb, but it does require a full + * backend recompile (including any user-defined C functions). + */ +#define FUNC_MAX_ARGS 100 + +/* + * Maximum number of columns in an index. There is little point in making + * this anything but a multiple of 32, because the main cost is associated + * with index tuple header size (see access/itup.h). + * + * Changing this requires an initdb. + */ +#define INDEX_MAX_KEYS 32 + +/* + * Set the upper and lower bounds of sequence values. + */ +#define SEQ_MAXVALUE INT64CONST(0x7FFFFFFFFFFFFFFF) +#define SEQ_MINVALUE (-SEQ_MAXVALUE) + +/* + * Number of spare LWLocks to allocate for user-defined add-on code. + */ +#define NUM_USER_DEFINED_LWLOCKS 4 + +/* + * Define this if you want to allow the lo_import and lo_export SQL + * functions to be executed by ordinary users. By default these + * functions are only available to the Postgres superuser. CAUTION: + * These functions are SECURITY HOLES since they can read and write + * any file that the PostgreSQL server has permission to access. If + * you turn this on, don't say we didn't warn you. + */ +/* #define ALLOW_DANGEROUS_LO_FUNCTIONS */ + +/* + * MAXPGPATH: standard size of a pathname buffer in PostgreSQL (hence, + * maximum usable pathname length is one less). + * + * We'd use a standard system header symbol for this, if there weren't + * so many to choose from: MAXPATHLEN, MAX_PATH, PATH_MAX are all + * defined by different "standards", and often have different values + * on the same platform! So we just punt and use a reasonably + * generous setting here. + */ +#define MAXPGPATH 1024 + +/* + * PG_SOMAXCONN: maximum accept-queue length limit passed to + * listen(2). You'd think we should use SOMAXCONN from + * , but on many systems that symbol is much smaller + * than the kernel's actual limit. In any case, this symbol need be + * twiddled only if you have a kernel that refuses large limit values, + * rather than silently reducing the value to what it can handle + * (which is what most if not all Unixen do). + */ +#define PG_SOMAXCONN 10000 + +/* + * You can try changing this if you have a machine with bytes of + * another size, but no guarantee... + */ +#define BITS_PER_BYTE 8 + +/* + * Preferred alignment for disk I/O buffers. On some CPUs, copies between + * user space and kernel space are significantly faster if the user buffer + * is aligned on a larger-than-MAXALIGN boundary. Ideally this should be + * a platform-dependent value, but for now we just hard-wire it. + */ +#define ALIGNOF_BUFFER 32 + +/* + * Disable UNIX sockets for certain operating systems. + */ +#if defined(WIN32) +#undef HAVE_UNIX_SOCKETS +#endif + +/* + * Define this if your operating system supports link() + */ +#if !defined(WIN32) && !defined(__CYGWIN__) +#define HAVE_WORKING_LINK 1 +#endif + +/* + * USE_POSIX_FADVISE controls whether Postgres will attempt to use the + * posix_fadvise() kernel call. Usually the automatic configure tests are + * sufficient, but some older Linux distributions had broken versions of + * posix_fadvise(). If necessary you can remove the #define here. + */ +#if HAVE_DECL_POSIX_FADVISE && defined(HAVE_POSIX_FADVISE) +#define USE_POSIX_FADVISE +#endif + +/* + * USE_PREFETCH code should be compiled only if we have a way to implement + * prefetching. (This is decoupled from USE_POSIX_FADVISE because there + * might in future be support for alternative low-level prefetch APIs.) + */ +#ifdef USE_POSIX_FADVISE +#define USE_PREFETCH +#endif + +/* + * This is the default directory in which AF_UNIX socket files are + * placed. Caution: changing this risks breaking your existing client + * applications, which are likely to continue to look in the old + * directory. But if you just hate the idea of sockets in /tmp, + * here's where to twiddle it. You can also override this at runtime + * with the postmaster's -k switch. + */ +#define DEFAULT_PGSOCKET_DIR "/tmp" + +/* + * The random() function is expected to yield values between 0 and + * MAX_RANDOM_VALUE. Currently, all known implementations yield + * 0..2^31-1, so we just hardwire this constant. We could do a + * configure test if it proves to be necessary. CAUTION: Think not to + * replace this with RAND_MAX. RAND_MAX defines the maximum value of + * the older rand() function, which is often different from --- and + * considerably inferior to --- random(). + */ +#define MAX_RANDOM_VALUE (0x7FFFFFFF) + +/* + * Set the format style used by gcc to check printf type functions. We really + * want the "gnu_printf" style set, which includes what glibc uses, such + * as %m for error strings and %lld for 64 bit long longs. But not all gcc + * compilers are known to support it, so we just use "printf" which all + * gcc versions alive are known to support, except on Windows where + * using "gnu_printf" style makes a dramatic difference. Maybe someday + * we'll have a configure test for this, if we ever discover use of more + * variants to be necessary. + */ +#ifdef WIN32 +#define PG_PRINTF_ATTRIBUTE gnu_printf +#else +#define PG_PRINTF_ATTRIBUTE printf +#endif + +/* + *------------------------------------------------------------------------ + * The following symbols are for enabling debugging code, not for + * controlling user-visible features or resource limits. + *------------------------------------------------------------------------ + */ + +/* + * Define this to cause pfree()'d memory to be cleared immediately, to + * facilitate catching bugs that refer to already-freed values. + * Right now, this gets defined automatically if --enable-cassert. + */ +#ifdef USE_ASSERT_CHECKING +#define CLOBBER_FREED_MEMORY +#endif + +/* + * Define this to check memory allocation errors (scribbling on more + * bytes than were allocated). Right now, this gets defined + * automatically if --enable-cassert. + */ +#ifdef USE_ASSERT_CHECKING +#define MEMORY_CONTEXT_CHECKING +#endif + +/* + * Define this to cause palloc()'d memory to be filled with random data, to + * facilitate catching code that depends on the contents of uninitialized + * memory. Caution: this is horrendously expensive. + */ +/* #define RANDOMIZE_ALLOCATED_MEMORY */ + +/* + * Define this to force all parse and plan trees to be passed through + * copyObject(), to facilitate catching errors and omissions in + * copyObject(). + */ +/* #define COPY_PARSE_PLAN_TREES */ + +/* + * Enable debugging print statements for lock-related operations. + */ +/* #define LOCK_DEBUG */ + +/* + * Enable debugging print statements for WAL-related operations; see + * also the wal_debug GUC var. + */ +#define WAL_DEBUG + +/* + * Enable tracing of resource consumption during sort operations; + * see also the trace_sort GUC var. For 8.1 this is enabled by default. + */ +#define TRACE_SORT 1 + +/* + * Enable tracing of syncscan operations (see also the trace_syncscan GUC var). + */ +/* #define TRACE_SYNCSCAN */ + +/* + * Other debug #defines (documentation, anyone?) + */ +/* #define HEAPDEBUGALL */ +/* #define ACLDEBUG */ +/* #define RTDEBUG */ diff --git a/src/libpq/pg_config_os.h.darwin b/src/libpq/pg_config_os.h.darwin new file mode 100644 index 00000000..29c4b91d --- /dev/null +++ b/src/libpq/pg_config_os.h.darwin @@ -0,0 +1,8 @@ +/* src/include/port/darwin.h */ + +#define __darwin__ 1 + +#if HAVE_DECL_F_FULLFSYNC /* not present before OS X 10.3 */ +#define HAVE_FSYNC_WRITETHROUGH + +#endif diff --git a/src/libpq/pg_config_os.h.win b/src/libpq/pg_config_os.h.win new file mode 100644 index 00000000..7e16f0ee --- /dev/null +++ b/src/libpq/pg_config_os.h.win @@ -0,0 +1,427 @@ +/* src/include/port/win32.h */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define WIN32_ONLY_COMPILER +#endif + +/* + * Make sure _WIN32_WINNT has the minumum required value. + * Leave a higher value in place. +*/ +#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0501 +#undef _WIN32_WINNT +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +/* + * Always build with SSPI support. Keep it as a #define in case + * we want a switch to disable it sometime in the future. + */ +#ifndef __BORLANDC__ +#define ENABLE_SSPI 1 +#endif + +/* undefine and redefine after #include */ +#undef mkdir + +#undef ERROR + +/* + * The Mingw64 headers choke if this is already defined - they + * define it themselves. + */ +#if defined(WIN32_ONLY_COMPILER) +#define _WINSOCKAPI_ +#endif +#include +#include +#include +#undef small +#include +#include +#include +#include +#ifndef __BORLANDC__ +#include /* for non-unicode version */ +#endif +#undef near + +/* Must be here to avoid conflicting with prototype in windows.h */ +#define mkdir(a,b) mkdir(a) + +#define ftruncate(a,b) chsize(a,b) + +/* Windows doesn't have fsync() as such, use _commit() */ +#define fsync(fd) _commit(fd) + +/* + * For historical reasons, we allow setting wal_sync_method to + * fsync_writethrough on Windows, even though it's really identical to fsync + * (both code paths wind up at _commit()). + */ +#define HAVE_FSYNC_WRITETHROUGH +#define FSYNC_WRITETHROUGH_IS_FSYNC + +#define USES_WINSOCK + +/* defines for dynamic linking on Win32 platform */ +#if defined(WIN32) || defined(__CYGWIN__) + +#if __GNUC__ && ! defined (__declspec) +#error You need egcs 1.1 or newer for compiling! +#endif + +#ifdef BUILDING_DLL +#define PGDLLIMPORT __declspec (dllexport) +#else /* not BUILDING_DLL */ +#define PGDLLIMPORT __declspec (dllimport) +#endif + +#ifdef _MSC_VER +#define PGDLLEXPORT __declspec (dllexport) +#else +#define PGDLLEXPORT +#endif +#else /* not CYGWIN, not MSVC, not MingW */ +#define PGDLLIMPORT +#define PGDLLEXPORT +#endif + + +/* + * IPC defines + */ +#undef HAVE_UNION_SEMUN +#define HAVE_UNION_SEMUN 1 + +#define IPC_RMID 256 +#define IPC_CREAT 512 +#define IPC_EXCL 1024 +#define IPC_PRIVATE 234564 +#define IPC_NOWAIT 2048 +#define IPC_STAT 4096 + +#define EACCESS 2048 +#define EIDRM 4096 + +#define SETALL 8192 +#define GETNCNT 16384 +#define GETVAL 65536 +#define SETVAL 131072 +#define GETPID 262144 + + +/* + * Signal stuff + * + * For WIN32, there is no wait() call so there are no wait() macros + * to interpret the return value of system(). Instead, system() + * return values < 0x100 are used for exit() termination, and higher + * values are used to indicated non-exit() termination, which is + * similar to a unix-style signal exit (think SIGSEGV == + * STATUS_ACCESS_VIOLATION). Return values are broken up into groups: + * + * http://msdn2.microsoft.com/en-gb/library/aa489609.aspx + * + * NT_SUCCESS 0 - 0x3FFFFFFF + * NT_INFORMATION 0x40000000 - 0x7FFFFFFF + * NT_WARNING 0x80000000 - 0xBFFFFFFF + * NT_ERROR 0xC0000000 - 0xFFFFFFFF + * + * Effectively, we don't care on the severity of the return value from + * system(), we just need to know if it was because of exit() or generated + * by the system, and it seems values >= 0x100 are system-generated. + * See this URL for a list of WIN32 STATUS_* values: + * + * Wine (URL used in our error messages) - + * http://source.winehq.org/source/include/ntstatus.h + * Descriptions - http://www.comp.nus.edu.sg/~wuyongzh/my_doc/ntstatus.txt + * MS SDK - http://www.nologs.com/ntstatus.html + * + * It seems the exception lists are in both ntstatus.h and winnt.h, but + * ntstatus.h has a more comprehensive list, and it only contains + * exception values, rather than winnt, which contains lots of other + * things: + * + * http://www.microsoft.com/msj/0197/exception/exception.aspx + * + * The ExceptionCode parameter is the number that the operating system + * assigned to the exception. You can see a list of various exception codes + * in WINNT.H by searching for #defines that start with "STATUS_". For + * example, the code for the all-too-familiar STATUS_ACCESS_VIOLATION is + * 0xC0000005. A more complete set of exception codes can be found in + * NTSTATUS.H from the Windows NT DDK. + * + * Some day we might want to print descriptions for the most common + * exceptions, rather than printing an include file name. We could use + * RtlNtStatusToDosError() and pass to FormatMessage(), which can print + * the text of error values, but MinGW does not support + * RtlNtStatusToDosError(). + */ +#define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) +#define WIFSIGNALED(w) (!WIFEXITED(w)) +#define WEXITSTATUS(w) (w) +#define WTERMSIG(w) (w) + +#define sigmask(sig) ( 1 << ((sig)-1) ) + +/* Signal function return values */ +#undef SIG_DFL +#undef SIG_ERR +#undef SIG_IGN +#define SIG_DFL ((pqsigfunc)0) +#define SIG_ERR ((pqsigfunc)-1) +#define SIG_IGN ((pqsigfunc)1) + +/* Some extra signals */ +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGTRAP 5 +#define SIGABRT 22 /* Set to match W32 value -- not UNIX value */ +#define SIGKILL 9 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGSTOP 17 +#define SIGTSTP 18 +#define SIGCONT 19 +#define SIGCHLD 20 +#define SIGTTIN 21 +#define SIGTTOU 22 /* Same as SIGABRT -- no problem, I hope */ +#define SIGWINCH 28 +#ifndef __BORLANDC__ +#define SIGUSR1 30 +#define SIGUSR2 31 +#endif + +/* + * New versions of mingw have gettimeofday() and also declare + * struct timezone to support it. + */ +#ifndef HAVE_GETTIMEOFDAY +struct timezone +{ + int tz_minuteswest; /* Minutes west of GMT. */ + int tz_dsttime; /* Nonzero if DST is ever in effect. */ +}; +#endif + +/* for setitimer in backend/port/win32/timer.c */ +#define ITIMER_REAL 0 +struct itimerval +{ + struct timeval it_interval; + struct timeval it_value; +}; + +int setitimer(int which, const struct itimerval * value, struct itimerval * ovalue); + +/* + * WIN32 does not provide 64-bit off_t, but does provide the functions operating + * with 64-bit offsets. + */ +#define pgoff_t __int64 +#ifdef WIN32_ONLY_COMPILER +#define fseeko(stream, offset, origin) _fseeki64(stream, offset, origin) +#define ftello(stream) _ftelli64(stream) +#else +#define fseeko(stream, offset, origin) fseeko64(stream, offset, origin) +#define ftello(stream) ftello64(stream) +#endif + +/* + * Supplement to . + * + * Perl already has typedefs for uid_t and gid_t. + */ +#ifndef PLPERL_HAVE_UID_GID +typedef int uid_t; +typedef int gid_t; +#endif +typedef long key_t; + +#ifdef WIN32_ONLY_COMPILER +typedef int pid_t; +#endif + +/* + * Supplement to . + */ +#define lstat(path, sb) stat((path), (sb)) + +/* + * Supplement to . + * This is the same value as _O_NOINHERIT in the MS header file. This is + * to ensure that we don't collide with a future definition. It means + * we cannot use _O_NOINHERIT ourselves. + */ +#define O_DSYNC 0x0080 + +/* + * Supplement to . + */ +#undef EAGAIN +#undef EINTR +#define EINTR WSAEINTR +#define EAGAIN WSAEWOULDBLOCK +#define EMSGSIZE WSAEMSGSIZE +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNRESET WSAECONNRESET +#define EINPROGRESS WSAEINPROGRESS +#define ENOBUFS WSAENOBUFS +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ECONNREFUSED WSAECONNREFUSED +#define EBADFD WSAENOTSOCK +#define EOPNOTSUPP WSAEOPNOTSUPP + +/* + * Extended locale functions with gratuitous underscore prefixes. + * (These APIs are nevertheless fully documented by Microsoft.) + */ +#define locale_t _locale_t +#define tolower_l _tolower_l +#define toupper_l _toupper_l +#define towlower_l _towlower_l +#define towupper_l _towupper_l +#define isdigit_l _isdigit_l +#define iswdigit_l _iswdigit_l +#define isalpha_l _isalpha_l +#define iswalpha_l _iswalpha_l +#define isalnum_l _isalnum_l +#define iswalnum_l _iswalnum_l +#define isupper_l _isupper_l +#define iswupper_l _iswupper_l +#define islower_l _islower_l +#define iswlower_l _iswlower_l +#define isgraph_l _isgraph_l +#define iswgraph_l _iswgraph_l +#define isprint_l _isprint_l +#define iswprint_l _iswprint_l +#define ispunct_l _ispunct_l +#define iswpunct_l _iswpunct_l +#define isspace_l _isspace_l +#define iswspace_l _iswspace_l +#define strcoll_l _strcoll_l +#define wcscoll_l _wcscoll_l +#define wcstombs_l _wcstombs_l +#define mbstowcs_l _mbstowcs_l + + +/* In backend/port/win32/signal.c */ +extern PGDLLIMPORT volatile int pg_signal_queue; +extern PGDLLIMPORT int pg_signal_mask; +extern HANDLE pgwin32_signal_event; +extern HANDLE pgwin32_initial_signal_pipe; + +#define UNBLOCKED_SIGNAL_QUEUE() (pg_signal_queue & ~pg_signal_mask) + + +void pgwin32_signal_initialize(void); +HANDLE pgwin32_create_signal_listener(pid_t pid); +void pgwin32_dispatch_queued_signals(void); +void pg_queue_signal(int signum); + +/* In backend/port/win32/socket.c */ +#ifndef FRONTEND +#define socket(af, type, protocol) pgwin32_socket(af, type, protocol) +#define accept(s, addr, addrlen) pgwin32_accept(s, addr, addrlen) +#define connect(s, name, namelen) pgwin32_connect(s, name, namelen) +#define select(n, r, w, e, timeout) pgwin32_select(n, r, w, e, timeout) +#define recv(s, buf, len, flags) pgwin32_recv(s, buf, len, flags) +#define send(s, buf, len, flags) pgwin32_send(s, buf, len, flags) + +SOCKET pgwin32_socket(int af, int type, int protocol); +SOCKET pgwin32_accept(SOCKET s, struct sockaddr * addr, int *addrlen); +int pgwin32_connect(SOCKET s, const struct sockaddr * name, int namelen); +int pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout); +int pgwin32_recv(SOCKET s, char *buf, int len, int flags); +int pgwin32_send(SOCKET s, const void *buf, int len, int flags); + +const char *pgwin32_socket_strerror(int err); +int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout); + +extern int pgwin32_noblock; + +/* in backend/port/win32/security.c */ +extern int pgwin32_is_admin(void); +extern int pgwin32_is_service(void); +#endif + +/* in backend/port/win32_shmem.c */ +extern int pgwin32_ReserveSharedMemoryRegion(HANDLE); + +/* in backend/port/win32/crashdump.c */ +extern void pgwin32_install_crashdump_handler(void); + +/* in port/win32error.c */ +extern void _dosmaperr(unsigned long); + +/* in port/win32env.c */ +extern int pgwin32_putenv(const char *); +extern void pgwin32_unsetenv(const char *); + +#define putenv(x) pgwin32_putenv(x) +#define unsetenv(x) pgwin32_unsetenv(x) + +/* Things that exist in MingW headers, but need to be added to MSVC & BCC */ +#ifdef WIN32_ONLY_COMPILER + +#ifndef _WIN64 +typedef long ssize_t; +#else +typedef __int64 ssize_t; +#endif + +#ifndef __BORLANDC__ +typedef unsigned short mode_t; + +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define S_IXUSR _S_IEXEC +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +/* see also S_IRGRP etc below */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif /* __BORLANDC__ */ + +#define F_OK 0 +#define W_OK 2 +#define R_OK 4 + +#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) || (_fpclass(x) == _FPCLASS_NINF)) +#define isnan(x) _isnan(x) + +/* Pulled from Makefile.port in mingw */ +#define DLSUFFIX ".dll" + +#ifdef __BORLANDC__ + +/* for port/dirent.c */ +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD) -1) +#endif + +/* for port/open.c */ +#ifndef O_RANDOM +#define O_RANDOM 0x0010 /* File access is primarily random */ +#define O_SEQUENTIAL 0x0020 /* File access is primarily sequential */ +#define O_TEMPORARY 0x0040 /* Temporary file bit */ +#define O_SHORT_LIVED 0x1000 /* Temporary storage file, try not to flush */ +#define _O_SHORT_LIVED O_SHORT_LIVED +#endif /* ifndef O_RANDOM */ +#endif /* __BORLANDC__ */ +#endif /* WIN32_ONLY_COMPILER */ + +/* These aren't provided by either MingW or MSVC */ +#ifndef __BORLANDC__ +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IXGRP 0 +#define S_IRWXG 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#define S_IXOTH 0 +#define S_IRWXO 0 + +#endif /* __BORLANDC__ */ diff --git a/src/libpq/pg_config_paths.h.darwin b/src/libpq/pg_config_paths.h.darwin new file mode 100644 index 00000000..8d16c70b --- /dev/null +++ b/src/libpq/pg_config_paths.h.darwin @@ -0,0 +1,12 @@ +#define PGBINDIR "/usr/local/pgsql/bin" +#define PGSHAREDIR "/usr/local/pgsql/share" +#define SYSCONFDIR "/usr/local/pgsql/etc" +#define INCLUDEDIR "/usr/local/pgsql/include" +#define PKGINCLUDEDIR "/usr/local/pgsql/include" +#define INCLUDEDIRSERVER "/usr/local/pgsql/include/server" +#define LIBDIR "/usr/local/pgsql/lib" +#define PKGLIBDIR "/usr/local/pgsql/lib" +#define LOCALEDIR "/usr/local/pgsql/share/locale" +#define DOCDIR "/usr/local/pgsql/share/doc/" +#define HTMLDIR "/usr/local/pgsql/share/doc/" +#define MANDIR "/usr/local/pgsql/share/man" diff --git a/src/libpq/pg_config_paths.h.win b/src/libpq/pg_config_paths.h.win new file mode 100644 index 00000000..8d16c70b --- /dev/null +++ b/src/libpq/pg_config_paths.h.win @@ -0,0 +1,12 @@ +#define PGBINDIR "/usr/local/pgsql/bin" +#define PGSHAREDIR "/usr/local/pgsql/share" +#define SYSCONFDIR "/usr/local/pgsql/etc" +#define INCLUDEDIR "/usr/local/pgsql/include" +#define PKGINCLUDEDIR "/usr/local/pgsql/include" +#define INCLUDEDIRSERVER "/usr/local/pgsql/include/server" +#define LIBDIR "/usr/local/pgsql/lib" +#define PKGLIBDIR "/usr/local/pgsql/lib" +#define LOCALEDIR "/usr/local/pgsql/share/locale" +#define DOCDIR "/usr/local/pgsql/share/doc/" +#define HTMLDIR "/usr/local/pgsql/share/doc/" +#define MANDIR "/usr/local/pgsql/share/man" diff --git a/src/libpq/pg_service.conf.sample b/src/libpq/pg_service.conf.sample new file mode 100644 index 00000000..5a1c0835 --- /dev/null +++ b/src/libpq/pg_service.conf.sample @@ -0,0 +1,17 @@ +# +# Connection configuration file +# +# A service is a set of named connection parameters. You may specify +# multiple services in this file. Each starts with a service name in +# brackets. Subsequent lines have connection configuration parameters of +# the pattern "param=value" or LDAP URLs starting with "ldap://" +# to look up such parameters. A sample configuration for postgres is +# included in this file. Lines beginning with '#' are comments. +# +# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and +# rename it pg_service.conf. +# +# +#[postgres] +#dbname=postgres +#user=postgres diff --git a/src/libpq/pgsleep.c b/src/libpq/pgsleep.c new file mode 100644 index 00000000..988bfb05 --- /dev/null +++ b/src/libpq/pgsleep.c @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + * + * pgsleep.c + * Portable delay handling. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * src/port/pgsleep.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include +#include + +/* + * In a Windows backend, we don't use this implementation, but rather + * the signal-aware version in src/backend/port/win32/signal.c. + */ +#if defined(FRONTEND) || !defined(WIN32) + +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: although the delay is specified in microseconds, the effective + * resolution is only 1/HZ, or 10 milliseconds, on most Unixen. Expect + * the requested delay to be rounded up to the next resolution boundary. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timeval delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_usec = microsec % 1000000L; + (void) select(0, NULL, NULL, NULL, &delay); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +#endif /* defined(FRONTEND) || !defined(WIN32) */ diff --git a/src/libpq/pgstrcasecmp.c b/src/libpq/pgstrcasecmp.c new file mode 100644 index 00000000..f6e226f0 --- /dev/null +++ b/src/libpq/pgstrcasecmp.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * + * pgstrcasecmp.c + * Portable SQL-like case-independent comparisons and conversions. + * + * SQL99 specifies Unicode-aware case normalization, which we don't yet + * have the infrastructure for. Instead we use tolower() to provide a + * locale-aware translation. However, there are some locales where this + * is not right either (eg, Turkish may do strange things with 'i' and + * 'I'). Our current compromise is to use tolower() for characters with + * the high bit set, and use an ASCII-only downcasing for 7-bit + * characters. + * + * NB: this code should match downcase_truncate_identifier() in scansup.c. + * + * We also provide strict ASCII-only case conversion functions, which can + * be used to implement C/POSIX case folding semantics no matter what the + * C library thinks the locale is. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * src/port/pgstrcasecmp.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include + + +/* + * Case-independent comparison of two null-terminated strings. + */ +int +pg_strcasecmp(const char *s1, const char *s2) +{ + for (;;) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Case-independent comparison of two not-necessarily-null-terminated strings. + * At most n bytes will be examined from each string. + */ +int +pg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + while (n-- > 0) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Fold a character to upper case. + * + * Unlike some versions of toupper(), this is safe to apply to characters + * that aren't lower case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + else if (IS_HIGHBIT_SET(ch) && islower(ch)) + ch = toupper(ch); + return ch; +} + +/* + * Fold a character to lower case. + * + * Unlike some versions of tolower(), this is safe to apply to characters + * that aren't upper case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch) && isupper(ch)) + ch = tolower(ch); + return ch; +} + +/* + * Fold a character to upper case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + return ch; +} + +/* + * Fold a character to lower case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + return ch; +} diff --git a/src/libpq/port.h b/src/libpq/port.h new file mode 100644 index 00000000..088e02e7 --- /dev/null +++ b/src/libpq/port.h @@ -0,0 +1,489 @@ +/*------------------------------------------------------------------------- + * + * port.h + * Header for src/port/ compatibility functions. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/port.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_PORT_H +#define PG_PORT_H + +#include +#include +#include + +/* socket has a different definition on WIN32 */ +#ifndef WIN32 +typedef int pgsocket; + +#define PGINVALID_SOCKET (-1) +#else +typedef SOCKET pgsocket; + +#define PGINVALID_SOCKET INVALID_SOCKET +#endif + +/* non-blocking */ +extern bool pg_set_noblock(pgsocket sock); +extern bool pg_set_block(pgsocket sock); + +/* Portable path handling for Unix/Win32 (in path.c) */ + +extern char *first_dir_separator(const char *filename); +extern char *last_dir_separator(const char *filename); +extern char *first_path_var_separator(const char *pathlist); +extern void join_path_components(char *ret_path, + const char *head, const char *tail); +extern void canonicalize_path(char *path); +extern void make_native_path(char *path); +extern bool path_contains_parent_reference(const char *path); +extern bool path_is_relative_and_below_cwd(const char *path); +extern bool path_is_prefix_of_path(const char *path1, const char *path2); +extern const char *get_progname(const char *argv0); +extern void get_share_path(const char *my_exec_path, char *ret_path); +extern void get_etc_path(const char *my_exec_path, char *ret_path); +extern void get_include_path(const char *my_exec_path, char *ret_path); +extern void get_pkginclude_path(const char *my_exec_path, char *ret_path); +extern void get_includeserver_path(const char *my_exec_path, char *ret_path); +extern void get_lib_path(const char *my_exec_path, char *ret_path); +extern void get_pkglib_path(const char *my_exec_path, char *ret_path); +extern void get_locale_path(const char *my_exec_path, char *ret_path); +extern void get_doc_path(const char *my_exec_path, char *ret_path); +extern void get_html_path(const char *my_exec_path, char *ret_path); +extern void get_man_path(const char *my_exec_path, char *ret_path); +extern bool get_home_path(char *ret_path); +extern void get_parent_directory(char *path); + +/* port/dirmod.c */ +extern char **pgfnames(const char *path); +extern void pgfnames_cleanup(char **filenames); + +/* + * is_absolute_path + * + * By making this a macro we avoid needing to include path.c in libpq. + */ +#ifndef WIN32 +#define IS_DIR_SEP(ch) ((ch) == '/') + +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) \ +) +#else +#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\') + +/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */ +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) || \ + (isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \ + IS_DIR_SEP((filename)[2])) \ +) +#endif + +/* Portable locale initialization (in exec.c) */ +extern void set_pglocale_pgservice(const char *argv0, const char *app); + +/* Portable way to find binaries (in exec.c) */ +extern int find_my_exec(const char *argv0, char *retpath); +extern int find_other_exec(const char *argv0, const char *target, + const char *versionstr, char *retpath); + +/* Windows security token manipulation (in exec.c) */ +#ifdef WIN32 +extern BOOL AddUserToTokenDacl(HANDLE hToken); +#endif + + +#if defined(WIN32) || defined(__CYGWIN__) +#define EXE ".exe" +#else +#define EXE "" +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +#define DEVNULL "nul" +/* "con" does not work from the Msys 1.0.10 console (part of MinGW). */ +#define DEVTTY "con" +#else +#define DEVNULL "/dev/null" +#define DEVTTY "/dev/tty" +#endif + +/* + * Win32 needs double quotes at the beginning and end of system() + * strings. If not, it gets confused with multiple quoted strings. + * It also requires double-quotes around the executable name and + * any files used for redirection. Other args can use single-quotes. + * + * Generated using Win32 "CMD /?": + * + * 1. If all of the following conditions are met, then quote characters + * on the command line are preserved: + * + * - no /S switch + * - exactly two quote characters + * - no special characters between the two quote characters, where special + * is one of: &<>()@^| + * - there are one or more whitespace characters between the two quote + * characters + * - the string between the two quote characters is the name of an + * executable file. + * + * 2. Otherwise, old behavior is to see if the first character is a quote + * character and if so, strip the leading character and remove the last + * quote character on the command line, preserving any text after the last + * quote character. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define SYSTEMQUOTE "\"" +#else +#define SYSTEMQUOTE "" +#endif + +/* Portable delay handling */ +extern void pg_usleep(long microsec); + +/* Portable SQL-like case-independent comparisons and conversions */ +extern int pg_strcasecmp(const char *s1, const char *s2); +extern int pg_strncasecmp(const char *s1, const char *s2, size_t n); +extern unsigned char pg_toupper(unsigned char ch); +extern unsigned char pg_tolower(unsigned char ch); +extern unsigned char pg_ascii_toupper(unsigned char ch); +extern unsigned char pg_ascii_tolower(unsigned char ch); + +#ifdef USE_REPL_SNPRINTF + +/* + * Versions of libintl >= 0.13 try to replace printf() and friends with + * macros to their own versions that understand the %$ format. We do the + * same, so disable their macros, if they exist. + */ +#ifdef vsnprintf +#undef vsnprintf +#endif +#ifdef snprintf +#undef snprintf +#endif +#ifdef sprintf +#undef sprintf +#endif +#ifdef vfprintf +#undef vfprintf +#endif +#ifdef fprintf +#undef fprintf +#endif +#ifdef printf +#undef printf +#endif + +extern int pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args); +extern int +pg_snprintf(char *str, size_t count, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4))); +extern int +pg_sprintf(char *str, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); +extern int pg_vfprintf(FILE *stream, const char *fmt, va_list args); +extern int +pg_fprintf(FILE *stream, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); +extern int +pg_printf(const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2))); + +/* + * The GCC-specific code below prevents the __attribute__(... 'printf') + * above from being replaced, and this is required because gcc doesn't + * know anything about pg_printf. + */ +#ifdef __GNUC__ +#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__) +#define snprintf(...) pg_snprintf(__VA_ARGS__) +#define sprintf(...) pg_sprintf(__VA_ARGS__) +#define vfprintf(...) pg_vfprintf(__VA_ARGS__) +#define fprintf(...) pg_fprintf(__VA_ARGS__) +#define printf(...) pg_printf(__VA_ARGS__) +#else +#define vsnprintf pg_vsnprintf +#define snprintf pg_snprintf +#define sprintf pg_sprintf +#define vfprintf pg_vfprintf +#define fprintf pg_fprintf +#define printf pg_printf +#endif +#endif /* USE_REPL_SNPRINTF */ + +#if defined(WIN32) +/* + * Versions of libintl >= 0.18? try to replace setlocale() with a macro + * to their own versions. Remove the macro, if it exists, because it + * ends up calling the wrong version when the backend and libintl use + * different versions of msvcrt. + */ +#if defined(setlocale) +#undef setlocale +#endif + +/* + * Define our own wrapper macro around setlocale() to work around bugs in + * Windows' native setlocale() function. + */ +extern char *pgwin32_setlocale(int category, const char *locale); + +#define setlocale(a,b) pgwin32_setlocale(a,b) + +#endif /* WIN32 */ + +/* Portable prompt handling */ +extern char *simple_prompt(const char *prompt, int maxlen, bool echo); + +/* + * WIN32 doesn't allow descriptors returned by pipe() to be used in select(), + * so for that platform we use socket() instead of pipe(). + * There is some inconsistency here because sometimes we require pg*, like + * pgpipe, but in other cases we define rename to pgrename just on Win32. + */ +#ifndef WIN32 +/* + * The function prototypes are not supplied because every C file + * includes this file. + */ +#define pgpipe(a) pipe(a) +#define piperead(a,b,c) read(a,b,c) +#define pipewrite(a,b,c) write(a,b,c) +#else +extern int pgpipe(int handles[2]); +extern int piperead(int s, char *buf, int len); + +#define pipewrite(a,b,c) send(a,b,c,0) + +#define PG_SIGNAL_COUNT 32 +#define kill(pid,sig) pgkill(pid,sig) +extern int pgkill(int pid, int sig); +#endif + +extern int pclose_check(FILE *stream); + +/* Global variable holding time zone information. */ +#ifndef __CYGWIN__ +#define TIMEZONE_GLOBAL timezone +#define TZNAME_GLOBAL tzname +#else +#define TIMEZONE_GLOBAL _timezone +#define TZNAME_GLOBAL _tzname +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* + * Win32 doesn't have reliable rename/unlink during concurrent access. + */ +extern int pgrename(const char *from, const char *to); +extern int pgunlink(const char *path); + +/* Include this first so later includes don't see these defines */ +#ifdef WIN32_ONLY_COMPILER +#include +#endif + +#define rename(from, to) pgrename(from, to) +#define unlink(path) pgunlink(path) +#endif /* defined(WIN32) || defined(__CYGWIN__) */ + +/* + * Win32 also doesn't have symlinks, but we can emulate them with + * junction points on newer Win32 versions. + * + * Cygwin has its own symlinks which work on Win95/98/ME where + * junction points don't, so use those instead. We have no way of + * knowing what type of system Cygwin binaries will be run on. + * Note: Some CYGWIN includes might #define WIN32. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +extern int pgsymlink(const char *oldpath, const char *newpath); +extern int pgreadlink(const char *path, char *buf, size_t size); +extern bool pgwin32_is_junction(char *path); + +#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath) +#define readlink(path, buf, size) pgreadlink(path, buf, size) +#endif + +extern bool rmtree(const char *path, bool rmtopdir); + +/* + * stat() is not guaranteed to set the st_size field on win32, so we + * redefine it to our own implementation that is. + * + * We must pull in sys/stat.h here so the system header definition + * goes in first, and we redefine that, and not the other way around. + * + * Some frontends don't need the size from stat, so if UNSAFE_STAT_OK + * is defined we don't bother with this. + */ +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(UNSAFE_STAT_OK) +#include +extern int pgwin32_safestat(const char *path, struct stat * buf); + +#define stat(a,b) pgwin32_safestat(a,b) +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) + +/* + * open() and fopen() replacements to allow deletion of open files and + * passing of other special options. + */ +#define O_DIRECT 0x80000000 +extern int pgwin32_open(const char *, int,...); +extern FILE *pgwin32_fopen(const char *, const char *); + +#ifndef FRONTEND +#define open(a,b,c) pgwin32_open(a,b,c) +#define fopen(a,b) pgwin32_fopen(a,b) +#endif + +#ifndef popen +#define popen(a,b) _popen(a,b) +#endif +#ifndef pclose +#define pclose(a) _pclose(a) +#endif + +/* New versions of MingW have gettimeofday, old mingw and msvc don't */ +#ifndef HAVE_GETTIMEOFDAY +/* Last parameter not used */ +extern int gettimeofday(struct timeval * tp, struct timezone * tzp); +#endif +#else /* !WIN32 */ + +/* + * Win32 requires a special close for sockets and pipes, while on Unix + * close() does them all. + */ +#define closesocket close +#endif /* WIN32 */ + +/* + * Default "extern" declarations or macro substitutes for library routines. + * When necessary, these routines are provided by files in src/port/. + */ +#ifndef HAVE_CRYPT +extern char *crypt(const char *key, const char *setting); +#endif + +/* WIN32 handled in port/win32.h */ +#ifndef WIN32 +#define pgoff_t off_t +#if defined(__bsdi__) || defined(__NetBSD__) +extern int fseeko(FILE *stream, off_t offset, int whence); +extern off_t ftello(FILE *stream); +#endif +#endif + +#ifndef HAVE_ERAND48 +/* we assume all of these are present or missing together */ +extern double erand48(unsigned short xseed[3]); +extern long lrand48(void); +extern void srand48(long seed); +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(a, b, c) fseek(a, b, c) +#define ftello(a) ftell(a) +#endif + +#ifndef HAVE_GETOPT +extern int getopt(int nargc, char *const * nargv, const char *ostr); +#endif + +#if !defined(HAVE_GETPEEREID) && !defined(WIN32) +extern int getpeereid(int sock, uid_t *uid, gid_t *gid); +#endif + +#ifndef HAVE_ISINF +extern int isinf(double x); +#endif + +#ifndef HAVE_RINT +extern double rint(double x); +#endif + +#ifndef HAVE_INET_ATON +#include +#include +extern int inet_aton(const char *cp, struct in_addr * addr); +#endif + +#ifndef HAVE_STRDUP +extern char *strdup(const char *str); +#endif + +#if !HAVE_DECL_STRLCAT +extern size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#if !HAVE_DECL_STRLCPY +extern size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#if !defined(HAVE_RANDOM) && !defined(__BORLANDC__) +extern long random(void); +#endif + +#ifndef HAVE_UNSETENV +extern void unsetenv(const char *name); +#endif + +#ifndef HAVE_SRANDOM +extern void srandom(unsigned int seed); +#endif + +/* thread.h */ +extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen); + +#if !defined(WIN32) || defined(__CYGWIN__) +extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result); +#endif + +extern int pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno); + +extern void pg_qsort(void *base, size_t nel, size_t elsize, + int (*cmp) (const void *, const void *)); + +#define qsort(a,b,c,d) pg_qsort(a,b,c,d) + +typedef int (*qsort_arg_comparator) (const void *a, const void *b, void *arg); + +extern void qsort_arg(void *base, size_t nel, size_t elsize, + qsort_arg_comparator cmp, void *arg); + +/* port/chklocale.c */ +extern int pg_get_encoding_from_locale(const char *ctype, bool write_message); + +/* port/inet_net_ntop.c */ +extern char *inet_net_ntop(int af, const void *src, int bits, + char *dst, size_t size); + +/* port/pgcheckdir.c */ +extern int pg_check_dir(const char *dir); + +/* port/pgmkdirp.c */ +extern int pg_mkdir_p(char *path, int omode); + +#endif /* PG_PORT_H */ diff --git a/src/libpq/postgres_ext.h b/src/libpq/postgres_ext.h new file mode 100644 index 00000000..b6ebb7aa --- /dev/null +++ b/src/libpq/postgres_ext.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------- + * + * postgres_ext.h + * + * This file contains declarations of things that are visible everywhere + * in PostgreSQL *and* are visible to clients of frontend interface libraries. + * For example, the Oid type is part of the API of libpq and other libraries. + * + * Declarations which are specific to a particular interface should + * go in the header file for that interface (such as libpq-fe.h). This + * file is only for fundamental Postgres declarations. + * + * User-written C functions don't count as "external to Postgres." + * Those function much as local modifications to the backend itself, and + * use header files that are otherwise internal to Postgres to interface + * with the backend. + * + * src/include/postgres_ext.h + * + *------------------------------------------------------------------------- + */ + +#ifndef POSTGRES_EXT_H +#define POSTGRES_EXT_H + +/* + * Object ID is a fundamental type in Postgres. + */ +typedef unsigned int Oid; + +#ifdef __cplusplus +#define InvalidOid (Oid(0)) +#else +#define InvalidOid ((Oid) 0) +#endif + +#define OID_MAX UINT_MAX +/* you will need to include to use the above #define */ + + +/* + * Identifiers of error message fields. Kept here to keep common + * between frontend and backend, and also to export them to libpq + * applications. + */ +#define PG_DIAG_SEVERITY 'S' +#define PG_DIAG_SQLSTATE 'C' +#define PG_DIAG_MESSAGE_PRIMARY 'M' +#define PG_DIAG_MESSAGE_DETAIL 'D' +#define PG_DIAG_MESSAGE_HINT 'H' +#define PG_DIAG_STATEMENT_POSITION 'P' +#define PG_DIAG_INTERNAL_POSITION 'p' +#define PG_DIAG_INTERNAL_QUERY 'q' +#define PG_DIAG_CONTEXT 'W' +#define PG_DIAG_SOURCE_FILE 'F' +#define PG_DIAG_SOURCE_LINE 'L' +#define PG_DIAG_SOURCE_FUNCTION 'R' + +#endif diff --git a/src/libpq/postgres_fe.h b/src/libpq/postgres_fe.h new file mode 100644 index 00000000..232a1907 --- /dev/null +++ b/src/libpq/postgres_fe.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * postgres_fe.h + * Primary include file for PostgreSQL client-side .c files + * + * This should be the first file included by PostgreSQL client libraries and + * application programs --- but not by backend modules, which should include + * postgres.h. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1995, Regents of the University of California + * + * src/include/postgres_fe.h + * + *------------------------------------------------------------------------- + */ +#ifndef POSTGRES_FE_H +#define POSTGRES_FE_H + +#ifndef FRONTEND +#define FRONTEND 1 +#endif + +#include "c.h" + +#endif /* POSTGRES_FE_H */ diff --git a/src/libpq/pqexpbuffer.c b/src/libpq/pqexpbuffer.c new file mode 100644 index 00000000..1abfed79 --- /dev/null +++ b/src/libpq/pqexpbuffer.c @@ -0,0 +1,377 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.c + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(). + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include + +#include "pqexpbuffer.h" + +#ifdef WIN32 +#include "win32.h" +#endif + + +/* All "broken" PQExpBuffers point to this string. */ +static const char oom_buffer[1] = ""; + + +/* + * markPQExpBufferBroken + * + * Put a PQExpBuffer in "broken" state if it isn't already. + */ +static void +markPQExpBufferBroken(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + + /* + * Casting away const here is a bit ugly, but it seems preferable to not + * marking oom_buffer const. We want to do that to encourage the compiler + * to put oom_buffer in read-only storage, so that anyone who tries to + * scribble on a broken PQExpBuffer will get a failure. + */ + str->data = (char *) oom_buffer; + str->len = 0; + str->maxlen = 0; +} + +/* + * createPQExpBuffer + * + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +PQExpBuffer +createPQExpBuffer(void) +{ + PQExpBuffer res; + + res = (PQExpBuffer) malloc(sizeof(PQExpBufferData)); + if (res != NULL) + initPQExpBuffer(res); + + return res; +} + +/* + * initPQExpBuffer + * + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +void +initPQExpBuffer(PQExpBuffer str) +{ + str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE); + if (str->data == NULL) + { + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; + } + else + { + str->maxlen = INITIAL_EXPBUFFER_SIZE; + str->len = 0; + str->data[0] = '\0'; + } +} + +/* + * destroyPQExpBuffer(str); + * + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + */ +void +destroyPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + termPQExpBuffer(str); + free(str); + } +} + +/* + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + */ +void +termPQExpBuffer(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + /* just for luck, make the buffer validly empty. */ + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; +} + +/* + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +void +resetPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + if (str->data != oom_buffer) + { + str->len = 0; + str->data[0] = '\0'; + } + else + { + /* try to reinitialize to valid state */ + initPQExpBuffer(str); + } + } +} + +/* + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +int +enlargePQExpBuffer(PQExpBuffer str, size_t needed) +{ + size_t newlen; + char *newdata; + + if (PQExpBufferBroken(str)) + return 0; /* already failed */ + + /* + * Guard against ridiculous "needed" values, which can occur if we're fed + * bogus data. Without this, we can get an overflow or infinite loop in + * the following. + */ + if (needed >= ((size_t) INT_MAX - str->len)) + { + markPQExpBufferBroken(str); + return 0; + } + + needed += str->len + 1; /* total space required now */ + + /* Because of the above test, we now have needed <= INT_MAX */ + + if (needed <= str->maxlen) + return 1; /* got enough space already */ + + /* + * We don't want to allocate just a little more space with each append; + * for efficiency, double the buffer size each time it overflows. + * Actually, we might need to more than double it if 'needed' is big... + */ + newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64; + while (needed > newlen) + newlen = 2 * newlen; + + /* + * Clamp to INT_MAX in case we went past it. Note we are assuming here + * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We + * will still have newlen >= needed. + */ + if (newlen > (size_t) INT_MAX) + newlen = (size_t) INT_MAX; + + newdata = (char *) realloc(str->data, newlen); + if (newdata != NULL) + { + str->data = newdata; + str->maxlen = newlen; + return 1; + } + + markPQExpBufferBroken(str); + return 0; +} + +/* + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +void +printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + size_t avail; + int nprinted; + + resetPQExpBuffer(str); + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + for (;;) + { + /* + * Try to format the given string into the available space; but if + * there's hardly any space, don't bother trying, just fall through to + * enlarge the buffer first. + */ + if (str->maxlen > str->len + 16) + { + avail = str->maxlen - str->len - 1; + va_start(args, fmt); + nprinted = vsnprintf(str->data + str->len, avail, + fmt, args); + va_end(args); + + /* + * Note: some versions of vsnprintf return the number of chars + * actually stored, but at least one returns -1 on failure. Be + * conservative about believing whether the print worked. + */ + if (nprinted >= 0 && nprinted < (int) avail - 1) + { + /* Success. Note nprinted does not include trailing null. */ + str->len += nprinted; + break; + } + } + /* Double the buffer size and try again. */ + if (!enlargePQExpBuffer(str, str->maxlen)) + return; /* oops, out of memory */ + } +} + +/* + * appendPQExpBuffer + * + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +void +appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + size_t avail; + int nprinted; + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + for (;;) + { + /* + * Try to format the given string into the available space; but if + * there's hardly any space, don't bother trying, just fall through to + * enlarge the buffer first. + */ + if (str->maxlen > str->len + 16) + { + avail = str->maxlen - str->len - 1; + va_start(args, fmt); + nprinted = vsnprintf(str->data + str->len, avail, + fmt, args); + va_end(args); + + /* + * Note: some versions of vsnprintf return the number of chars + * actually stored, but at least one returns -1 on failure. Be + * conservative about believing whether the print worked. + */ + if (nprinted >= 0 && nprinted < (int) avail - 1) + { + /* Success. Note nprinted does not include trailing null. */ + str->len += nprinted; + break; + } + } + /* Double the buffer size and try again. */ + if (!enlargePQExpBuffer(str, str->maxlen)) + return; /* oops, out of memory */ + } +} + +/* + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendPQExpBufferStr(PQExpBuffer str, const char *data) +{ + appendBinaryPQExpBuffer(str, data, strlen(data)); +} + +/* + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +void +appendPQExpBufferChar(PQExpBuffer str, char ch) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, 1)) + return; + + /* OK, append the character */ + str->data[str->len] = ch; + str->len++; + str->data[str->len] = '\0'; +} + +/* + * appendBinaryPQExpBuffer + * + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, datalen)) + return; + + /* OK, append the data */ + memcpy(str->data + str->len, data, datalen); + str->len += datalen; + + /* + * Keep a trailing null in place, even though it's probably useless for + * binary data... + */ + str->data[str->len] = '\0'; +} diff --git a/src/libpq/pqexpbuffer.h b/src/libpq/pqexpbuffer.h new file mode 100644 index 00000000..d60fedc7 --- /dev/null +++ b/src/libpq/pqexpbuffer.h @@ -0,0 +1,180 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.h + * Declarations/definitions for "PQExpBuffer" functions. + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(). + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQEXPBUFFER_H +#define PQEXPBUFFER_H + +/*------------------------- + * PQExpBufferData holds information about an extensible string. + * data is the current buffer for the string (allocated with malloc). + * len is the current string length. There is guaranteed to be + * a terminating '\0' at data[len], although this is not very + * useful when the string holds binary data rather than text. + * maxlen is the allocated size in bytes of 'data', i.e. the maximum + * string size (including the terminating '\0' char) that we can + * currently store in 'data' without having to reallocate + * more space. We must always have maxlen > len. + * + * An exception occurs if we failed to allocate enough memory for the string + * buffer. In that case data points to a statically allocated empty string, + * and len = maxlen = 0. + *------------------------- + */ +typedef struct PQExpBufferData +{ + char *data; + size_t len; + size_t maxlen; +} PQExpBufferData; + +typedef PQExpBufferData *PQExpBuffer; + +/*------------------------ + * Test for a broken (out of memory) PQExpBuffer. + * When a buffer is "broken", all operations except resetting or deleting it + * are no-ops. + *------------------------ + */ +#define PQExpBufferBroken(str) \ + ((str) == NULL || (str)->maxlen == 0) + +/*------------------------ + * Initial size of the data buffer in a PQExpBuffer. + * NB: this must be large enough to hold error messages that might + * be returned by PQrequestCancel(). + *------------------------ + */ +#define INITIAL_EXPBUFFER_SIZE 256 + +/*------------------------ + * There are two ways to create a PQExpBuffer object initially: + * + * PQExpBuffer stringptr = createPQExpBuffer(); + * Both the PQExpBufferData and the data buffer are malloc'd. + * + * PQExpBufferData string; + * initPQExpBuffer(&string); + * The data buffer is malloc'd but the PQExpBufferData is presupplied. + * This is appropriate if the PQExpBufferData is a field of another + * struct. + *------------------------- + */ + +/*------------------------ + * createPQExpBuffer + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +extern PQExpBuffer createPQExpBuffer(void); + +/*------------------------ + * initPQExpBuffer + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +extern void initPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * To destroy a PQExpBuffer, use either: + * + * destroyPQExpBuffer(str); + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + * + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + * + * NOTE: some routines build up a string using PQExpBuffer, and then + * release the PQExpBufferData but return the data string itself to their + * caller. At that point the data string looks like a plain malloc'd + * string. + */ +extern void destroyPQExpBuffer(PQExpBuffer str); +extern void termPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +extern void resetPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed); + +/*------------------------ + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +extern void +printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); + +/*------------------------ + * appendPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +extern void +appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) +/* This extension allows gcc to check the format string */ +__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); + +/*------------------------ + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendPQExpBufferStr(PQExpBuffer str, const char *data); + +/*------------------------ + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +extern void appendPQExpBufferChar(PQExpBuffer str, char ch); + +/*------------------------ + * appendBinaryPQExpBuffer + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendBinaryPQExpBuffer(PQExpBuffer str, + const char *data, size_t datalen); + +#endif /* PQEXPBUFFER_H */ diff --git a/src/libpq/pqsignal.c b/src/libpq/pqsignal.c new file mode 100644 index 00000000..04cee8d3 --- /dev/null +++ b/src/libpq/pqsignal.c @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.c + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/pqsignal.c + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include + +#include "pqsignal.h" + + +pqsigfunc +pqsignal(int signo, pqsigfunc func) +{ +#if !defined(HAVE_POSIX_SIGNALS) + return signal(signo, func); +#else + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (signo != SIGALRM) + act.sa_flags |= SA_RESTART; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +#endif /* !HAVE_POSIX_SIGNALS */ +} diff --git a/src/libpq/pqsignal.h b/src/libpq/pqsignal.h new file mode 100644 index 00000000..02966fd4 --- /dev/null +++ b/src/libpq/pqsignal.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.h + * prototypes for the reliable BSD-style signal(2) routine. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqsignal.h + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#ifndef PQSIGNAL_H +#define PQSIGNAL_H + +typedef void (*pqsigfunc) (int); + +extern pqsigfunc pqsignal(int signo, pqsigfunc func); + +#endif /* PQSIGNAL_H */ diff --git a/src/libpq/pthread-win32.c b/src/libpq/pthread-win32.c new file mode 100644 index 00000000..e24a42dc --- /dev/null +++ b/src/libpq/pthread-win32.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- +* +* pthread-win32.c +* partial pthread implementation for win32 +* +* Copyright (c) 2004-2011, PostgreSQL Global Development Group +* IDENTIFICATION +* src/interfaces/libpq/pthread-win32.c +* +*------------------------------------------------------------------------- +*/ + +#include "postgres_fe.h" + +#include +#include "pthread-win32.h" + +DWORD +pthread_self(void) +{ + return GetCurrentThreadId(); +} + +void +pthread_setspecific(pthread_key_t key, void *val) +{ +} + +void * +pthread_getspecific(pthread_key_t key) +{ + return NULL; +} + +int +pthread_mutex_init(pthread_mutex_t *mp, void *attr) +{ + *mp = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); + if (!*mp) + return 1; + InitializeCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_lock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + EnterCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_unlock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + LeaveCriticalSection(*mp); + return 0; +} diff --git a/src/libpq/pthread-win32.h b/src/libpq/pthread-win32.h new file mode 100644 index 00000000..97ccc17a --- /dev/null +++ b/src/libpq/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/src/libpq/snprintf.c b/src/libpq/snprintf.c new file mode 100644 index 00000000..9f719505 --- /dev/null +++ b/src/libpq/snprintf.c @@ -0,0 +1,1041 @@ +/* + * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/snprintf.c + */ + +#include "c.h" + +#include +#ifndef WIN32 +#include +#endif +#include + +#ifndef NL_ARGMAX +#define NL_ARGMAX 16 +#endif + + +/* + * SNPRINTF, VSNPRINTF and friends + * + * These versions have been grabbed off the net. They have been + * cleaned up to compile properly and support for most of the Single Unix + * Specification has been added. Remaining unimplemented features are: + * + * 1. No locale support: the radix character is always '.' and the ' + * (single quote) format flag is ignored. + * + * 2. No support for the "%n" format specification. + * + * 3. No support for wide characters ("lc" and "ls" formats). + * + * 4. No support for "long double" ("Lf" and related formats). + * + * 5. Space and '#' flags are not implemented. + * + * + * The result values of these functions are not the same across different + * platforms. This implementation is compatible with the Single Unix Spec: + * + * 1. -1 is returned only if processing is abandoned due to an invalid + * parameter, such as incorrect format string. (Although not required by + * the spec, this happens only when no characters have yet been transmitted + * to the destination.) + * + * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0; + * no data has been stored. + * + * 3. Otherwise, the number of bytes actually transmitted to the destination + * is returned (excluding the trailing '\0' for snprintf and sprintf). + * + * For snprintf with nonzero count, the result cannot be more than count-1 + * (a trailing '\0' is always stored); it is not possible to distinguish + * buffer overrun from exact fit. This is unlike some implementations that + * return the number of bytes that would have been needed for the complete + * result string. + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point. (now it does ... tgl) + **************************************************************/ + +/* Prevent recursion */ +#undef vsnprintf +#undef snprintf +#undef sprintf +#undef vfprintf +#undef fprintf +#undef printf + +/* Info about where the formatted output is going */ +typedef struct +{ + char *bufptr; /* next buffer output position */ + char *bufstart; /* first buffer element */ + char *bufend; /* last buffer element, or NULL */ + /* bufend == NULL is for sprintf, where we assume buf is big enough */ + FILE *stream; /* eventual output destination, or NULL */ + int nchars; /* # chars already sent to stream */ +} PrintfTarget; + +/* + * Info about the type and value of a formatting parameter. Note that we + * don't currently support "long double", "wint_t", or "wchar_t *" data, + * nor the '%n' formatting code; else we'd need more types. Also, at this + * level we need not worry about signed vs unsigned values. + */ +typedef enum +{ + ATYPE_NONE = 0, + ATYPE_INT, + ATYPE_LONG, + ATYPE_LONGLONG, + ATYPE_DOUBLE, + ATYPE_CHARPTR +} PrintfArgType; + +typedef union +{ + int i; + long l; + int64 ll; + double d; + char *cptr; +} PrintfArgValue; + + +static void flushbuffer(PrintfTarget *target); +static int dopr(PrintfTarget *target, const char *format, va_list args); + + +int +pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL || count == 0) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = str + count - 1; + target.stream = NULL; + /* target.nchars is unused in this case */ + if (dopr(&target, fmt, args)) + { + *(target.bufptr) = '\0'; + errno = EINVAL; /* bad format */ + return -1; + } + *(target.bufptr) = '\0'; + return target.bufptr - target.bufstart; +} + +int +pg_snprintf(char *str, size_t count, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + +static int +pg_vsprintf(char *str, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = NULL; + target.stream = NULL; + /* target.nchars is unused in this case */ + if (dopr(&target, fmt, args)) + { + *(target.bufptr) = '\0'; + errno = EINVAL; /* bad format */ + return -1; + } + *(target.bufptr) = '\0'; + return target.bufptr - target.bufstart; +} + +int +pg_sprintf(char *str, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsprintf(str, fmt, args); + va_end(args); + return len; +} + +int +pg_vfprintf(FILE *stream, const char *fmt, va_list args) +{ + PrintfTarget target; + char buffer[1024]; /* size is arbitrary */ + + if (stream == NULL) + { + errno = EINVAL; + return -1; + } + target.bufstart = target.bufptr = buffer; + target.bufend = buffer + sizeof(buffer) - 1; + target.stream = stream; + target.nchars = 0; + if (dopr(&target, fmt, args)) + { + errno = EINVAL; /* bad format */ + return -1; + } + /* dump any remaining buffer contents */ + flushbuffer(&target); + return target.nchars; +} + +int +pg_fprintf(FILE *stream, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stream, fmt, args); + va_end(args); + return len; +} + +int +pg_printf(const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stdout, fmt, args); + va_end(args); + return len; +} + +/* call this only when stream is defined */ +static void +flushbuffer(PrintfTarget *target) +{ + size_t nc = target->bufptr - target->bufstart; + + if (nc > 0) + target->nchars += fwrite(target->bufstart, 1, nc, target->stream); + target->bufptr = target->bufstart; +} + + +static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target); +static void fmtptr(void *value, PrintfTarget *target); +static void fmtint(int64 value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); +static void fmtfloat(double value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void dostr(const char *str, int slen, PrintfTarget *target); +static void dopr_outch(int c, PrintfTarget *target); +static int adjust_sign(int is_negative, int forcesign, int *signvalue); +static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); +static void leading_pad(int zpad, int *signvalue, int *padlen, + PrintfTarget *target); +static void trailing_pad(int *padlen, PrintfTarget *target); + + +/* + * dopr(): poor man's version of doprintf + */ +static int +dopr(PrintfTarget *target, const char *format, va_list args) +{ + const char *format_start = format; + int ch; + bool have_dollar; + bool have_non_dollar; + bool have_star; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int pointflag; + int leftjust; + int fieldwidth; + int precision; + int zpad; + int forcesign; + int last_dollar; + int fmtpos; + int cvalue; + int64 numvalue; + double fvalue; + char *strvalue; + int i; + PrintfArgType argtypes[NL_ARGMAX + 1]; + PrintfArgValue argvalues[NL_ARGMAX + 1]; + + /* + * Parse the format string to determine whether there are %n$ format + * specs, and identify the types and order of the format parameters. + */ + have_dollar = have_non_dollar = false; + last_dollar = 0; + MemSet(argtypes, 0, sizeof(argtypes)); + + while ((ch = *format++) != '\0') + { + if (ch != '%') + continue; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + afterstar = false; +nextch1: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + case '+': + goto nextch1; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch1; + case '.': + pointflag = 1; + accum = 0; + goto nextch1; + case '*': + if (afterstar) + have_non_dollar = true; /* multiple stars */ + afterstar = true; + accum = 0; + goto nextch1; + case '$': + have_dollar = true; + if (accum <= 0 || accum > NL_ARGMAX) + return -1; + if (afterstar) + { + if (argtypes[accum] && + argtypes[accum] != ATYPE_INT) + return -1; + argtypes[accum] = ATYPE_INT; + last_dollar = Max(last_dollar, accum); + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch1; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch1; + case 'h': + case '\'': + /* ignore these */ + goto nextch1; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (fmtpos) + { + PrintfArgType atype; + + if (longlongflag) + atype = ATYPE_LONGLONG; + else if (longflag) + atype = ATYPE_LONG; + else + atype = ATYPE_INT; + if (argtypes[fmtpos] && + argtypes[fmtpos] != atype) + return -1; + argtypes[fmtpos] = atype; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'c': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_INT) + return -1; + argtypes[fmtpos] = ATYPE_INT; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 's': + case 'p': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_CHARPTR) + return -1; + argtypes[fmtpos] = ATYPE_CHARPTR; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_DOUBLE) + return -1; + argtypes[fmtpos] = ATYPE_DOUBLE; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case '%': + break; + } + + /* + * If we finish the spec with afterstar still set, there's a + * non-dollar star in there. + */ + if (afterstar) + have_non_dollar = true; + } + + /* Per spec, you use either all dollar or all not. */ + if (have_dollar && have_non_dollar) + return -1; + + /* + * In dollar mode, collect the arguments in physical order. + */ + for (i = 1; i <= last_dollar; i++) + { + switch (argtypes[i]) + { + case ATYPE_NONE: + return -1; /* invalid format */ + case ATYPE_INT: + argvalues[i].i = va_arg(args, int); + break; + case ATYPE_LONG: + argvalues[i].l = va_arg(args, long); + break; + case ATYPE_LONGLONG: + argvalues[i].ll = va_arg(args, int64); + break; + case ATYPE_DOUBLE: + argvalues[i].d = va_arg(args, double); + break; + case ATYPE_CHARPTR: + argvalues[i].cptr = va_arg(args, char *); + break; + } + } + + /* + * At last we can parse the format for real. + */ + format = format_start; + while ((ch = *format++) != '\0') + { + if (ch != '%') + { + dopr_outch(ch, target); + continue; + } + fieldwidth = precision = zpad = leftjust = forcesign = 0; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + have_star = afterstar = false; +nextch2: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + leftjust = 1; + goto nextch2; + case '+': + forcesign = 1; + goto nextch2; + case '0': + /* set zero padding if no nonzero digits yet */ + if (accum == 0 && !pointflag) + zpad = '0'; + /* FALL THRU */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch2; + case '.': + if (have_star) + have_star = false; + else + fieldwidth = accum; + pointflag = 1; + accum = 0; + goto nextch2; + case '*': + if (have_dollar) + { + /* process value after reading n$ */ + afterstar = true; + } + else + { + /* fetch and process value now */ + int starval = va_arg(args, int); + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + } + have_star = true; + accum = 0; + goto nextch2; + case '$': + if (afterstar) + { + /* fetch and process star value */ + int starval = argvalues[accum].i; + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch2; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch2; + case 'h': + case '\'': + /* ignore these */ + goto nextch2; + case 'd': + case 'i': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = argvalues[fmtpos].ll; + else if (longflag) + numvalue = argvalues[fmtpos].l; + else + numvalue = argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = va_arg(args, int64); + else if (longflag) + numvalue = va_arg(args, long); + else + numvalue = va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'o': + case 'u': + case 'x': + case 'X': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = (uint64) argvalues[fmtpos].ll; + else if (longflag) + numvalue = (unsigned long) argvalues[fmtpos].l; + else + numvalue = (unsigned int) argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = (uint64) va_arg(args, int64); + else if (longflag) + numvalue = (unsigned long) va_arg(args, long); + else + numvalue = (unsigned int) va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'c': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + cvalue = (unsigned char) argvalues[fmtpos].i; + else + cvalue = (unsigned char) va_arg(args, int); + fmtchar(cvalue, leftjust, fieldwidth, target); + break; + case 's': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag, + target); + break; + case 'p': + /* fieldwidth/leftjust are ignored ... */ + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtptr((void *) strvalue, target); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + fvalue = argvalues[fmtpos].d; + else + fvalue = va_arg(args, double); + fmtfloat(fvalue, ch, forcesign, leftjust, + fieldwidth, zpad, + precision, pointflag, + target); + break; + case '%': + dopr_outch('%', target); + break; + } + } + + return 0; +} + +static size_t +pg_strnlen(const char *str, size_t maxlen) +{ + const char *p = str; + + while (maxlen-- > 0 && *p) + p++; + return p - str; +} + +static void +fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target) +{ + int padlen, + vallen; /* amount to pad */ + + /* + * If a maxwidth (precision) is specified, we must not fetch more bytes + * than that. + */ + if (pointflag) + vallen = pg_strnlen(value, maxwidth); + else + vallen = strlen(value); + + adjust_padlen(minlen, vallen, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dostr(value, vallen, target); + + trailing_pad(&padlen, target); +} + +static void +fmtptr(void *value, PrintfTarget *target) +{ + int vallen; + char convert[64]; + + /* we rely on regular C library's sprintf to do the basic conversion */ + vallen = sprintf(convert, "%p", value); + + dostr(convert, vallen, target); +} + +static void +fmtint(int64 value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + uint64 base; + int dosign; + const char *cvt = "0123456789abcdef"; + int signvalue = 0; + char convert[64]; + int vallen = 0; + int padlen = 0; /* amount to pad */ + int zeropad; /* extra leading zeroes */ + + switch (type) + { + case 'd': + case 'i': + base = 10; + dosign = 1; + break; + case 'o': + base = 8; + dosign = 0; + break; + case 'u': + base = 10; + dosign = 0; + break; + case 'x': + base = 16; + dosign = 0; + break; + case 'X': + cvt = "0123456789ABCDEF"; + base = 16; + dosign = 0; + break; + default: + return; /* keep compiler quiet */ + } + + /* Handle +/- */ + if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + /* + * SUS: the result of converting 0 with an explicit precision of 0 is no + * characters + */ + if (value == 0 && pointflag && precision == 0) + vallen = 0; + else + { + /* make integer string */ + uint64 uvalue = (uint64) value; + + do + { + convert[vallen++] = cvt[uvalue % base]; + uvalue = uvalue / base; + } while (uvalue); + } + + zeropad = Max(0, precision - vallen); + + adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + while (zeropad-- > 0) + dopr_outch('0', target); + + while (vallen > 0) + dopr_outch(convert[--vallen], target); + + trailing_pad(&padlen, target); +} + +static void +fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) +{ + int padlen = 0; /* amount to pad */ + + adjust_padlen(minlen, 1, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dopr_outch(value, target); + + trailing_pad(&padlen, target); +} + +static void +fmtfloat(double value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + int signvalue = 0; + int vallen; + char fmt[32]; + char convert[512]; + int padlen = 0; /* amount to pad */ + + /* we rely on regular C library's sprintf to do the basic conversion */ + if (pointflag) + sprintf(fmt, "%%.%d%c", precision, type); + else + sprintf(fmt, "%%%c", type); + + if (adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + vallen = sprintf(convert, fmt, value); + + adjust_padlen(minlen, vallen, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + dostr(convert, vallen, target); + + trailing_pad(&padlen, target); +} + +static void +dostr(const char *str, int slen, PrintfTarget *target) +{ + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memmove(target->bufptr, str, avail); + target->bufptr += avail; + str += avail; + slen -= avail; + } +} + +static void +dopr_outch(int c, PrintfTarget *target) +{ + if (target->bufend != NULL && target->bufptr >= target->bufend) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + } + *(target->bufptr++) = c; +} + + +static int +adjust_sign(int is_negative, int forcesign, int *signvalue) +{ + if (is_negative) + { + *signvalue = '-'; + return true; + } + else if (forcesign) + *signvalue = '+'; + return false; +} + + +static void +adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) +{ + *padlen = minlen - vallen; + if (*padlen < 0) + *padlen = 0; + if (leftjust) + *padlen = -(*padlen); +} + + +static void +leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target) +{ + if (*padlen > 0 && zpad) + { + if (*signvalue) + { + dopr_outch(*signvalue, target); + --(*padlen); + *signvalue = 0; + } + while (*padlen > 0) + { + dopr_outch(zpad, target); + --(*padlen); + } + } + while (*padlen > (*signvalue != 0)) + { + dopr_outch(' ', target); + --(*padlen); + } + if (*signvalue) + { + dopr_outch(*signvalue, target); + if (*padlen > 0) + --(*padlen); + else if (*padlen < 0) + ++(*padlen); + } +} + + +static void +trailing_pad(int *padlen, PrintfTarget *target) +{ + while (*padlen < 0) + { + dopr_outch(' ', target); + ++(*padlen); + } +} diff --git a/src/libpq/strlcpy.c b/src/libpq/strlcpy.c new file mode 100644 index 00000000..b20af029 --- /dev/null +++ b/src/libpq/strlcpy.c @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * strlcpy.c + * strncpy done right + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/strlcpy.c + * + * This file was taken from OpenBSD and is used on platforms that don't + * provide strlcpy(). The OpenBSD copyright terms follow. + *------------------------------------------------------------------------- + */ + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "c.h" + + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + * Function creation history: http://www.gratisoft.us/todd/papers/strlcpy.html + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} diff --git a/src/libpq/thread.c b/src/libpq/thread.c new file mode 100644 index 00000000..bca5815a --- /dev/null +++ b/src/libpq/thread.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * + * thread.c + * + * Prototypes and macros around system calls, used to help make + * threaded libraries reentrant and safe to use from threaded applications. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * src/port/thread.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * Threading sometimes requires specially-named versions of functions + * that return data in static buffers, like strerror_r() instead of + * strerror(). Other operating systems use pthread_setspecific() + * and pthread_getspecific() internally to allow standard library + * functions to return static data to threaded applications. And some + * operating systems have neither. + * + * Additional confusion exists because many operating systems that + * use pthread_setspecific/pthread_getspecific() also have *_r versions + * of standard library functions for compatibility with operating systems + * that require them. However, internally, these *_r functions merely + * call the thread-safe standard library functions. + * + * For example, BSD/OS 4.3 uses Bind 8.2.3 for getpwuid(). Internally, + * getpwuid() calls pthread_setspecific/pthread_getspecific() to return + * static data to the caller in a thread-safe manner. However, BSD/OS + * also has getpwuid_r(), which merely calls getpwuid() and shifts + * around the arguments to match the getpwuid_r() function declaration. + * Therefore, while BSD/OS has getpwuid_r(), it isn't required. It also + * doesn't have strerror_r(), so we can't fall back to only using *_r + * functions for threaded programs. + * + * The current setup is to try threading in this order: + * + * use *_r function names if they exit + * (*_THREADSAFE=yes) + * use non-*_r functions if they are thread-safe + * + * One thread-safe solution for gethostbyname() might be to use getaddrinfo(). + * + * Run src/test/thread to test if your operating system has thread-safe + * non-*_r functions. + */ + + +/* + * Wrapper around strerror and strerror_r to use the former if it is + * available and also return a more useful value (the error string). + */ +char * +pqStrerror(int errnum, char *strerrbuf, size_t buflen) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_STRERROR_R) + /* reentrant strerror_r is available */ +#ifdef STRERROR_R_INT + /* SUSv3 version */ + if (strerror_r(errnum, strerrbuf, buflen) == 0) + return strerrbuf; + else + return "Unknown error"; +#else + /* GNU libc */ + return strerror_r(errnum, strerrbuf, buflen); +#endif +#else + /* no strerror_r() available, just use strerror */ + strlcpy(strerrbuf, strerror(errnum), buflen); + + return strerrbuf; +#endif +} + +/* + * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() + * behaviour, if it is not available or required. + */ +#ifndef WIN32 +int +pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETPWUID_R) + +#ifdef GETPWUID_R_5ARG + /* POSIX version */ + getpwuid_r(uid, resultbuf, buffer, buflen, result); +#else + + /* + * Early POSIX draft of getpwuid_r() returns 'struct passwd *'. + * getpwuid_r(uid, resultbuf, buffer, buflen) + */ + *result = getpwuid_r(uid, resultbuf, buffer, buflen); +#endif +#else + + /* no getpwuid_r() available, just use getpwuid() */ + *result = getpwuid(uid); +#endif + + return (*result == NULL) ? -1 : 0; +} +#endif + +/* + * Wrapper around gethostbyname() or gethostbyname_r() to mimic + * POSIX gethostbyname_r() behaviour, if it is not available or required. + * This function is called _only_ by our getaddinfo() portability function. + */ +#ifndef HAVE_GETADDRINFO +int +pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETHOSTBYNAME_R) + + /* + * broken (well early POSIX draft) gethostbyname_r() which returns 'struct + * hostent *' + */ + *result = gethostbyname_r(name, resultbuf, buffer, buflen, herrno); + return (*result == NULL) ? -1 : 0; +#else + + /* no gethostbyname_r(), just use gethostbyname() */ + *result = gethostbyname(name); + + if (*result != NULL) + *herrno = h_errno; + + if (*result != NULL) + return 0; + else + return -1; +#endif +} + +#endif diff --git a/src/libpq/wchar.c b/src/libpq/wchar.c new file mode 100644 index 00000000..5b0cf628 --- /dev/null +++ b/src/libpq/wchar.c @@ -0,0 +1,1646 @@ +/* + * conversion functions between pg_wchar and multibyte streams. + * Tatsuo Ishii + * src/backend/utils/mb/wchar.c + * + */ +/* can be used in either frontend or backend */ +#ifdef FRONTEND +#include "postgres_fe.h" +#define Assert(condition) +#else +#include "postgres.h" +#endif + +#include "mb/pg_wchar.h" + + +/* + * conversion to pg_wchar is done by "table driven." + * to add an encoding support, define mb2wchar_with_len(), mblen(), dsplen() + * for the particular encoding. Note that if the encoding is only + * supported in the client, you don't need to define + * mb2wchar_with_len() function (SJIS is the case). + * + * These functions generally assume that their input is validly formed. + * The "verifier" functions, further down in the file, have to be more + * paranoid. We expect that mblen() does not need to examine more than + * the first byte of the character to discover the correct length. + * + * Note: for the display output of psql to work properly, the return values + * of the dsplen functions must conform to the Unicode standard. In particular + * the NUL character is zero width and control characters are generally + * width -1. It is recommended that non-ASCII encodings refer their ASCII + * subset to the ASCII routines to ensure consistency. + */ + +/* + * SQL/ASCII + */ +static int +pg_ascii2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_ascii_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_ascii_dsplen(const unsigned char *s) +{ + if (*s == '\0') + return 0; + if (*s < 0x20 || *s == 0x7f) + return -1; + + return 1; +} + +/* + * EUC + */ +static int +pg_euc2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 2) /* JIS X 0201 (so called "1 byte + * KANA") */ + { + from++; + *to = (SS2 << 8) | *from++; + len -= 2; + } + else if (*from == SS3 && len >= 3) /* JIS X 0212 KANJI */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* JIS X 0208 KANJI */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + /* must be ASCII */ + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static inline int +pg_euc_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static inline int +pg_euc_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_JP + */ +static int +pg_eucjp2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_eucjp_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_eucjp_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 1; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_KR + */ +static int +pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_euckr_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_euckr_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * EUC_CN + * + */ +static int +pg_euccn2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 3) /* code set 2 (unused?) */ + { + from++; + *to = (SS2 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unsed ?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 1 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euccn_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euccn_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_TW + * + */ +static int +pg_euctw2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 4) /* code set 2 */ + { + from++; + *to = (((uint32) SS2) << 24) | (*from++ << 16); + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unused?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 2 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euctw_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 4; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euctw_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * JOHAB + */ +static int +pg_johab_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_johab_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * convert UTF8 string to pg_wchar (UCS-4) + * caller must allocate enough space for "to", including a trailing zero! + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + uint32 c1, + c2, + c3, + c4; + + while (len > 0 && *from) + { + if ((*from & 0x80) == 0) + { + *to = *from++; + len--; + } + else if ((*from & 0xe0) == 0xc0) + { + if (len < 2) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x1f; + c2 = *from++ & 0x3f; + *to = (c1 << 6) | c2; + len -= 2; + } + else if ((*from & 0xf0) == 0xe0) + { + if (len < 3) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x0f; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + *to = (c1 << 12) | (c2 << 6) | c3; + len -= 3; + } + else if ((*from & 0xf8) == 0xf0) + { + if (len < 4) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x07; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + c4 = *from++ & 0x3f; + *to = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; + len -= 4; + } + else + { + /* treat a bogus char as length 1; not ours to raise error */ + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + + +/* + * Map a Unicode code point to UTF-8. utf8string must have 4 bytes of + * space allocated. + */ +unsigned char * +unicode_to_utf8(pg_wchar c, unsigned char *utf8string) +{ + if (c <= 0x7F) + { + utf8string[0] = c; + } + else if (c <= 0x7FF) + { + utf8string[0] = 0xC0 | ((c >> 6) & 0x1F); + utf8string[1] = 0x80 | (c & 0x3F); + } + else if (c <= 0xFFFF) + { + utf8string[0] = 0xE0 | ((c >> 12) & 0x0F); + utf8string[1] = 0x80 | ((c >> 6) & 0x3F); + utf8string[2] = 0x80 | (c & 0x3F); + } + else + { + utf8string[0] = 0xF0 | ((c >> 18) & 0x07); + utf8string[1] = 0x80 | ((c >> 12) & 0x3F); + utf8string[2] = 0x80 | ((c >> 6) & 0x3F); + utf8string[3] = 0x80 | (c & 0x3F); + } + + return utf8string; +} + + +/* + * Return the byte length of a UTF8 character pointed to by s + * + * Note: in the current implementation we do not support UTF8 sequences + * of more than 4 bytes; hence do NOT return a value larger than 4. + * We return "1" for any leading byte that is either flat-out illegal or + * indicates a length larger than we support. + * + * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal(), and perhaps + * other places would need to be fixed to change this. + */ +int +pg_utf_mblen(const unsigned char *s) +{ + int len; + + if ((*s & 0x80) == 0) + len = 1; + else if ((*s & 0xe0) == 0xc0) + len = 2; + else if ((*s & 0xf0) == 0xe0) + len = 3; + else if ((*s & 0xf8) == 0xf0) + len = 4; +#ifdef NOT_USED + else if ((*s & 0xfc) == 0xf8) + len = 5; + else if ((*s & 0xfe) == 0xfc) + len = 6; +#endif + else + len = 1; + return len; +} + +/* + * This is an implementation of wcwidth() and wcswidth() as defined in + * "The Single UNIX Specification, Version 2, The Open Group, 1997" + * + * + * Markus Kuhn -- 2001-09-08 -- public domain + * + * customised for PostgreSQL + * + * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +struct mbinterval +{ + unsigned short first; + unsigned short last; +}; + +/* auxiliary function for binary search in interval table */ +static int +mbbisearch(pg_wchar ucs, const struct mbinterval * table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) + { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int +ucs_wcwidth(pg_wchar ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + static const struct mbinterval combining[] = { + {0x0300, 0x034E}, {0x0360, 0x0362}, {0x0483, 0x0486}, + {0x0488, 0x0489}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, + {0x05C4, 0x05C4}, {0x064B, 0x0655}, {0x0670, 0x0670}, + {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, + {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, + {0x07A6, 0x07B0}, {0x0901, 0x0902}, {0x093C, 0x093C}, + {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, + {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, + {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, + {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, + {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, + {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, + {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, + {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, + {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, + {0x1160, 0x11FF}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, + {0x17C9, 0x17D3}, {0x180B, 0x180E}, {0x18A9, 0x18A9}, + {0x200B, 0x200F}, {0x202A, 0x202E}, {0x206A, 0x206F}, + {0x20D0, 0x20E3}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xFB1E, 0xFB1E}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB} + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + + if (ucs < 0x20 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) + return -1; + + /* binary search in table of non-spacing characters */ + if (mbbisearch(ucs, combining, + sizeof(combining) / sizeof(struct mbinterval) - 1)) + return 0; + + /* + * if we arrive here, ucs is not a combining or C0/C1 control character + */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility + * Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2ffff))); +} + +/* + * Convert a UTF-8 character to a Unicode code point. + * This is a one-character version of pg_utf2wchar_with_len. + * + * No error checks here, c must point to a long-enough string. + */ +pg_wchar +utf8_to_unicode(const unsigned char *c) +{ + if ((*c & 0x80) == 0) + return (pg_wchar) c[0]; + else if ((*c & 0xe0) == 0xc0) + return (pg_wchar) (((c[0] & 0x1f) << 6) | + (c[1] & 0x3f)); + else if ((*c & 0xf0) == 0xe0) + return (pg_wchar) (((c[0] & 0x0f) << 12) | + ((c[1] & 0x3f) << 6) | + (c[2] & 0x3f)); + else if ((*c & 0xf8) == 0xf0) + return (pg_wchar) (((c[0] & 0x07) << 18) | + ((c[1] & 0x3f) << 12) | + ((c[2] & 0x3f) << 6) | + (c[3] & 0x3f)); + else + /* that is an invalid code on purpose */ + return 0xffffffff; +} + +static int +pg_utf_dsplen(const unsigned char *s) +{ + return ucs_wcwidth(utf8_to_unicode(s)); +} + +/* + * convert mule internal code to pg_wchar + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_mule2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (IS_LC1(*from) && len >= 2) + { + *to = *from++ << 16; + *to |= *from++; + len -= 2; + } + else if (IS_LCPRV1(*from) && len >= 3) + { + from++; + *to = *from++ << 16; + *to |= *from++; + len -= 3; + } + else if (IS_LC2(*from) && len >= 3) + { + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 3; + } + else if (IS_LCPRV2(*from) && len >= 4) + { + from++; + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else + { /* assume ASCII */ + *to = (unsigned char) *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +int +pg_mule_mblen(const unsigned char *s) +{ + int len; + + if (IS_LC1(*s)) + len = 2; + else if (IS_LCPRV1(*s)) + len = 3; + else if (IS_LC2(*s)) + len = 3; + else if (IS_LCPRV2(*s)) + len = 4; + else + len = 1; /* assume ASCII */ + return len; +} + +static int +pg_mule_dsplen(const unsigned char *s) +{ + int len; + + if (IS_LC1(*s)) + len = 1; + else if (IS_LCPRV1(*s)) + len = 1; + else if (IS_LC2(*s)) + len = 2; + else if (IS_LCPRV2(*s)) + len = 2; + else + len = 1; /* assume ASCII */ + + return len; +} + +/* + * ISO8859-1 + */ +static int +pg_latin12wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_latin1_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_latin1_dsplen(const unsigned char *s) +{ + return pg_ascii_dsplen(s); +} + +/* + * SJIS + */ +static int +pg_sjis_mblen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_sjis_dsplen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * Big5 + */ +static int +pg_big5_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_big5_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * GBK + */ +static int +pg_gbk_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_gbk_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * UHC + */ +static int +pg_uhc_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_uhc_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * * GB18030 + * * Added by Bill Huang , + * */ +static int +pg_gb18030_mblen(const unsigned char *s) +{ + int len; + + if (!IS_HIGHBIT_SET(*s)) + len = 1; /* ASCII */ + else + { + if ((*(s + 1) >= 0x40 && *(s + 1) <= 0x7e) || (*(s + 1) >= 0x80 && *(s + 1) <= 0xfe)) + len = 2; + else if (*(s + 1) >= 0x30 && *(s + 1) <= 0x39) + len = 4; + else + len = 2; + } + return len; +} + +static int +pg_gb18030_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); /* ASCII */ + return len; +} + +/* + *------------------------------------------------------------------- + * multibyte sequence validators + * + * These functions accept "s", a pointer to the first byte of a string, + * and "len", the remaining length of the string. If there is a validly + * encoded character beginning at *s, return its length in bytes; else + * return -1. + * + * The functions can assume that len > 0 and that *s != '\0', but they must + * test for and reject zeroes in any additional bytes of a multibyte character. + * + * Note that this definition allows the function for a single-byte + * encoding to be just "return 1". + *------------------------------------------------------------------- + */ + +static int +pg_ascii_verifier(const unsigned char *s, int len) +{ + return 1; +} + +#define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe) + +static int +pg_eucjp_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* JIS X 0201 */ + l = 2; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xdf) + return -1; + break; + + case SS3: /* JIS X 0212 */ + l = 3; + if (l > len) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + default: + if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + + return l; +} + +static int +pg_euckr_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + if (IS_HIGHBIT_SET(c1)) + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + + return l; +} + +/* EUC-CN byte sequences are exactly same as EUC-KR */ +#define pg_euccn_verifier pg_euckr_verifier + +static int +pg_euctw_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* CNS 11643 Plane 1-7 */ + l = 4; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xa7) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + case SS3: /* unused */ + return -1; + + default: + if (IS_HIGHBIT_SET(c1)) /* CNS 11643 Plane 1 */ + { + l = 2; + if (l > len) + return -1; + /* no further range check on c1? */ + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + return l; +} + +static int +pg_johab_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_johab_mblen(s); + + if (len < l) + return -1; + + if (!IS_HIGHBIT_SET(*s)) + return mbl; + + while (--l > 0) + { + c = *++s; + if (!IS_EUC_RANGE_VALID(c)) + return -1; + } + return mbl; +} + +static int +pg_mule_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_mule_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + c = *++s; + if (!IS_HIGHBIT_SET(c)) + return -1; + } + return mbl; +} + +static int +pg_latin1_verifier(const unsigned char *s, int len) +{ + return 1; +} + +static int +pg_sjis_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c1, + c2; + + l = mbl = pg_sjis_mblen(s); + + if (len < l) + return -1; + + if (l == 1) /* pg_sjis_mblen already verified it */ + return mbl; + + c1 = *s++; + c2 = *s; + if (!ISSJISHEAD(c1) || !ISSJISTAIL(c2)) + return -1; + return mbl; +} + +static int +pg_big5_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_big5_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gbk_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_gbk_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_uhc_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_uhc_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gb18030_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_gb18030_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_utf8_verifier(const unsigned char *s, int len) +{ + int l = pg_utf_mblen(s); + + if (len < l) + return -1; + + if (!pg_utf8_islegal(s, l)) + return -1; + + return l; +} + +/* + * Check for validity of a single UTF-8 encoded character + * + * This directly implements the rules in RFC3629. The bizarre-looking + * restrictions on the second byte are meant to ensure that there isn't + * more than one encoding of a given Unicode character point; that is, + * you may not use a longer-than-necessary byte sequence with high order + * zero bits to represent a character that would fit in fewer bytes. + * To do otherwise is to create security hazards (eg, create an apparent + * non-ASCII character that decodes to plain ASCII). + * + * length is assumed to have been obtained by pg_utf_mblen(), and the + * caller must have checked that that many bytes are present in the buffer. + */ +bool +pg_utf8_islegal(const unsigned char *source, int length) +{ + unsigned char a; + + switch (length) + { + default: + /* reject lengths 5 and 6 for now */ + return false; + case 4: + a = source[3]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 3: + a = source[2]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 2: + a = source[1]; + switch (*source) + { + case 0xE0: + if (a < 0xA0 || a > 0xBF) + return false; + break; + case 0xED: + if (a < 0x80 || a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90 || a > 0xBF) + return false; + break; + case 0xF4: + if (a < 0x80 || a > 0x8F) + return false; + break; + default: + if (a < 0x80 || a > 0xBF) + return false; + break; + } + /* FALL THRU */ + case 1: + a = *source; + if (a >= 0x80 && a < 0xC2) + return false; + if (a > 0xF4) + return false; + break; + } + return true; +} + +/* + *------------------------------------------------------------------- + * encoding info table + * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) + *------------------------------------------------------------------- + */ +pg_wchar_tbl pg_wchar_table[] = { + {pg_ascii2wchar_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */ + {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JP */ + {pg_euccn2wchar_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifier, 2}, /* PG_EUC_CN */ + {pg_euckr2wchar_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifier, 3}, /* PG_EUC_KR */ + {pg_euctw2wchar_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifier, 4}, /* PG_EUC_TW */ + {pg_eucjp2wchar_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */ + {pg_utf2wchar_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifier, 4}, /* PG_UTF8 */ + {pg_mule2wchar_with_len, pg_mule_mblen, pg_mule_dsplen, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN1 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN2 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN3 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN4 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN5 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN6 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN7 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN8 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN9 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN10 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1256 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1258 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN866 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN874 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8R */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1251 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1252 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-5 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-6 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-7 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-8 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1250 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1253 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1254 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1255 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1257 */ + {pg_latin12wchar_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8U */ + {0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}, /* PG_SJIS */ + {0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifier, 2}, /* PG_BIG5 */ + {0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifier, 2}, /* PG_GBK */ + {0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifier, 2}, /* PG_UHC */ + {0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifier, 4}, /* PG_GB18030 */ + {0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifier, 3}, /* PG_JOHAB */ + {0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */ +}; + +/* returns the byte length of a word for mule internal code */ +int +pg_mic_mblen(const unsigned char *mbstr) +{ + return pg_mule_mblen(mbstr); +} + +/* + * Returns the byte length of a multibyte character. + */ +int +pg_encoding_mblen(int encoding, const char *mbstr) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return ((encoding >= 0 && + encoding < sizeof(pg_wchar_table) / sizeof(pg_wchar_tbl)) ? + ((*pg_wchar_table[encoding].mblen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].mblen) ((const unsigned char *) mbstr))); +} + +/* + * Returns the display length of a multibyte character. + */ +int +pg_encoding_dsplen(int encoding, const char *mbstr) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return ((encoding >= 0 && + encoding < sizeof(pg_wchar_table) / sizeof(pg_wchar_tbl)) ? + ((*pg_wchar_table[encoding].dsplen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].dsplen) ((const unsigned char *) mbstr))); +} + +/* + * Verify the first multibyte character of the given string. + * Return its byte length if good, -1 if bad. (See comments above for + * full details of the mbverify API.) + */ +int +pg_encoding_verifymb(int encoding, const char *mbstr, int len) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return ((encoding >= 0 && + encoding < sizeof(pg_wchar_table) / sizeof(pg_wchar_tbl)) ? + ((*pg_wchar_table[encoding].mbverify) ((const unsigned char *) mbstr, len)) : + ((*pg_wchar_table[PG_SQL_ASCII].mbverify) ((const unsigned char *) mbstr, len))); +} + +/* + * fetch maximum length of a given encoding + */ +int +pg_encoding_max_length(int encoding) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return pg_wchar_table[encoding].maxmblen; +} + +#ifndef FRONTEND + +/* + * fetch maximum length of the encoding for the current database + */ +int +pg_database_encoding_max_length(void) +{ + return pg_wchar_table[GetDatabaseEncoding()].maxmblen; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the current + * database encoding. Otherwise same as pg_verify_mbstr(). + */ +bool +pg_verifymbstr(const char *mbstr, int len, bool noError) +{ + return + pg_verify_mbstr_len(GetDatabaseEncoding(), mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + */ +bool +pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError) +{ + return pg_verify_mbstr_len(encoding, mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + * + * mbstr is not necessarily zero terminated; length of mbstr is + * specified by len. + * + * If OK, return length of string in the encoding. + * If a problem is found, return -1 when noError is + * true; when noError is false, ereport() a descriptive message. + */ +int +pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError) +{ + mbverifier mbverify; + int mb_len; + + Assert(PG_VALID_ENCODING(encoding)); + + /* + * In single-byte encodings, we need only reject nulls (\0). + */ + if (pg_encoding_max_length(encoding) <= 1) + { + const char *nullpos = memchr(mbstr, 0, len); + + if (nullpos == NULL) + return len; + if (noError) + return -1; + report_invalid_encoding(encoding, nullpos, 1); + } + + /* fetch function pointer just once */ + mbverify = pg_wchar_table[encoding].mbverify; + + mb_len = 0; + + while (len > 0) + { + int l; + + /* fast path for ASCII-subset characters */ + if (!IS_HIGHBIT_SET(*mbstr)) + { + if (*mbstr != '\0') + { + mb_len++; + mbstr++; + len--; + continue; + } + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + l = (*mbverify) ((const unsigned char *) mbstr, len); + + if (l < 0) + { + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + mbstr += l; + len -= l; + mb_len++; + } + return mb_len; +} + +/* + * check_encoding_conversion_args: check arguments of a conversion function + * + * "expected" arguments can be either an encoding ID or -1 to indicate that + * the caller will check whether it accepts the ID. + * + * Note: the errors here are not really user-facing, so elog instead of + * ereport seems sufficient. Also, we trust that the "expected" encoding + * arguments are valid encoding IDs, but we don't trust the actuals. + */ +void +check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding) +{ + if (!PG_VALID_ENCODING(src_encoding)) + elog(ERROR, "invalid source encoding ID: %d", src_encoding); + if (src_encoding != expected_src_encoding && expected_src_encoding >= 0) + elog(ERROR, "expected source encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_src_encoding].name, + pg_enc2name_tbl[src_encoding].name); + if (!PG_VALID_ENCODING(dest_encoding)) + elog(ERROR, "invalid destination encoding ID: %d", dest_encoding); + if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0) + elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_dest_encoding].name, + pg_enc2name_tbl[dest_encoding].name); + if (len < 0) + elog(ERROR, "encoding conversion length must not be negative"); +} + +/* + * report_invalid_encoding: complain about invalid multibyte character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_invalid_encoding(int encoding, const char *mbstr, int len) +{ + int l = pg_encoding_mblen(encoding, mbstr); + char buf[8 * 2 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + p += sprintf(p, "%02x", (unsigned char) mbstr[j]); + + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\": 0x%s", + pg_enc2name_tbl[encoding].name, + buf))); +} + +/* + * report_untranslatable_char: complain about untranslatable character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len) +{ + int l = pg_encoding_mblen(src_encoding, mbstr); + char buf[8 * 2 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + p += sprintf(p, "%02x", (unsigned char) mbstr[j]); + + ereport(ERROR, + (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), + errmsg("character 0x%s of encoding \"%s\" has no equivalent in \"%s\"", + buf, + pg_enc2name_tbl[src_encoding].name, + pg_enc2name_tbl[dest_encoding].name))); +} + +#endif diff --git a/src/libpq/win32.c b/src/libpq/win32.c new file mode 100644 index 00000000..d77fc48b --- /dev/null +++ b/src/libpq/win32.c @@ -0,0 +1,332 @@ +/* + * src/interfaces/libpq/win32.c + * + * + * FILE + * win32.c + * + * DESCRIPTION + * Win32 support functions. + * + * Contains table and functions for looking up win32 socket error + * descriptions. But will/may contain other win32 helper functions + * for libpq. + * + * The error constants are taken from the Frambak Bakfram LGSOCKET + * library guys who in turn took them from the Winsock FAQ. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + +/* Make stuff compile faster by excluding not used stuff */ + +#define VC_EXTRALEAN +#ifndef __MINGW32__ +#define NOGDI +#endif +#define NOCRYPT + +#include "postgres_fe.h" + +#include "win32.h" + +/* Declared here to avoid pulling in all includes, which causes name collissions */ +#ifdef ENABLE_NLS +extern char * +libpq_gettext(const char *msgid) +__attribute__((format_arg(1))); +#else +#define libpq_gettext(x) (x) +#endif + + +static struct WSErrorEntry +{ + DWORD error; + const char *description; +} WSErrors[] = + +{ + { + 0, "No error" + }, + { + WSAEINTR, "Interrupted system call" + }, + { + WSAEBADF, "Bad file number" + }, + { + WSAEACCES, "Permission denied" + }, + { + WSAEFAULT, "Bad address" + }, + { + WSAEINVAL, "Invalid argument" + }, + { + WSAEMFILE, "Too many open sockets" + }, + { + WSAEWOULDBLOCK, "Operation would block" + }, + { + WSAEINPROGRESS, "Operation now in progress" + }, + { + WSAEALREADY, "Operation already in progress" + }, + { + WSAENOTSOCK, "Socket operation on non-socket" + }, + { + WSAEDESTADDRREQ, "Destination address required" + }, + { + WSAEMSGSIZE, "Message too long" + }, + { + WSAEPROTOTYPE, "Protocol wrong type for socket" + }, + { + WSAENOPROTOOPT, "Bad protocol option" + }, + { + WSAEPROTONOSUPPORT, "Protocol not supported" + }, + { + WSAESOCKTNOSUPPORT, "Socket type not supported" + }, + { + WSAEOPNOTSUPP, "Operation not supported on socket" + }, + { + WSAEPFNOSUPPORT, "Protocol family not supported" + }, + { + WSAEAFNOSUPPORT, "Address family not supported" + }, + { + WSAEADDRINUSE, "Address already in use" + }, + { + WSAEADDRNOTAVAIL, "Cannot assign requested address" + }, + { + WSAENETDOWN, "Network is down" + }, + { + WSAENETUNREACH, "Network is unreachable" + }, + { + WSAENETRESET, "Net connection reset" + }, + { + WSAECONNABORTED, "Software caused connection abort" + }, + { + WSAECONNRESET, "Connection reset by peer" + }, + { + WSAENOBUFS, "No buffer space available" + }, + { + WSAEISCONN, "Socket is already connected" + }, + { + WSAENOTCONN, "Socket is not connected" + }, + { + WSAESHUTDOWN, "Cannot send after socket shutdown" + }, + { + WSAETOOMANYREFS, "Too many references, cannot splice" + }, + { + WSAETIMEDOUT, "Connection timed out" + }, + { + WSAECONNREFUSED, "Connection refused" + }, + { + WSAELOOP, "Too many levels of symbolic links" + }, + { + WSAENAMETOOLONG, "File name too long" + }, + { + WSAEHOSTDOWN, "Host is down" + }, + { + WSAEHOSTUNREACH, "No route to host" + }, + { + WSAENOTEMPTY, "Directory not empty" + }, + { + WSAEPROCLIM, "Too many processes" + }, + { + WSAEUSERS, "Too many users" + }, + { + WSAEDQUOT, "Disc quota exceeded" + }, + { + WSAESTALE, "Stale NFS file handle" + }, + { + WSAEREMOTE, "Too many levels of remote in path" + }, + { + WSASYSNOTREADY, "Network system is unavailable" + }, + { + WSAVERNOTSUPPORTED, "Winsock version out of range" + }, + { + WSANOTINITIALISED, "WSAStartup not yet called" + }, + { + WSAEDISCON, "Graceful shutdown in progress" + }, + { + WSAHOST_NOT_FOUND, "Host not found" + }, + { + WSATRY_AGAIN, "NA Host not found / SERVFAIL" + }, + { + WSANO_RECOVERY, "Non recoverable FORMERR||REFUSED||NOTIMP" + }, + { + WSANO_DATA, "No host data of that type was found" + }, + { + 0, 0 + } /* End of table */ +}; + + +/* + * Returns 0 if not found, linear but who cares, at this moment + * we're already in pain :) + */ + +static int +LookupWSErrorMessage(DWORD err, char *dest) +{ + struct WSErrorEntry *e; + + for (e = WSErrors; e->description; e++) + { + if (e->error == err) + { + strcpy(dest, e->description); + return 1; + } + } + return 0; +} + + +struct MessageDLL +{ + const char *dll_name; + void *handle; + int loaded; /* BOOL */ +} dlls[] = + +{ + { + "netmsg.dll", 0, 0 + }, + { + "winsock.dll", 0, 0 + }, + { + "wsock32.dll", 0, 0 + }, + { + "ws2_32.dll", 0, 0 + }, + { + "wsock32n.dll", 0, 0 + }, + { + "mswsock.dll", 0, 0 + }, + { + "ws2help.dll", 0, 0 + }, + { + "ws2thk.dll", 0, 0 + }, + { + 0, 0, 1 + } /* Last one, no dll, always loaded */ +}; + +#define DLLS_SIZE (sizeof(dlls)/sizeof(struct MessageDLL)) + +/* + * Returns a description of the socket error by first trying + * to find it in the lookup table, and if that fails, tries + * to load any of the winsock dlls to find that message. + * The DLL thing works from Nt4 (spX ?) up, but some special + * versions of winsock might have this aswell (seen on Win98 SE + * special install) / Magnus Naeslund (mag@fbab.net) + * + */ + +const char * +winsock_strerror(int err, char *strerrbuf, size_t buflen) +{ + unsigned long flags; + int offs, + i; + int success = LookupWSErrorMessage(err, strerrbuf); + + for (i = 0; !success && i < DLLS_SIZE; i++) + { + + if (!dlls[i].loaded) + { + dlls[i].loaded = 1; /* Only load once */ + dlls[i].handle = (void *) LoadLibraryEx( + dlls[i].dll_name, + 0, + LOAD_LIBRARY_AS_DATAFILE); + } + + if (dlls[i].dll_name && !dlls[i].handle) + continue; /* Didn't load */ + + flags = FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS + | (dlls[i].handle ? FORMAT_MESSAGE_FROM_HMODULE : 0); + + success = 0 != FormatMessage( + flags, + dlls[i].handle, err, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + strerrbuf, buflen - 64, + 0 + ); + } + + if (!success) + sprintf(strerrbuf, libpq_gettext("Unknown socket error (0x%08X/%i)"), err, err); + else + { + strerrbuf[buflen - 1] = '\0'; + offs = strlen(strerrbuf); + if (offs > (int) buflen - 64) + offs = buflen - 64; + sprintf(strerrbuf + offs, " (0x%08X/%i)", err, err); + } + return strerrbuf; +} diff --git a/src/libpq/win32.h b/src/libpq/win32.h new file mode 100644 index 00000000..b65da9ad --- /dev/null +++ b/src/libpq/win32.h @@ -0,0 +1,34 @@ +/* + * src/interfaces/libpq/win32.h + */ +#ifndef __win32_h_included +#define __win32_h_included + +/* + * Some compatibility functions + */ +#ifdef __BORLANDC__ +#define _timeb timeb +#define _ftime(a) ftime(a) +#define _errno errno +#define popen(a,b) _popen(a,b) +#else +/* open provided elsewhere */ +#define close(a) _close(a) +#define read(a,b,c) _read(a,b,c) +#define write(a,b,c) _write(a,b,c) +#endif + +#undef EAGAIN /* doesn't apply on sockets */ +#undef EINTR +#define EINTR WSAEINTR +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNRESET WSAECONNRESET +#define EINPROGRESS WSAEINPROGRESS + +/* + * support for handling Windows Socket errors + */ +extern const char *winsock_strerror(int err, char *strerrbuf, size_t buflen); + +#endif diff --git a/src/libpq/win32.mak b/src/libpq/win32.mak new file mode 100644 index 00000000..70a741a5 --- /dev/null +++ b/src/libpq/win32.mak @@ -0,0 +1,344 @@ +# Makefile for Microsoft Visual C++ 7.1-8.0 + +# Will build a static library libpq(d).lib +# and a dynamic library libpq(d).dll with import library libpq(d)dll.lib +# USE_SSL=1 will compile with OpenSSL +# USE_KFW=1 will compile with kfw(kerberos for Windows) +# DEBUG=1 compiles with debugging symbols +# ENABLE_THREAD_SAFETY=1 compiles with threading enabled + +ENABLE_THREAD_SAFETY=1 + +# CPU="i386" or CPU environment of nmake.exe (AMD64 or IA64) + +!IF ("$(CPU)" == "")||("$(CPU)" == "i386") +CPU=i386 +!MESSAGE Building the Win32 static library... +!MESSAGE +!ELSEIF ("$(CPU)" == "IA64")||("$(CPU)" == "AMD64") +ADD_DEFINES=/D "WIN64" /Wp64 /GS +ADD_SECLIB=bufferoverflowU.lib +!MESSAGE Building the Win64 static library... +!MESSAGE +!ELSE +!MESSAGE Please check a CPU=$(CPU) ? +!MESSAGE CPU=i386 or AMD64 or IA64 +!ERROR Make aborted. +!ENDIF + +!IFDEF DEBUG +OPT=/Od /Zi /MDd +LOPT=/DEBUG +DEBUGDEF=/D _DEBUG +OUTFILENAME=libpqd +!ELSE +OPT=/O2 /MD +LOPT= +DEBUGDEF=/D NDEBUG +OUTFILENAME=libpq +!ENDIF + +!IF "$(SSL_INC)" == "" +SSL_INC=C:\OpenSSL\include +!MESSAGE Using default OpenSSL Include directory: $(SSL_INC) +!ENDIF + +!IF "$(SSL_LIB_PATH)" == "" +SSL_LIB_PATH=C:\OpenSSL\lib\VC +!MESSAGE Using default OpenSSL Library directory: $(SSL_LIB_PATH) +!ENDIF + +!IF "$(KFW_INC)" == "" +KFW_INC=C:\kfw-2.6.5\inc +!MESSAGE Using default Kerberos Include directory: $(KFW_INC) +!ENDIF + +!IF "$(KFW_LIB_PATH)" == "" +KFW_LIB_PATH=C:\kfw-2.6.5\lib\$(CPU) +!MESSAGE Using default Kerberos Library directory: $(KFW_LIB_PATH) +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +CPP=cl.exe +RSC=rc.exe + +!IFDEF DEBUG +OUTDIR=.\Debug +INTDIR=.\Debug +CPP_OBJS=.\Debug/ +!ELSE +OUTDIR=.\Release +INTDIR=.\Release +CPP_OBJS=.\Release/ +!ENDIF + + +ALL : config "$(OUTDIR)\$(OUTFILENAME).lib" "$(OUTDIR)\$(OUTFILENAME).dll" + +CLEAN : + -@erase "$(INTDIR)\getaddrinfo.obj" + -@erase "$(INTDIR)\pgstrcasecmp.obj" + -@erase "$(INTDIR)\thread.obj" + -@erase "$(INTDIR)\inet_aton.obj" + -@erase "$(INTDIR)\crypt.obj" + -@erase "$(INTDIR)\noblock.obj" + -@erase "$(INTDIR)\chklocale.obj" + -@erase "$(INTDIR)\inet_net_ntop.obj" + -@erase "$(INTDIR)\md5.obj" + -@erase "$(INTDIR)\ip.obj" + -@erase "$(INTDIR)\fe-auth.obj" + -@erase "$(INTDIR)\fe-protocol2.obj" + -@erase "$(INTDIR)\fe-protocol3.obj" + -@erase "$(INTDIR)\fe-connect.obj" + -@erase "$(INTDIR)\fe-exec.obj" + -@erase "$(INTDIR)\fe-lobj.obj" + -@erase "$(INTDIR)\fe-misc.obj" + -@erase "$(INTDIR)\fe-print.obj" + -@erase "$(INTDIR)\fe-secure.obj" + -@erase "$(INTDIR)\libpq-events.obj" + -@erase "$(INTDIR)\pqexpbuffer.obj" + -@erase "$(INTDIR)\pqsignal.obj" + -@erase "$(INTDIR)\win32.obj" + -@erase "$(INTDIR)\wchar.obj" + -@erase "$(INTDIR)\encnames.obj" + -@erase "$(INTDIR)\pthread-win32.obj" + -@erase "$(INTDIR)\snprintf.obj" + -@erase "$(INTDIR)\strlcpy.obj" + -@erase "$(INTDIR)\dirent.obj" + -@erase "$(INTDIR)\dirmod.obj" + -@erase "$(INTDIR)\pgsleep.obj" + -@erase "$(INTDIR)\open.obj" + -@erase "$(INTDIR)\win32error.obj" + -@erase "$(INTDIR)\win32setlocale.obj" + -@erase "$(OUTDIR)\$(OUTFILENAME).lib" + -@erase "$(OUTDIR)\$(OUTFILENAME)dll.lib" + -@erase "$(OUTDIR)\libpq.res" + -@erase "$(OUTDIR)\$(OUTFILENAME).dll" + -@erase "$(OUTDIR)\$(OUTFILENAME)dll.exp" + -@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest" + -@erase "$(OUTDIR)\*.idb" + -@erase pg_config_paths.h" + + +LIB32=link.exe -lib +LIB32_FLAGS=$(LOPT) /nologo /out:"$(OUTDIR)\$(OUTFILENAME).lib" +LIB32_OBJS= \ + "$(INTDIR)\win32.obj" \ + "$(INTDIR)\getaddrinfo.obj" \ + "$(INTDIR)\pgstrcasecmp.obj" \ + "$(INTDIR)\thread.obj" \ + "$(INTDIR)\inet_aton.obj" \ + "$(INTDIR)\crypt.obj" \ + "$(INTDIR)\noblock.obj" \ + "$(INTDIR)\chklocale.obj" \ + "$(INTDIR)\inet_net_ntop.obj" \ + "$(INTDIR)\md5.obj" \ + "$(INTDIR)\ip.obj" \ + "$(INTDIR)\fe-auth.obj" \ + "$(INTDIR)\fe-protocol2.obj" \ + "$(INTDIR)\fe-protocol3.obj" \ + "$(INTDIR)\fe-connect.obj" \ + "$(INTDIR)\fe-exec.obj" \ + "$(INTDIR)\fe-lobj.obj" \ + "$(INTDIR)\fe-misc.obj" \ + "$(INTDIR)\fe-print.obj" \ + "$(INTDIR)\fe-secure.obj" \ + "$(INTDIR)\libpq-events.obj" \ + "$(INTDIR)\pqexpbuffer.obj" \ + "$(INTDIR)\pqsignal.obj" \ + "$(INTDIR)\wchar.obj" \ + "$(INTDIR)\encnames.obj" \ + "$(INTDIR)\snprintf.obj" \ + "$(INTDIR)\strlcpy.obj" \ + "$(INTDIR)\dirent.obj" \ + "$(INTDIR)\dirmod.obj" \ + "$(INTDIR)\pgsleep.obj" \ + "$(INTDIR)\open.obj" \ + "$(INTDIR)\win32error.obj" \ + "$(INTDIR)\win32setlocale.obj" \ + "$(INTDIR)\pthread-win32.obj" + + +config: ..\..\include\pg_config.h pg_config_paths.h ..\..\include\pg_config_os.h + +..\..\include\pg_config.h: ..\..\include\pg_config.h.win32 + copy ..\..\include\pg_config.h.win32 ..\..\include\pg_config.h + +..\..\include\pg_config_os.h: + copy ..\..\include\port\win32.h ..\..\include\pg_config_os.h + +pg_config_paths.h: win32.mak + echo #define SYSCONFDIR "" > pg_config_paths.h + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win32" /I "..\..\include\port\win32_msvc" /I "..\..\port" /I. /I "$(SSL_INC)" \ + /D "FRONTEND" $(DEBUGDEF) \ + /D "WIN32" /D "_WINDOWS" /Fp"$(INTDIR)\libpq.pch" \ + /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c \ + /D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES) + +!IFDEF USE_SSL +CPP_PROJ=$(CPP_PROJ) /D USE_SSL +SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib +!ENDIF + +!IFDEF USE_KFW +CPP_PROJ=$(CPP_PROJ) /D KRB5 +KFW_LIBS=krb5_32.lib comerr32.lib gssapi32.lib +!ENDIF + +!IFDEF ENABLE_THREAD_SAFETY +CPP_PROJ=$(CPP_PROJ) /D ENABLE_THREAD_SAFETY +!ENDIF + +CPP_SBRS=. + +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\libpq.res" + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib advapi32.lib shfolder.lib wsock32.lib ws2_32.lib secur32.lib $(SSL_LIBS) $(KFW_LIB) $(ADD_SECLIB) \ + /nologo /subsystem:windows /dll $(LOPT) /incremental:no \ + /pdb:"$(OUTDIR)\libpqdll.pdb" /machine:$(CPU) \ + /out:"$(OUTDIR)\$(OUTFILENAME).dll"\ + /implib:"$(OUTDIR)\$(OUTFILENAME)dll.lib" \ + /libpath:"$(SSL_LIB_PATH)" /libpath:"$(KFW_LIB_PATH)" \ + /def:$(OUTFILENAME)dll.def +LINK32_OBJS= \ + "$(OUTDIR)\$(OUTFILENAME).lib" \ + "$(OUTDIR)\libpq.res" + +# @<< is a Response file, http://www.opussoftware.com/tutorial/TutMakefile.htm + +"$(OUTDIR)\$(OUTFILENAME).lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +"$(INTDIR)\libpq.res" : "$(INTDIR)" libpq-dist.rc + $(RSC) $(RSC_PROJ) libpq-dist.rc + + +"$(OUTDIR)\$(OUTFILENAME).dll" : "$(OUTDIR)" "$(INTDIR)\libpq.res" + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< +# Inclusion of manifest +!IF "$(_NMAKE_VER)" != "6.00.8168.0" && "$(_NMAKE_VER)" != "7.00.9466" +!IF "$(_NMAKE_VER)" != "6.00.9782.0" && "$(_NMAKE_VER)" != "7.10.3077" + mt -manifest $(OUTDIR)\$(OUTFILENAME).dll.manifest -outputresource:$(OUTDIR)\$(OUTFILENAME).dll;2 +!ENDIF +!ENDIF + +"$(INTDIR)\getaddrinfo.obj" : ..\..\port\getaddrinfo.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\getaddrinfo.c +<< + +"$(INTDIR)\pgstrcasecmp.obj" : ..\..\port\pgstrcasecmp.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\pgstrcasecmp.c +<< + +"$(INTDIR)\thread.obj" : ..\..\port\thread.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\thread.c +<< + +"$(INTDIR)\inet_aton.obj" : ..\..\port\inet_aton.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\inet_aton.c +<< + +"$(INTDIR)\crypt.obj" : ..\..\port\crypt.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\crypt.c +<< + +"$(INTDIR)\noblock.obj" : ..\..\port\noblock.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\noblock.c +<< + +"$(INTDIR)\chklocale.obj" : ..\..\port\chklocale.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\chklocale.c +<< + +"$(INTDIR)\inet_net_ntop.obj" : ..\..\port\inet_net_ntop.c + $(CPP) @<< + $(CPP_PROJ) ..\..\port\inet_net_ntop.c +<< + +"$(INTDIR)\md5.obj" : ..\..\backend\libpq\md5.c + $(CPP) @<< + $(CPP_PROJ) ..\..\backend\libpq\md5.c +<< + +"$(INTDIR)\ip.obj" : ..\..\backend\libpq\ip.c + $(CPP) @<< + $(CPP_PROJ) ..\..\backend\libpq\ip.c +<< + +"$(INTDIR)\wchar.obj" : ..\..\backend\utils\mb\wchar.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\backend\utils\mb\wchar.c +<< + + +"$(INTDIR)\encnames.obj" : ..\..\backend\utils\mb\encnames.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\backend\utils\mb\encnames.c +<< + +"$(INTDIR)\snprintf.obj" : ..\..\port\snprintf.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\snprintf.c +<< + +"$(INTDIR)\strlcpy.obj" : ..\..\port\strlcpy.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\strlcpy.c +<< + +"$(INTDIR)\dirent.obj" : ..\..\port\dirent.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\dirent.c +<< + +"$(INTDIR)\dirmod.obj" : ..\..\port\dirmod.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\dirmod.c +<< + +"$(INTDIR)\pgsleep.obj" : ..\..\port\pgsleep.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\pgsleep.c +<< + +"$(INTDIR)\open.obj" : ..\..\port\open.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\open.c +<< + +"$(INTDIR)\win32error.obj" : ..\..\port\win32error.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\win32error.c +<< + +"$(INTDIR)\win32setlocale.obj" : ..\..\port\win32setlocale.c + $(CPP) @<< + $(CPP_PROJ) /I"." ..\..\port\win32setlocale.c +<< + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c.obj: + $(CPP) $(CPP_PROJ) $< diff --git a/src/libpq/win32error.c b/src/libpq/win32error.c new file mode 100644 index 00000000..4b3b1ea7 --- /dev/null +++ b/src/libpq/win32error.c @@ -0,0 +1,204 @@ +/*------------------------------------------------------------------------- + * + * win32error.c + * Map win32 error codes to errno values + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32error.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +static const struct +{ + DWORD winerr; + int doserr; +} doserrors[] = + +{ + { + ERROR_INVALID_FUNCTION, EINVAL + }, + { + ERROR_FILE_NOT_FOUND, ENOENT + }, + { + ERROR_PATH_NOT_FOUND, ENOENT + }, + { + ERROR_TOO_MANY_OPEN_FILES, EMFILE + }, + { + ERROR_ACCESS_DENIED, EACCES + }, + { + ERROR_INVALID_HANDLE, EBADF + }, + { + ERROR_ARENA_TRASHED, ENOMEM + }, + { + ERROR_NOT_ENOUGH_MEMORY, ENOMEM + }, + { + ERROR_INVALID_BLOCK, ENOMEM + }, + { + ERROR_BAD_ENVIRONMENT, E2BIG + }, + { + ERROR_BAD_FORMAT, ENOEXEC + }, + { + ERROR_INVALID_ACCESS, EINVAL + }, + { + ERROR_INVALID_DATA, EINVAL + }, + { + ERROR_INVALID_DRIVE, ENOENT + }, + { + ERROR_CURRENT_DIRECTORY, EACCES + }, + { + ERROR_NOT_SAME_DEVICE, EXDEV + }, + { + ERROR_NO_MORE_FILES, ENOENT + }, + { + ERROR_LOCK_VIOLATION, EACCES + }, + { + ERROR_SHARING_VIOLATION, EACCES + }, + { + ERROR_BAD_NETPATH, ENOENT + }, + { + ERROR_NETWORK_ACCESS_DENIED, EACCES + }, + { + ERROR_BAD_NET_NAME, ENOENT + }, + { + ERROR_FILE_EXISTS, EEXIST + }, + { + ERROR_CANNOT_MAKE, EACCES + }, + { + ERROR_FAIL_I24, EACCES + }, + { + ERROR_INVALID_PARAMETER, EINVAL + }, + { + ERROR_NO_PROC_SLOTS, EAGAIN + }, + { + ERROR_DRIVE_LOCKED, EACCES + }, + { + ERROR_BROKEN_PIPE, EPIPE + }, + { + ERROR_DISK_FULL, ENOSPC + }, + { + ERROR_INVALID_TARGET_HANDLE, EBADF + }, + { + ERROR_INVALID_HANDLE, EINVAL + }, + { + ERROR_WAIT_NO_CHILDREN, ECHILD + }, + { + ERROR_CHILD_NOT_COMPLETE, ECHILD + }, + { + ERROR_DIRECT_ACCESS_HANDLE, EBADF + }, + { + ERROR_NEGATIVE_SEEK, EINVAL + }, + { + ERROR_SEEK_ON_DEVICE, EACCES + }, + { + ERROR_DIR_NOT_EMPTY, ENOTEMPTY + }, + { + ERROR_NOT_LOCKED, EACCES + }, + { + ERROR_BAD_PATHNAME, ENOENT + }, + { + ERROR_MAX_THRDS_REACHED, EAGAIN + }, + { + ERROR_LOCK_FAILED, EACCES + }, + { + ERROR_ALREADY_EXISTS, EEXIST + }, + { + ERROR_FILENAME_EXCED_RANGE, ENOENT + }, + { + ERROR_NESTING_NOT_ALLOWED, EAGAIN + }, + { + ERROR_NOT_ENOUGH_QUOTA, ENOMEM + } +}; + +void +_dosmaperr(unsigned long e) +{ + int i; + + if (e == 0) + { + errno = 0; + return; + } + + for (i = 0; i < lengthof(doserrors); i++) + { + if (doserrors[i].winerr == e) + { + errno = doserrors[i].doserr; +#ifndef FRONTEND + ereport(DEBUG5, + (errmsg_internal("mapped win32 error code %lu to %d", + e, errno))); +#elif FRONTEND_DEBUG + fprintf(stderr, _("mapped win32 error code %lu to %d"), e, errno); +#endif + return; + } + } + +#ifndef FRONTEND + ereport(LOG, + (errmsg_internal("unrecognized win32 error code: %lu", + e))); +#else + fprintf(stderr, _("unrecognized win32 error code: %lu"), e); +#endif + + errno = EINVAL; + return; +} diff --git a/src/libpq/win32setlocale.c b/src/libpq/win32setlocale.c new file mode 100644 index 00000000..a301e769 --- /dev/null +++ b/src/libpq/win32setlocale.c @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- + * + * win32setlocale.c + * Wrapper to work around bugs in Windows setlocale() implementation + * + * Copyright (c) 2011, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32setlocale.c + * + * + * Windows has a problem with locale names that have a dot in the country + * name. For example: + * + * "Chinese (Traditional)_Hong Kong S.A.R..950" + * + * For some reason, setlocale() doesn't accept that. Fortunately, Windows' + * setlocale() accepts various alternative names for such countries, so we + * provide a wrapper setlocale() function that maps the troublemaking locale + * names to accepted aliases. + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#undef setlocale + +struct locale_map +{ + const char *locale_name_part; /* string in locale name to replace */ + const char *replacement; /* string to replace it with */ +}; + +static const struct locale_map locale_map_list[] = { + + /* + * "HKG" is listed here: + * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx + * (Country/Region Strings). + * + * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the + * above list, but seems to work anyway. + */ + { "Hong Kong S.A.R.", "HKG" }, + { "U.A.E.", "ARE" }, + + /* + * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't + * seem to recognize that. And Macau isn't listed in the table of + * accepted abbreviations linked above. Fortunately, "ZHM" seems to be + * accepted as an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm + * not sure where "ZHM" comes from, must be some legacy naming scheme. But + * hey, it works. + * + * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale + * name, not just the country part. + * + * Some versions of Windows spell it "Macau", others "Macao". + */ + { "Chinese (Traditional)_Macau S.A.R..950", "ZHM" }, + { "Chinese_Macau S.A.R..950", "ZHM" }, + { "Chinese (Traditional)_Macao S.A.R..950", "ZHM" }, + { "Chinese_Macao S.A.R..950", "ZHM" } +}; + +char * +pgwin32_setlocale(int category, const char *locale) +{ + char *result; + char *alias; + int i; + + if (locale == NULL) + return setlocale(category, locale); + + /* Check if the locale name matches any of the problematic ones. */ + alias = NULL; + for (i = 0; i < lengthof(locale_map_list); i++) + { + const char *needle = locale_map_list[i].locale_name_part; + const char *replacement = locale_map_list[i].replacement; + char *match; + + match = strstr(locale, needle); + if (match != NULL) + { + /* Found a match. Replace the matched string. */ + int matchpos = match - locale; + int replacementlen = strlen(replacement); + char *rest = match + strlen(needle); + int restlen = strlen(rest); + + alias = malloc(matchpos + replacementlen + restlen + 1); + if (!alias) + return NULL; + + memcpy(&alias[0], &locale[0], matchpos); + memcpy(&alias[matchpos], replacement, replacementlen); + memcpy(&alias[matchpos + replacementlen], rest, restlen + 1); /* includes null terminator */ + + break; + } + } + + /* Call the real setlocale() function */ + if (alias) + { + result = setlocale(category, alias); + free(alias); + } + else + result = setlocale(category, locale); + + return result; +} diff --git a/src/libpq/wininclude/arpa/inet.h b/src/libpq/wininclude/arpa/inet.h new file mode 100644 index 00000000..ad180317 --- /dev/null +++ b/src/libpq/wininclude/arpa/inet.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/arpa/inet.h */ + +#include diff --git a/src/libpq/wininclude/netdb.h b/src/libpq/wininclude/netdb.h new file mode 100644 index 00000000..ad0627e9 --- /dev/null +++ b/src/libpq/wininclude/netdb.h @@ -0,0 +1 @@ +/* src/include/port/win32/netdb.h */ diff --git a/src/libpq/wininclude/netinet/in.h b/src/libpq/wininclude/netinet/in.h new file mode 100644 index 00000000..a4e22f89 --- /dev/null +++ b/src/libpq/wininclude/netinet/in.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/netinet/in.h */ + +#include diff --git a/src/libpq/wininclude/pwd.h b/src/libpq/wininclude/pwd.h new file mode 100644 index 00000000..b8c7178f --- /dev/null +++ b/src/libpq/wininclude/pwd.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/pwd.h + */ diff --git a/src/libpq/wininclude/sys/socket.h b/src/libpq/wininclude/sys/socket.h new file mode 100644 index 00000000..edaee6a8 --- /dev/null +++ b/src/libpq/wininclude/sys/socket.h @@ -0,0 +1,33 @@ +/* + * src/include/port/win32/sys/socket.h + */ +#ifndef WIN32_SYS_SOCKET_H +#define WIN32_SYS_SOCKET_H + +/* + * Unfortunately, of VC++ also defines ERROR. + * To avoid the conflict, we include here and undefine ERROR + * immediately. + * + * Note: Don't include directly. It causes compile errors. + */ +#include +#include +#include + +#undef ERROR +#undef small + +/* Restore old ERROR value */ +#ifdef PGERROR +#define ERROR PGERROR +#endif + +/* + * we can't use the windows gai_strerror{AW} functions because + * they are defined inline in the MS header files. So we'll use our + * own + */ +#undef gai_strerror + +#endif /* WIN32_SYS_SOCKET_H */ diff --git a/src/libpq/wininclude/sys/wait.h b/src/libpq/wininclude/sys/wait.h new file mode 100644 index 00000000..eaeb5661 --- /dev/null +++ b/src/libpq/wininclude/sys/wait.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/sys/wait.h + */