text: ''' CFFI interface to libpq the library '''
  
  from __future__ import print_function
  
  import os.path
  import re
  import sys
  import subprocess
  
  from cffi import FFI
  
  
  PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
  
  
  class PostgresConfig:
  
      def __init__(self):
          try:
              from psycopg2cffi import _config
          except ImportError:
              self.pg_config_exe = None
              if not self.pg_config_exe:
                  self.pg_config_exe = self.autodetect_pg_config_path()
              if self.pg_config_exe is None:
                  # FIXME - do we need some way to set it?
                  sys.stderr.write("""\
  Error: pg_config executable not found.
  Please add the directory containing pg_config to the PATH.
  """)
                  sys.exit(1)
              self.libpq_path = self.find_libpq()
              self.libpq_version = self.find_version()
              self.libpq_include_dir = self.query('includedir') or None
          else:
              self.libpq_path = _config.PG_LIBRARY
              self.libpq_version = _config.PG_VERSION
              self.libpq_include_dir = _config.PG_INCLUDE_DIR
  
      def query(self, attr_name):
          """Spawn the pg_config executable, querying for the given config
          name, and return the printed value, sanitized. """
          try:
              pg_config_process = subprocess.Popen(
                  [self.pg_config_exe, "--" + attr_name],
                  stdin=subprocess.PIPE,
                  stdout=subprocess.PIPE,
                  stderr=subprocess.PIPE)
          except OSError:
              raise Warning("Unable to find 'pg_config' file in '%s'" %
                            self.pg_config_exe)
          pg_config_process.stdin.close()
          result = pg_config_process.stdout.readline().strip()
          if not result:
              raise Warning(pg_config_process.stderr.readline())
          if not isinstance(result, str):
              result = result.decode('ascii')
          return result
  
      def find_on_path(self, exename, path_directories=None):
          if not path_directories:
              path_directories = os.environ['PATH'].split(os.pathsep)
          for dir_name in path_directories:
              fullpath = os.path.join(dir_name, exename)
              if os.path.isfile(fullpath):
                  return fullpath
          return None
  
      def autodetect_pg_config_path(self):
          """Find and return the path to the pg_config executable."""
          if PLATFORM_IS_WINDOWS:
              return self.autodetect_pg_config_path_windows()
          else:
              return self.find_on_path('pg_config')
  
      def autodetect_pg_config_path_windows(self):
          """Attempt several different ways of finding the pg_config
          executable on Windows, and return its full path, if found."""
  
          # This code only runs if they have not specified a pg_config option
          # in the config file or via the commandline.
  
          # First, check for pg_config.exe on the PATH, and use that if found.
          pg_config_exe = self.find_on_path('pg_config.exe')
          if pg_config_exe:
              return pg_config_exe
  
          # Now, try looking in the Windows Registry to find a PostgreSQL
          # installation, and infer the path from that.
          pg_config_exe = self._get_pg_config_from_registry()
          if pg_config_exe:
              return pg_config_exe
  
          return None
  
      def _get_pg_config_from_registry(self):
          try:
              import winreg
          except ImportError:
              import _winreg as winreg
  
          reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
          try:
              pg_inst_list_key = winreg.OpenKey(reg,
                  'SOFTWARE\\PostgreSQL\\Installations')
          except EnvironmentError:
              # No PostgreSQL installation, as best as we can tell.
              return None
  
          try:
              # Determine the name of the first subkey, if any:
              try:
                  first_sub_key_name = winreg.EnumKey(pg_inst_list_key, 0)
              except EnvironmentError:
                  return None
  
              pg_first_inst_key = winreg.OpenKey(reg,
                  'SOFTWARE\\PostgreSQL\\Installations\\'
                  + first_sub_key_name)
              try:
                  pg_inst_base_dir = winreg.QueryValueEx(
                      pg_first_inst_key, 'Base Directory')[0]
              finally:
                  winreg.CloseKey(pg_first_inst_key)
  
          finally:
              winreg.CloseKey(pg_inst_list_key)
  
          pg_config_path = os.path.join(
              pg_inst_base_dir, 'bin', 'pg_config.exe')
          if not os.path.exists(pg_config_path):
              return None
  
          # Support unicode paths, if this version of Python provides the
          # necessary infrastructure:
          if sys.version_info[0] < 3 \
                  and hasattr(sys, 'getfilesystemencoding'):
              pg_config_path = pg_config_path.encode(
                  sys.getfilesystemencoding())
  
          return pg_config_path
  
      def find_version(self):
          try:
              # Here we take a conservative approach: we suppose that
              # *at least* PostgreSQL 7.4 is available (this is the only
              # 7.x series supported by psycopg 2)
              pgversion = self.query('version').split()[1]
          except:
              pgversion = '7.4.0'
  
          verre = re.compile(
              r'(\d+)\.(\d+)(?:(?:\.(\d+))|(devel|(alpha|beta|rc)\d+))')
          m = verre.match(pgversion)
          if m:
              pgmajor, pgminor, pgpatch = m.group(1, 2, 3)
              if pgpatch is None or not pgpatch.isdigit():
                  pgpatch = 0
          else:
              sys.stderr.write(
                  "Error: could not determine PostgreSQL version from '%s'"
                  % pgversion)
              sys.exit(1)
  
          return int(
              '%02X%02X%02X' % (int(pgmajor), int(pgminor), int(pgpatch)), 16)
  
      def find_libpq(self):
          path = self.query('libdir')
          fname = None
          if os.name == 'posix':
              if sys.platform == 'darwin':
                  fname = os.path.join(path, 'libpq.dylib')
              if sys.platform in ['linux', 'linux2', 'linux3']:
                  fname = os.path.join(path, 'libpq.so')
              if sys.platform.startswith('freebsd'):
                  fname = os.path.join(path, 'libpq.so')
          if fname:
              print()
              print('=' * 80)
              print()
              print('Found libpq at:')
              print(' -> %s' %  fname)
              print()
              print('=' * 80)
              return fname
          else:
              import ctypes.util
              fname = ctypes.util.find_library('pq')
              print()
              print('=' * 80)
              print()
              print('Unable to find the libpq for your platform in:')
              print(' -> %s' %  path)
              print()
              print('Ignoring pg_config, trying ctypes.util.find_library()')
              if fname:
                  print(' -> OK (%s)' % fname)
              else:
                  print(' -> FAILED')
              print()
              print('=' * 80)
              if not fname:
                  sys.exit(1)
              return fname
  
  
  _config = PostgresConfig()
  
  ffi = FFI()
  
  # order and comments taken from libpq (ctypes impl)
  
  ffi.cdef('''
  
  static int const _PG_VERSION;
  
  // postgres_ext.h
  
  typedef unsigned int Oid;
  
  // See comment below.
  
  static int const LIBPQ_DIAG_SEVERITY;
  static int const LIBPQ_DIAG_SQLSTATE;
  static int const LIBPQ_DIAG_MESSAGE_PRIMARY;
  static int const LIBPQ_DIAG_MESSAGE_DETAIL;
  static int const LIBPQ_DIAG_MESSAGE_HINT;
  static int const LIBPQ_DIAG_STATEMENT_POSITION;
  static int const LIBPQ_DIAG_INTERNAL_POSITION;
  static int const LIBPQ_DIAG_INTERNAL_QUERY;
  static int const LIBPQ_DIAG_CONTEXT;
  static int const LIBPQ_DIAG_SOURCE_FILE;
  static int const LIBPQ_DIAG_SCHEMA_NAME;
  static int const LIBPQ_DIAG_TABLE_NAME;
  static int const LIBPQ_DIAG_COLUMN_NAME;
  static int const LIBPQ_DIAG_DATATYPE_NAME ;
  static int const LIBPQ_DIAG_CONSTRAINT_NAME;
  static int const LIBPQ_DIAG_SOURCE_LINE;
  static int const LIBPQ_DIAG_SOURCE_FUNCTION;
  
  // libpq-fe.h
  
  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 */
  } 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 ... PGconn;
  typedef ... PGresult;
  typedef ... PGcancel;
  
  typedef struct pgNotify
  {
      char       *relname;  /* notification condition name */
      int        be_pid;    /* process ID of notifying server process */
      char       *extra;    /* notification parameter */
      ...;
  } PGnotify;
  
  // Database connection control functions
  
  extern PGconn *PQconnectdb(const char *conninfo);
  extern PGconn *PQconnectStart(const char *conninfo);
  extern /*PostgresPollingStatusType*/ int PQconnectPoll(PGconn *conn);
  extern void PQfinish(PGconn *conn);
  
  // Connection status functions
  
  extern /*ConnStatusType*/ int PQstatus(const PGconn *conn);
  extern /*PGTransactionStatusType*/ int 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);
  
  // Command execution functions
  
  extern PGresult *PQexec(PGconn *conn, const char *query);
  extern /*ExecStatusType*/ int PQresultStatus(const PGresult *res);
  extern char *PQresultErrorMessage(const PGresult *res);
  extern char *PQresultErrorField(const PGresult *res, int fieldcode);
  extern void PQclear(PGresult *res);
  
  // Retrieving query result information
  
  extern int PQntuples(const PGresult *res);
  extern int PQnfields(const PGresult *res);
  extern char *PQfname(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 int PQgetisnull(const PGresult *res, int tup_num, int field_num);
  extern int PQgetlength(const PGresult *res, int tup_num, int field_num);
  extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num);
  
  // direct parsers - not part of libpq
  
  int64_t PQEgetlong(const PGresult *res, int tup_num, int field_num);
  int32_t PQEgetint(const PGresult *res, int tup_num, int field_num);
  float PQEgetfloat(const PGresult *res, int tup_num, int field_num);
  double PQEgetdouble(const PGresult *res, int tup_num, int field_num);
  
  // Retrieving other result information
  
  extern char *PQcmdStatus(PGresult *res);
  extern char *PQcmdTuples(PGresult *res);
  extern Oid PQoidValue(const PGresult *res); /* new and improved */
  
  ''')
  
  if _config.libpq_version >= 0x090000:
      ffi.cdef('''
  // Escaping string for inclusion in sql commands
  extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len);
      ''')
  
  ffi.cdef('''
  // Escaping string for inclusion in sql commands
  extern size_t PQescapeStringConn(PGconn *conn,
      char *to, const char *from, size_t length,
      int *error);
  extern size_t PQescapeString(char *to, const char *from, size_t length);
  extern unsigned char *PQescapeByteaConn(PGconn *conn,
      const unsigned char *from, size_t from_length,
      size_t *to_length);
  extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length,
      size_t *to_length);
  extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
      size_t *retbuflen);
  
  // Asynchronous Command Processing
  
  extern int PQsendQuery(PGconn *conn, const char *query);
  extern PGresult *PQgetResult(PGconn *conn);
  extern int PQconsumeInput(PGconn *conn);
  extern int PQisBusy(PGconn *conn);
  extern int PQsetnonblocking(PGconn *conn, int arg);
  extern int PQflush(PGconn *conn);
  
  // Cancelling queries in progress
  
  extern PGcancel *PQgetCancel(PGconn *conn);
  extern void PQfreeCancel(PGcancel *cancel);
  extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
  extern int PQrequestCancel(PGconn *conn);
  
  // Functions Associated with the COPY Command
  
  extern int PQgetCopyData(PGconn *conn, char **buffer, int async);
  extern int PQputCopyEnd(PGconn *conn, const char *errormsg);
  extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes);
  
  // Miscellaneous functions
  
  extern void PQfreemem(void *ptr);
  
  // Notice processing
  
  typedef void (*PQnoticeProcessor) (void *arg, const char *message);
  extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
      PQnoticeProcessor proc,
      void *arg);
  extern PGnotify *PQnotifies(PGconn *conn);
  
  // Large object
  extern int lo_open(PGconn *conn, Oid lobjId, int mode);
  extern Oid lo_create(PGconn *conn, Oid lobjId);
  extern Oid lo_import(PGconn *conn, const char *filename);
  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_tell(PGconn *conn, int fd);
  extern int lo_lseek(PGconn *conn, int fd, int offset, int whence);
  extern int lo_close(PGconn *conn, int fd);
  extern int lo_unlink(PGconn *conn, Oid lobjId);
  extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
  extern int lo_truncate(PGconn *conn, int fd, size_t len);
  
  ''')
  
  
  C_SOURCE = '''
  #include <stdint.h>
  #include <postgres_ext.h>
  #include <libpq-fe.h>
  
  int64_t PQEgetlong(const PGresult *res, int tup_num, int field_num) {
      long raw_res;
      char *val = PQgetvalue(res, tup_num, field_num);
      sscanf(val, "%ld", &raw_res);
      return raw_res;
  }
  
  int32_t PQEgetint(const PGresult *res, int tup_num, int field_num) {
      int raw_res;
      char *val = PQgetvalue(res, tup_num, field_num);
      sscanf(val, "%d", &raw_res);
      return raw_res;
  }
  
  float PQEgetfloat(const PGresult *res, int tup_num, int field_num) {
      float raw_res = -1;
      char *val = PQgetvalue(res, tup_num, field_num);
      sscanf(val, "%f", &raw_res);
      return raw_res;
  }
  
  double PQEgetdouble(const PGresult *res, int tup_num, int field_num) {
      double raw_res = -1;
      char *val = PQgetvalue(res, tup_num, field_num);
      sscanf(val, "%lf", &raw_res);
      return raw_res;
  }
  
  // Real names start with PG_DIAG_, but here we define our prefixes,
  // because some are defined and some are not depending on pg version.
  
  static int const LIBPQ_DIAG_SEVERITY = 'S';
  static int const LIBPQ_DIAG_SQLSTATE = 'C';
  static int const LIBPQ_DIAG_MESSAGE_PRIMARY = 'M';
  static int const LIBPQ_DIAG_MESSAGE_DETAIL = 'D';
  static int const LIBPQ_DIAG_MESSAGE_HINT = 'H';
  static int const LIBPQ_DIAG_STATEMENT_POSITION = 'P';
  static int const LIBPQ_DIAG_INTERNAL_POSITION = 'p';
  static int const LIBPQ_DIAG_INTERNAL_QUERY = 'q';
  static int const LIBPQ_DIAG_CONTEXT = 'W';
  static int const LIBPQ_DIAG_SCHEMA_NAME = 's';
  static int const LIBPQ_DIAG_TABLE_NAME = 't';
  static int const LIBPQ_DIAG_COLUMN_NAME = 'c';
  static int const LIBPQ_DIAG_DATATYPE_NAME  = 'd';
  static int const LIBPQ_DIAG_CONSTRAINT_NAME = 'n';
  static int const LIBPQ_DIAG_SOURCE_FILE = 'F';
  static int const LIBPQ_DIAG_SOURCE_LINE = 'L';
  static int const LIBPQ_DIAG_SOURCE_FUNCTION = 'R';
  ''' + '''
  
  static int const _PG_VERSION = {libpq_version};
  '''.format(libpq_version=_config.libpq_version)
  
  
  _or_empty = lambda x: [x] if x else []
  
  
  C_SOURCE_KWARGS = dict(
      libraries=['pq'],
      library_dirs=_or_empty(os.path.dirname(_config.libpq_path)),
      include_dirs=_or_empty(_config.libpq_include_dir),
      )
  
  
  if hasattr(ffi, 'set_source'):
      ffi.set_source('psycopg2cffi._impl._libpq', C_SOURCE, **C_SOURCE_KWARGS)
  
  
  if __name__ == '__main__':
      ffi.compile()
  