Logo Search packages:      
Sourcecode: firebird1.5 version File versions

ipserver.cpp

/*
 *      PROGRAM:        Interprocess Interface server
 *      MODULE:         ipserver.c
 *      DESCRIPTION:    interprocess interface server
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 */

#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <signal.h>
#include <errno.h>
#include <windows.h>

#include "../ipserver/ipc.h"
#include "../jrd/license.h"
#include "../jrd/gds.h"
#include "../jrd/gds_proto.h"
#include "../ipserver/alli_proto.h"
#include "../jrd/thd_proto.h"
#include "../ipserver/ipsrv_proto.h"
#include "../ipserver/ips.h"
#include "../jrd/isc_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/ibsetjmp.h"
#include "../jrd/why_proto.h"
#include "../common/config/config.h"
#include "../jrd/gdsassert.h"

static void allocate_statement(ICC);
static void attach_database(ICC, P_OP);
static void cancel_events(ICC);
static USHORT check_statement_type(IPSERVER_ISR);
static void compile(ICC);
static void ddl(ICC);
static void drop_database(ICC);
static void end_blob(ICC, P_OP);
static void end_database(ICC);
static void end_request(ICC);
static void end_statement(ICC);
static void end_transaction(ICC, P_OP);
static void event_ast(IVNT, USHORT, UCHAR *);
static void execute_immediate(ICC, P_OP);
static void execute_statement(ICC, P_OP);
static void fetch(ICC);
static UCHAR *get_buffer(IPS, USHORT, USHORT);
static void get_segment(ICC);
static void get_slice(ICC);
static void info(ICC, P_OP);
static void insert(ICC);
static void ipi_end_thread(ICC);
static void ipi_server(ICC);
static IPM make_map(USHORT);
static ITR make_transaction(IDB, FRBRD *);
static void open_blob(ICC, P_OP);
static void prepare_statement(ICC);
static void put_segment(ICC);
static void put_slice(ICC);
static void que_events(ICC);
static void receive_msg(ICC);
static void reconnect(ICC);
static void release_blob(IBL);
static void release_event(IVNT);
static void release_request(IRQ);
static void release_sql_request(IPSERVER_ISR);
static void release_transaction(ITR);
static void seek_blob(ICC);
static BOOL send_and_wait(ICC);
static void send_msg(ICC);
static void send_no_wait(ICC);
static void send_response(ICC, ISC_STATUS *);
static void set_cursor(ICC);
static void service_attach(ICC);
static void service_end(ICC);
static void service_start(ICC);
static void shutdown_attachments(ICC);
static void start(ICC);
static void start_and_send(ICC);
static void start_transaction(ICC);
static void transact_request(ICC);
static BOOL transfer_buffers(ICC, ips_comm_area *);
static void unwind(ICC);
static BOOL wait_no_send(ICC);

static HWND IPSVR_window = NULL;
static UCHAR sql_info[] = { isc_info_sql_batch_fetch };

/* Macro to check a given handle for validity.  While generally
 * we can depend on the client-side remote library to do this
 * for us, there were bugs due to not checking on our side.
 * Also, since these handles are passed though to
 * the why-value routines and checked there, strictly speaking
 * we only have to check ones that we dereference before
 * passing along.  For safety's side, we check them all.
 */

/* h = handle to check
 * t = type of handle to expect
 * c = error code to return
 */
#define CHECK_HANDLE(h,t,c)     if ( !(h) || (((BLK) (h))->blk_type !=( t))) \
                            { \
                            status_vector[0] = isc_arg_gds;\
                            status_vector[1] = (c);\
                            status_vector[2] = isc_arg_end;\
                            send_response( icc, status_vector); \
                            return; \
                            }

#define NOT_NULL(p,s)           if ( !(p) && (s)) \
                            { \
                            status_vector[0] = isc_arg_gds;\
                            status_vector[1] = isc_virmemexh;\
                            status_vector[2] = isc_arg_end;\
                            send_response( icc, status_vector); \
                            return; \
                            }

#define GET_COMM_OBJECT { comm = (ips_comm_area*)icc->icc_mapped_addr;\
                    comm_ptr = (TEXT*)comm->ips_data;}

#define IPS_SERVER(c,n,a,s)     { c->ips_buffers[n].ips_sv_size = s;\
                          c->ips_buffers[n].ips_sv_addr = a;\
                          c->ips_buffers[n].ips_sv_copied = 0;}



#define GDS_ATTACH_DATABASE     isc_attach_database
#define GDS_BLOB_INFO           isc_blob_info
#define GDS_CANCEL_BLOB         isc_cancel_blob
#define GDS_CANCEL_EVENTS       isc_cancel_events
#define GDS_CLOSE_BLOB          isc_close_blob
#define GDS_COMMIT              isc_commit_transaction
#define GDS_COMMIT_RETAINING    isc_commit_retaining
#define GDS_COMPILE             isc_compile_request
#define GDS_CREATE_BLOB         isc_create_blob
#define GDS_CREATE_BLOB2        isc_create_blob2
#define GDS_CREATE_DATABASE     isc_create_database
#define GDS_DATABASE_INFO       isc_database_info
#define GDS_DDL                 isc_ddl
#define GDS_DETACH              isc_detach_database
#define GDS_DROP_DATABASE       isc_drop_database
#define GDS_GET_SEGMENT         isc_get_segment
#define GDS_GET_SLICE           isc_get_slice
#define GDS_OPEN_BLOB           isc_open_blob
#define GDS_OPEN_BLOB2          isc_open_blob2
#define GDS_PREPARE             isc_prepare_transaction
#define GDS_PREPARE2            isc_prepare_transaction2
#define GDS_PUT_SEGMENT         isc_put_segment
#define GDS_PUT_SLICE           isc_put_slice
#define GDS_QUE_EVENTS          isc_que_events
#define GDS_RECONNECT           isc_reconnect_transaction

#ifdef SCROLLABLE_CURSORS
#define GDS_RECEIVE             isc_receive2
#else
#define GDS_RECEIVE             isc_receive
#endif

#define GDS_RELEASE_REQUEST     isc_release_request
#define GDS_REQUEST_INFO        isc_request_info
#define GDS_ROLLBACK            isc_rollback_transaction
#define GDS_ROLLBACK_RETAINING  isc_rollback_retaining
#define GDS_SEEK_BLOB           isc_seek_blob
#define GDS_SEND                isc_send
#define GDS_SERVICE_ATTACH      isc_service_attach
#define GDS_SERVICE_DETACH      isc_service_detach
#define GDS_SERVICE_QUERY       isc_service_query
#define GDS_SERVICE_START       isc_service_start
#define GDS_START_AND_SEND      isc_start_and_send
#define GDS_START               isc_start_request
#define GDS_START_MULTIPLE      isc_start_multiple
#define GDS_START_TRANSACTION   isc_start_transaction
#define GDS_TRANSACT_REQUEST    isc_transact_request
#define GDS_TRANSACTION_INFO    isc_transaction_info
#define GDS_UNWIND              isc_unwind_request

#define GDS_DSQL_ALLOCATE       isc_dsql_allocate_statement
#define GDS_DSQL_EXECUTE        isc_dsql_execute2_m
#define GDS_DSQL_EXECUTE_IMMED  isc_dsql_exec_immed3_m
#define GDS_DSQL_FETCH          isc_dsql_fetch_m
#define GDS_DSQL_FREE           isc_dsql_free_statement
#define GDS_DSQL_INSERT         isc_dsql_insert_m
#define GDS_DSQL_PREPARE        isc_dsql_prepare_m
#define GDS_DSQL_SET_CURSOR     isc_dsql_set_cursor_name
#define GDS_DSQL_SQL_INFO       isc_dsql_sql_info

//
// TMN:
//
// Static data ahead.
//
// But the multithread-wizard said:
// - Let there be light. Move static data into specific instances.
//   Let these instances be one per thread, and there will be rejoicing
//   among your followers again... Currently we don't use thread-specific
//   data here, but putting it in a struct is a step.
//
struct ipserver_private_data_t
{
      IPM         first_ipm;
      IPM         ipms;
      MUTX_T      ipics;
      USHORT      pages_per_user;
      USHORT      users_per_map;
      // TMN: num_maps was never used, why I patched it with comments.
      // If anyone can shed some light as to why this was added in the
      // first place it would be appreciated.
//    USHORT      num_maps;
};

static ipserver_private_data_t ipserver_private_data =
{
      0,                                  // first_ipm
      0,                                  // ipms
      { 0 },                              // ipics - DON'T REMOVE THE BRACES!
      IPS_DEF_PAGES_PER_CLI,  // pages_per_user
      IPS_DEF_NUM_CLI               // users_per_map
};


#ifdef  DEBUG_IP_TRACE
static const char* op_strings[] =
{
      "op_attach",
      "op_create",
      "op_detach",
      "op_compile",
      "op_start",
      "op_start_and_send",
      "op_send",
      "op_receive",
      "op_unwind",
      "op_release",
      "op_transaction",
      "op_commit",
      "op_rollback",
      "op_reconnect",
      "op_create_blob",
      "op_open_blob",
      "op_get_segment",
      "op_put_segment",
      "op_cancel_blob",
      "op_close_blob",
      "op_info_database",
      "op_info_request",
      "op_info_transaction",
      "op_info_blob",
      "op_prepare2",
      "op_commit_retaining",
      "op_rollback_retaining",
      "op_open_blob2",
      "op_create_blob2",
      "op_ddl",
      "op_que_events",
      "op_cancel_events",
      "op_put_slice",
      "op_get_slice",
      "op_seek_blob",
      "op_allocate_stmt",
      "op_execute",
      "op_exec_immediate",
      "op_fetch",
      "op_free_stmt",
      "op_prepare_stmt",
      "op_set_cursor",
      "op_info_sql",
      "op_insert",
      "op_execute2",
      "op_exec_immediate2",
      "op_service_attach",
      "op_service_detach",
      "op_service_info",
      "op_service_start",
      "op_transact_request",
      "op_drop_database",
      "op_ping",
      "op_disconnect"
};
#endif


#if defined(SUPERSERVER) && defined(WIN_NT)
extern "C" {
      static void atexit_close_handles()
      {
            IPM pFirstIpm = ipserver_private_data.first_ipm;
            for (IPM pIpm = pFirstIpm; pIpm; pIpm = pIpm->ipm_next)
            {
                  if (pIpm->ipm_address) {
                        UnmapViewOfFile(pIpm->ipm_address);
                        pIpm->ipm_address = 0;
                  }
                  if (pIpm->ipm_handle) {
                        CloseHandle(pIpm->ipm_handle);
                        pIpm->ipm_handle = 0;
                  }
            }
      }
}
#endif      // SUPERSERVER && WIN_NT


USHORT IPS_init(HWND    hwnd,
                        USHORT      usrs_pr_mp,
                        USHORT      pgs_pr_usr,
                        USHORT      mx_mps)
{
/**************************************
 *
 *      I P S _ i n i t
 *
 **************************************
 *
 * Functional description
 *      Create a critical section structure and init
 *      the mapped file stuff.   Returns 1 if successful
 *      and 0 otherwise.
 *
 **************************************/

      /* init the limits */

      if (usrs_pr_mp &&
            usrs_pr_mp >= IPS_MIN_NUM_CLI &&
            usrs_pr_mp <= IPS_MAX_NUM_CLI)
      {
            ipserver_private_data.users_per_map = usrs_pr_mp;
      }
      if (pgs_pr_usr &&
            pgs_pr_usr >= IPS_MIN_PAGES_PER_CLI &&
            pgs_pr_usr <= IPS_MAX_PAGES_PER_CLI)
      {
            ipserver_private_data.pages_per_user = pgs_pr_usr;
      }

// TMN: Disabled code. See definition of the struct above for comments.
//    if (mx_mps && mx_mps >= IPS_MIN_NUM_MAPS && mx_mps <= IPS_MAX_NUM_MAPS) {
//          ipserver_private_data.num_maps = mx_mps;
//    }

      /* init mapped chain and critical regions */

      ipserver_private_data.ipms = NULL;
      THD_mutex_init(&ipserver_private_data.ipics);
      IPSVR_window = hwnd;

      /* create the first map (failure means it's already there */

      IPM ipm = make_map(0);

      if (ipm && (ipm != (IPM) - 1))
      {
            fb_assert(!ipserver_private_data.first_ipm);
            ipserver_private_data.first_ipm = ipm;
#if defined(SUPERSERVER) && defined(WIN_NT)
            // TMN: 2003-03-11: Close the handles at server shutdown
            // Possibly this is also needed for CS, but since I can't test that
            // I decided to only do it for SS.
            atexit(&atexit_close_handles);
#endif
            return (USHORT) 1;
      }

      return (USHORT)(ULONG) ipm;
}


ULONG IPS_start_thread(ULONG client_pid)
{
/**************************************
 *
 *      I P S _ s t a r t _ t h r e a d
 *
 **************************************
 *
 * Functional description
 *      Start an interprocess thread.   This allocates
 *      the next available chunk of the mapped file and
 *      tells the client where it is.
 *
 **************************************/
      ICC icc;
      USHORT mapped_area, mapped_position;
      TEXT name_buffer[128];
      IPM ipm;
      USHORT i, j;
      UCHAR *p;
      HANDLE server_proc;
      ips_comm_area *comm;


      /* go through list of maps */

      THD_mutex_lock(&ipserver_private_data.ipics);
      j = 0;
      for (ipm = ipserver_private_data.ipms; ipm; ipm = ipm->ipm_next)
      {
            /* find an available unused comm area */

            for (i = 0; i < ipserver_private_data.users_per_map; i++)
            {
                  if (!ipm->ipm_ids[i])
                  {
                        break;
                  }
            }
            if (i < ipserver_private_data.users_per_map)
            {
                  ipm->ipm_count++;
                  ipm->ipm_ids[i]++;
                  j = ipm->ipm_number;
                  break;
            }
            j++;
      }

      /* if the mapped file structure has not yet been initialized,
         make one now */

      if (!ipm)
      {

            /* allocate new map file and first slot */

            ipm = make_map(j);

            /* check for errors in creation of mapped file */

            if (!ipm || ipm == (IPM) - 1)
            {
                  THD_mutex_unlock(&ipserver_private_data.ipics);
                  return (ULONG) - 1;
            }
            i = 0;
            ipm->ipm_ids[0]++;
            ipm->ipm_count++;
      }
      mapped_area = j;
      mapped_position = i;

      /* allocate a communications control structure and fill it in */

      icc = (ICC) ALLOC(type_icc);
      if (!icc)
      {
            THD_mutex_unlock(&ipserver_private_data.ipics);
            return (ULONG) - 1;
      }
      p = (UCHAR *) ipm->ipm_address;
      p += IPS_MAPPED_FOR_CLI(ipserver_private_data.pages_per_user, mapped_position);
      memset(p, (char) 0, IPS_MAPPED_PER_CLI(ipserver_private_data.pages_per_user));
      icc->icc_mapped_addr = reinterpret_cast<UCHAR*>(p);
      icc->icc_ipm = ipm;
      icc->icc_slot = mapped_position;
      icc->icc_flags = 0;
      icc->icc_client_id = (DWORD) client_pid;
      icc->icc_client_proc =
            OpenProcess(PROCESS_ALL_ACCESS, TRUE, icc->icc_client_id);
      comm = (ips_comm_area *) icc->icc_mapped_addr;
      comm->ips_client_id = icc->icc_client_id;
      comm->ips_client_proc = icc->icc_client_proc;
      comm->ips_server_id = GetCurrentProcessId();

      /* make sure client knows what this server speaks */

      comm->ips_server_protocol = 1L;
      comm->ips_client_protocol = 0L;

      /* Duplicate the server process handle for client synchronization */
      if (!DuplicateHandle(GetCurrentProcess(),
                                     GetCurrentProcess(),
                                     GetCurrentProcess(),
                                     &server_proc, PROCESS_ALL_ACCESS, FALSE, 0))
      {
            CHAR szBuf[80];
            DWORD dw = GetLastError();

            sprintf(szBuf, "%s:%s failed: GetLastError returned %lu\n",
                        "ipserver.c", "DuplicateHandle(server_proc)", dw);
            gds__log(szBuf);
      }
      if (!DuplicateHandle(server_proc,
                                     server_proc,
                                     comm->ips_client_proc,
                                     &comm->ips_server_proc, SYNCHRONIZE, FALSE, 0))
      {
            CHAR szBuf[80];
            DWORD dw = GetLastError();

            sprintf(szBuf, "%s:%s failed: GetLastError returned %lu\n",
                        "ipserver.c", "DuplicateHandle(ips_server_proc)", dw);
            gds__log(szBuf);
            comm->ips_server_proc = 0;
      }

      CloseHandle(server_proc);

      /* create the semaphores and put the handles into the icc */

      sprintf(name_buffer, IPI_CLIENT_SEM_NAME, Config::getIpcName(),
            mapped_area, mapped_position);
      icc->icc_client_sem =
            CreateSemaphore(ISC_get_security_desc(), 0L, 1L, name_buffer);
      sprintf(name_buffer, IPI_SERVER_SEM_NAME, Config::getIpcName(),
            mapped_area, mapped_position);
      icc->icc_server_sem =
            CreateSemaphore(ISC_get_security_desc(), 0L, 1L, name_buffer);

      /* make sure semaphores are "empty" */

      while (WaitForSingleObject(icc->icc_client_sem, (DWORD) 1) ==
               WAIT_OBJECT_0);
      while (WaitForSingleObject(icc->icc_server_sem, (DWORD) 1) ==
               WAIT_OBJECT_0);

      /* initialize wait for multiple objects array */

      icc->icc_waits[0] = icc->icc_server_sem;
      icc->icc_waits[1] = icc->icc_client_proc;

      /* finally, start the thread for this client */

      gds__thread_start(reinterpret_cast < FPTR_INT_VOID_PTR > (ipi_server),
                                icc, THREAD_medium, 0, 0);

      /* return combined mapped area and number */

#ifdef  DEBUG_IP_TRACE
      gds__log("ipserver icc %8lX, comm %8lX", icc, comm);
#endif

      THD_mutex_unlock(&ipserver_private_data.ipics);
      return IPS_PACK_PARAMS(ipserver_private_data.users_per_map, ipserver_private_data.pages_per_user, mapped_area,
                                       mapped_position);
}


static void ipi_end_thread( ICC icc)
{
/**************************************
 *
 *      i p i _ e n d _ t h r e a d
 *
 **************************************
 *
 * Functional description
 *      End a thread's existence, close its handles,
 *      free its slot and whatnot.
 *
 **************************************/

      /* free up this thread's slot */

      IPM ipm = icc->icc_ipm;
      THD_mutex_lock(&ipserver_private_data.ipics);
      ipm->ipm_ids[icc->icc_slot] = 0;
      ipm->ipm_count--;

      /* if last slot was emptied, unmap and free structure, except
         for the first map, which is always retained */

      if (!ipm->ipm_count && ipm != ipserver_private_data.first_ipm)
      {
            if (ipm->ipm_address) {
                  UnmapViewOfFile(ipm->ipm_address);
                  ipm->ipm_address = 0;
            }
            if (ipm->ipm_handle) {
                  CloseHandle(ipm->ipm_handle);
                  ipm->ipm_handle = 0;
            }

            if (ipserver_private_data.ipms == ipm){
                  ipserver_private_data.ipms = ipm->ipm_next;
            } else {
                  for (IPM pipm = ipserver_private_data.ipms;
                         pipm->ipm_next;
                         pipm = pipm->ipm_next)
                  {
                        if (pipm->ipm_next == ipm)
                        {
                              pipm->ipm_next = ipm->ipm_next;
                              break;
                        }
                  }
            }
            ALLI_free((UCHAR *) ipm);
      }

      /* close handles for semaphores */

      CloseHandle(icc->icc_client_sem);
      CloseHandle(icc->icc_server_sem);
      CloseHandle(icc->icc_client_proc);
      ALLI_release((BLK) icc);
      THD_mutex_unlock(&ipserver_private_data.ipics);
}


static void ipi_server( ICC icc)
{
/**************************************
 *
 *      i p i _ s e r v e r
 *
 **************************************
 *
 * Functional description
 *      Main entrypoint of server.
 *
 **************************************/
      ips_comm_area *comm;
      TEXT *comm_ptr;

      // start this thread

      gds__thread_enable(-1);

      /* error handler */

      try {

      /* request processing loop */

      while (!(icc->icc_flags & ICCF_SHUTDOWN))
      {
            if (!wait_no_send(icc))
                  break;
            GET_COMM_OBJECT;
            comm->ips_ops_count++;
#ifdef  DEBUG_IP_TRACE
            {
                  char line[200];
                  sprintf(line, "%8lX %8lX %8lX %d ipserver %s",
                              (long) GetCurrentThreadId(), (long) icc,
                              comm, comm->ips_operation, op_strings[comm->ips_operation]);
                  gds__log(line);
            }
#endif
            switch (comm->ips_operation)
            {
            case op_attach:
            case op_create:
                  attach_database(icc, static_cast<P_OP>(comm->ips_operation));
                  break;

            case op_cancel_events:
                  cancel_events(icc);
                  break;

            case op_compile:
                  compile(icc);
                  break;

            case op_ddl:
                  ddl(icc);
                  break;

            case op_receive:
                  receive_msg(icc);
                  break;

            case op_seek_blob:
                  seek_blob(icc);
                  break;

            case op_send:
                  send_msg(icc);
                  break;

            case op_start:
                  start(icc);
                  break;

            case op_start_and_send:
                  start_and_send(icc);
                  break;

            case op_transact_request:
                  transact_request(icc);
                  break;

            case op_transaction:
                  start_transaction(icc);
                  break;

            case op_rollback:
            case op_commit:
            case op_commit_retaining:
            case op_rollback_retaining:
            case op_prepare2:
                  end_transaction(icc, static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_detach:
                  end_database(icc);
                  break;

            case op_drop_database:
                  drop_database(icc);
                  break;

            case op_create_blob:
            case op_open_blob:
            case op_create_blob2:
            case op_open_blob2:
                  open_blob(icc, static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_put_segment:
                  put_segment(icc);
                  break;

            case op_get_segment:
                  get_segment(icc);
                  break;

            case op_cancel_blob:
            case op_close_blob:
                  end_blob(icc, static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_release:
                  end_request(icc);
                  break;

            case op_que_events:
                  que_events(icc);
                  break;

            case op_unwind:
                  unwind(icc);
                  break;

            case op_reconnect:
                  reconnect(icc);
                  break;

            case op_info_blob:
            case op_info_request:
            case op_info_database:
            case op_info_transaction:
            case op_info_sql:
            case op_service_info:
                  info(icc, static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_get_slice:
                  get_slice(icc);
                  break;

            case op_put_slice:
                  put_slice(icc);
                  break;

            case op_allocate_stmt:
                  allocate_statement(icc);
                  break;

            case op_execute:
            case op_execute2:
                  execute_statement(icc,
                                            static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_exec_immediate:
            case op_exec_immediate2:
                  execute_immediate(icc,
                                            static_cast < P_OP > (comm->ips_operation));
                  break;

            case op_fetch:
                  fetch(icc);
                  break;

            case op_free_stmt:
                  end_statement(icc);
                  break;

            case op_insert:
                  insert(icc);
                  break;

            case op_prepare_stmt:
                  prepare_statement(icc);
                  break;

            case op_set_cursor:
                  set_cursor(icc);
                  break;

            case op_service_attach:
                  service_attach(icc);
                  break;

            case op_service_detach:
                  service_end(icc);
                  break;

            case op_service_start:
                  service_start(icc);
                  break;

            case op_disconnect:
                  icc->icc_flags |= ICCF_SHUTDOWN;
                  break;

            default:
                  gds__log("Interprocess Server op code %d unknown\n",
                               (int *)(ULONG) comm->ips_operation);
                  icc->icc_flags |= ICCF_SHUTDOWN;
                  break;
            }
      }

      /* left loop, so shut down */

      shutdown_attachments(icc);
      ipi_end_thread(icc);

      }     // try
      catch (const std::exception&) {
            gds__log("ipi_server: error during startup, shutting down");
      }
}


static void allocate_statement( ICC icc)
{
/**************************************
 *
 *      a l l o c a t e _ s t a t e m e n t
 *
 **************************************
 *
 * Functional description
 *      Allocate a statement handle.
 *
 **************************************/
      IDB idb;
      IPSERVER_ISR statement;
      FRBRD* handle;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_dsql *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      handle = NULL;

      /* allocate the statement and create a structure */

      result = GDS_DSQL_ALLOCATE(status_vector, &idb->idb_handle, &handle);
      if (!result)
      {
            statement = (IPSERVER_ISR) ALLOC(type_ipserver_isr);
            ips->ips_st_handle = (UCHAR *) statement;
            NOT_NULL(statement, TRUE);
            statement->isr_handle = handle;
            statement->isr_idb = idb;
            statement->isr_next = idb->idb_sql_requests;
            statement->isr_batch_flag = 0;
            statement->isr_max_recs = 0;
            statement->isr_packed = NULL;
            idb->idb_sql_requests = statement;
      }
      else {
            ips->ips_st_handle = NULL;
      }
      send_response(icc, status_vector);
}


static void attach_database( ICC icc, P_OP operation)
{
/**************************************
 *
 *      a t t a c h _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 *      Process an attach or create packet.
 *
 **************************************/
      USHORT file_length, dpb_length, expanded_length;
      UCHAR *file_name, file_buf[256], *dpb, dpb_buf[256];
      UCHAR *expanded, expanded_buf[256];
      FRBRD* handle;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      IDB idb;
      ips_object *ips;
      ips_string *ips_name;
      ips_string *ips_dpb;
      ips_string *ips_expanded;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;

      /* figure out file name buffer */

      ips_name = &comm->ips_buffers[IPS_ATTACH_NAME];
      file_length = (USHORT) ips_name->ips_cl_size;
      if (file_length <= sizeof(file_buf))
            file_name = file_buf;
      else
            file_name = get_buffer(comm, file_length, IPS_ATTACH_NAME);
      NOT_NULL(file_name, TRUE);
      IPS_SERVER(comm, IPS_ATTACH_NAME, file_name, file_length);

      /* figure out dpb buffer */

      ips_dpb = &comm->ips_buffers[IPS_ATTACH_DPB];
      dpb_length = (USHORT) ips_dpb->ips_cl_size;
      if (dpb_length <= sizeof(dpb_buf))
            dpb = dpb_buf;
      else
            dpb = get_buffer(comm, dpb_length, IPS_ATTACH_DPB);
      NOT_NULL(dpb, dpb_length);
      IPS_SERVER(comm, IPS_ATTACH_DPB, dpb, dpb_length);

      /* figure out expanded file name buffer */

      ips_expanded = &comm->ips_buffers[IPS_ATTACH_EXPANDED];
      expanded_length = (USHORT) ips_expanded->ips_cl_size;
      if (expanded_length <= sizeof(expanded_buf))
            expanded = expanded_buf;
      else
            expanded = get_buffer(comm, expanded_length, IPS_ATTACH_EXPANDED);
      NOT_NULL(expanded, expanded_length);
      IPS_SERVER(comm, IPS_ATTACH_EXPANDED, expanded, expanded_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* get handle */

      handle = NULL;
      if (operation == op_attach)
            result = GDS_ATTACH_DATABASE(status_vector,
                                                       file_length,
                                                       reinterpret_cast < char *>(file_name),
                                                       GDS_REF(handle),
                                                       dpb_length,
                                                       reinterpret_cast < char *>(dpb));
      else
            result = GDS_CREATE_DATABASE(status_vector,
                                                       file_length,
                                                       reinterpret_cast < char *>(file_name),
                                                       GDS_REF(handle),
                                                       dpb_length,
                                                       reinterpret_cast < char *>(dpb), 0);

      /* allocate structure and return handle */

      if (!result)
      {
            idb = (IDB) ALLOC(type_idb);
            ips->ips_handle = (UCHAR *) idb;
            NOT_NULL(idb, TRUE);
            idb->idb_flags = IDBF_DATABASE_ATTACHMENT;
            idb->idb_handle = handle;
            idb->idb_next = icc->icc_databases;
            icc->icc_databases = idb;
      }
      else
            ips->ips_handle = NULL;
      send_response(icc, status_vector);
}


static void cancel_events( ICC icc)
{
/**************************************
 *
 *      c a n c e l _ e v e n t s
 *
 **************************************
 *
 * Functional description
 *      Cancel an outstanding event call.
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      IDB idb;
      IVNT event;
      SLONG id;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      idb = (IDB) ips->ips_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      id = ips->ips_parameter;

      /* perform operation, return response and remove event */

      GDS_CANCEL_EVENTS(status_vector, GDS_REF(idb->idb_handle), GDS_REF(id));
      for (event = idb->idb_events; event; event = event->ivnt_next)
      {
            if (event->ivnt_ast && event->ivnt_id == id)
            {
                  event->ivnt_ast = NULL;
                  event->ivnt_id = 0;
                  break;
            }
      }
      send_response(icc, status_vector);
}


static USHORT check_statement_type( IPSERVER_ISR statement)
{
/**************************************
 *
 *      c h e c k _ s t a t e m e n t _ t y p e
 *
 **************************************
 *
 * Functional description
 *      Return the type of SQL statement.
 *
 **************************************/
      UCHAR buffer[16], *info;
      USHORT l, type;
      ISC_STATUS_ARRAY local_status;
      ISC_STATUS result;
      USHORT ret;
      BOOLEAN done;


      /* init to no batch allowed */

      ret = 0;
      done = FALSE;
      result = GDS_DSQL_SQL_INFO(local_status,
                                             &statement->isr_handle,
                                             sizeof(sql_info),
                                             reinterpret_cast < char *>(sql_info),
                                             sizeof(buffer),
                                             reinterpret_cast < char *>(buffer));
      if (!result)
      {
            /* check the buffer for a batch fetch flag */

            for (info = buffer; (*info != isc_info_end) && !done;)
            {
                  l = (USHORT)isc_vax_integer(reinterpret_cast<char*>(info + 1),
                                                             2);
                  type = (USHORT)isc_vax_integer(reinterpret_cast<char*>(info + 3),
                                                             l);
                  switch (*info)
                  {
                  case isc_info_sql_batch_fetch:
                        ret = type;
                        done = TRUE;
                        break;

                  case isc_info_error:
                  case isc_info_truncated:
                        done = TRUE;
                        break;
                  }
                  info += 3 + l;
            }
      }
      return ret;
}


static void compile( ICC icc)
{
/**************************************
 *
 *      c o m p i l e
 *
 **************************************
 *
 * Functional description
 *      Compile a request.
 *
 **************************************/
      IDB idb;
      IRQ request;
      USHORT blr_length;
      UCHAR *blr;
      FRBRD* handle;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_compile_req *ips;
      ips_string *ips_blr;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_compile;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

      /* set up blr buffer */

      ips_blr = &comm->ips_buffers[IPS_COMPILE_REQ];
      blr_length = (USHORT) ips_blr->ips_cl_size;
      blr = get_buffer(comm, blr_length, IPS_COMPILE_REQ);
      NOT_NULL(blr, blr_length);
      IPS_SERVER(comm, IPS_COMPILE_REQ, blr, blr_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* compile and get handle */

      handle = NULL;
      result = GDS_COMPILE(status_vector,
                                     GDS_REF(idb->idb_handle),
                                     GDS_REF(handle),
                                     blr_length,
                                     reinterpret_cast < char *>(GDS_VAL(blr)));

      /* create data structure and return handle */

      if (!result)
      {
            request = (IRQ) ALLOC(type_irq);
            ips->ips_rq_handle = (UCHAR *) request;
            NOT_NULL(request, TRUE);
            request->irq_handle = handle;
            request->irq_idb = idb;
            request->irq_next = idb->idb_requests;
            idb->idb_requests = request;
      }
      else
            ips->ips_rq_handle = NULL;
      send_response(icc, status_vector);
}


static void ddl( ICC icc)
{
/**************************************
 *
 *      d d l
 *
 **************************************
 *
 * Functional description
 *      Pass thru a DDL call.
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      ITR transaction;
      IDB idb;
      UCHAR *buffer;
      USHORT length;
      ips_ddl *ips;
      ips_string *ips_ddl_string;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_ddl;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);

      /* set up ddl buffer */

      ips_ddl_string = &comm->ips_buffers[IPS_DDL_DDL];
      length = (USHORT) ips_ddl_string->ips_cl_size;
      buffer = get_buffer(comm, length, IPS_DDL_DDL);
      NOT_NULL(buffer, length);
      IPS_SERVER(comm, IPS_DDL_DDL, buffer, length);
      if (!transfer_buffers(icc, comm))
            return;

      /* perform operation and return response */

      GDS_DDL(status_vector,
                  GDS_REF(idb->idb_handle),
                  GDS_REF(transaction->itr_handle),
                  length, reinterpret_cast < char *>(GDS_VAL(buffer)));
      send_response(icc, status_vector);
}


static void drop_database( ICC icc)
{
/**************************************
 *
 *      d r o p _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 *      End a request.
 *
 **************************************/
      IDB idb, *ptr;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS code;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      idb = (IDB) ips->ips_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

      /* drop database, release resources and send response */

      code = GDS_DROP_DATABASE(status_vector, &idb->idb_handle);
      send_response(icc, status_vector);
      if (!code || (code == isc_drdb_completed_with_errs))
      {
            while (idb->idb_events)
                  release_event(idb->idb_events);
            while (idb->idb_requests)
                  release_request(idb->idb_requests);
            while (idb->idb_sql_requests)
                  release_sql_request(idb->idb_sql_requests);
            while (idb->idb_transactions)
                  release_transaction(idb->idb_transactions);
            for (ptr = &icc->icc_databases; *ptr; ptr = &(*ptr)->idb_next)
            {
                  if (*ptr == idb)
                  {
                        *ptr = idb->idb_next;
                        break;
                  }
            }
            ALLI_release((BLK) idb);
      }
}


static void end_blob( ICC icc, P_OP operation)
{
/**************************************
 *
 *      e n d _ b l o b
 *
 **************************************
 *
 * Functional description
 *      End a blob.
 *
 **************************************/
      IBL blob;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      UCHAR *buffer;
      USHORT length;
      ips_object *ips;
      ips_string *ips_seg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      blob = (IBL) ips->ips_handle;
      CHECK_HANDLE(blob, type_ibl, isc_bad_segstr_handle);

      /* set up buffer for last segment, if any */

      ips_seg = &comm->ips_buffers[IPS_CLOSE_BLOB_REM];
      length = (USHORT) ips_seg->ips_cl_size;
      if (length)
      {
            buffer = get_buffer(comm, length, IPS_CLOSE_BLOB_REM);
            NOT_NULL(buffer, length);
            IPS_SERVER(comm, IPS_CLOSE_BLOB_REM, buffer, length);
            if (!transfer_buffers(icc, comm))
                  return;
      }

      /* perform operation, send response and free resources */

      if (operation == op_close_blob)
            result = GDS_CLOSE_BLOB(status_vector, GDS_REF(blob->ibl_handle));
      else
            result = GDS_CANCEL_BLOB(status_vector, GDS_REF(blob->ibl_handle));
      send_response(icc, status_vector);
      if (!result)
            release_blob(blob);
}


static void end_database( ICC icc)
{
/**************************************
 *
 *      e n d _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 *      End a request.
 *
 **************************************/
      IDB idb, *ptr;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      idb = (IDB) ips->ips_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

      /* detach database, send response and free resources */

      result = GDS_DETACH(status_vector, GDS_REF(idb->idb_handle));
      send_response(icc, status_vector);
      if (!result)
      {
            while (idb->idb_events)
                  release_event(idb->idb_events);
            while (idb->idb_requests)
                  release_request(idb->idb_requests);
            while (idb->idb_sql_requests)
                  release_sql_request(idb->idb_sql_requests);
            while (idb->idb_transactions)
                  release_transaction(idb->idb_transactions);
            for (ptr = &icc->icc_databases; *ptr; ptr = &(*ptr)->idb_next)
            {
                  if (*ptr == idb)
                  {
                        *ptr = idb->idb_next;
                        break;
                  }
            }
            ALLI_release((BLK) idb);
      }
}


static void end_request( ICC icc)
{
/**************************************
 *
 *      e n d _ r e q u e s t
 *
 **************************************
 *
 * Functional description
 *      End a request.
 *
 **************************************/
      IRQ request;
      ISC_STATUS_ARRAY status_vector;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      request = (IRQ) ips->ips_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);

      /* process call, respond and free resources */

      GDS_RELEASE_REQUEST(status_vector, GDS_REF(request->irq_handle));
      send_response(icc, status_vector);
      release_request(request);
}


static void end_statement( ICC icc)
{
/**************************************
 *
 *      e n d _ s t a t e m e n t
 *
 **************************************
 *
 * Functional description
 *      Free a dynamic statement.
 *
 **************************************/
      IPSERVER_ISR statement;
      USHORT option;
      ISC_STATUS_ARRAY status_vector;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;

/* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      statement = (IPSERVER_ISR) ips->ips_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      option = (USHORT) ips->ips_parameter;

/* free statement, return response and free resources */

      GDS_DSQL_FREE(status_vector, &statement->isr_handle, option);

      if (statement->isr_handle)
      {
            ips->ips_handle = (UCHAR *) statement;
            send_response(icc, status_vector);
      }
      else
      {
            ips->ips_handle = (UCHAR *) NULL;
            send_response(icc, status_vector);
            release_sql_request(statement);
      }
}


static void end_transaction( ICC icc, P_OP operation)
{
/**************************************
 *
 *      e n d _ t r a n s a c t i o n
 *
 **************************************
 *
 * Functional description
 *      End a transaction.
 *
 **************************************/
      ITR transaction;
      UCHAR *msg;
      USHORT msg_length;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_object *ips;
      ips_string *ips_prep;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      transaction = (ITR) ips->ips_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);

      /* set up buffer for prepare2 */

      if (operation == op_prepare2)
      {
            ips_prep = &comm->ips_buffers[IPS_PREPARE_TRANS];
            msg_length = (USHORT) ips_prep->ips_cl_size;
            msg = get_buffer(comm, msg_length, IPS_PREPARE_TRANS);
            NOT_NULL(msg, msg_length);
            IPS_SERVER(comm, IPS_PREPARE_TRANS, msg, msg_length);
            if (!transfer_buffers(icc, comm))
                  return;
      }

      /* perform operation as required */

      switch (operation)
      {
      case op_commit:
            result = GDS_COMMIT(status_vector, GDS_REF(transaction->itr_handle));
            if (!result)
                  release_transaction(transaction);
            break;

      case op_rollback:
            result = GDS_ROLLBACK(status_vector,
                                            GDS_REF(transaction->itr_handle));
            if (!result)
                  release_transaction(transaction);
            break;

      case op_commit_retaining:
            result = GDS_COMMIT_RETAINING(status_vector,
                                                        GDS_REF(transaction->itr_handle));
            break;

      case op_rollback_retaining:
            result = GDS_ROLLBACK_RETAINING(status_vector,
                                                            GDS_REF(transaction->itr_handle));
            break;

      case op_prepare2:
            result = GDS_PREPARE2(status_vector,
                                            GDS_REF(transaction->itr_handle),
                                            msg_length,
                                            reinterpret_cast<UCHAR*>(GDS_VAL(msg)));
            if (!result)
                  transaction->itr_flags |= ITR_limbo;
            break;
      }
      send_response(icc, status_vector);
}


static void event_ast( IVNT event, USHORT length, UCHAR * data)
{
/**************************************
 *
 *      e v e n t _ a s t
 *
 **************************************
 *
 * Functional description
 *      Send off event stuff.
 *
 **************************************/
      COPYDATASTRUCT cpdata;
      ULONG result;


      if (!event->ivnt_ast)
            return;
      cpdata.dwData = (DWORD) event->ivnt_id;
      cpdata.cbData = (DWORD) length;
      cpdata.lpData = (LPVOID) data;
      result =
            SendMessage(event->ivnt_window, WM_COPYDATA, (WPARAM) IPSVR_window,
                              (LPARAM) & cpdata);
      event->ivnt_ast = NULL;
}


static void execute_immediate( ICC icc, P_OP operation)
{
/**************************************
 *
 *      e x e c u t e _ i m m e d i a t e
 *
 **************************************
 *
 * Functional description
 *      Prepare and execute a statement.
 *
 **************************************/
      IDB idb;
      ITR transaction;
      FRBRD* handle;
      USHORT length, dialect, in_blr_length, in_msg_type, in_msg_length,
            out_blr_length, out_msg_type, out_msg_length, parser_version;
      UCHAR *string, *in_blr, *in_msg, *out_blr, *out_msg;
      ISC_STATUS_ARRAY status_vector;
      ips_dsql *ips;
      ips_string *ips_blr;
      ips_string *ips_msg;
      ips_string *ips_sql;
      ips_string *ips_blr_out;
      ips_string *ips_msg_out;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      if (transaction)
      {
            CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
            handle = transaction->itr_handle;
      }
      else
            handle = NULL;
      in_msg_type = ips->ips_msg_type;
      dialect = (USHORT) ips->ips_parameter;

      /* set up sql */

      ips_sql = &comm->ips_buffers[IPS_DSQL_EXEC_IMMED_SQL];
      length = (USHORT) ips_sql->ips_cl_size;
      string = get_buffer(comm, length, IPS_DSQL_EXEC_IMMED_SQL);
      NOT_NULL(string, length);
      IPS_SERVER(comm, IPS_DSQL_EXEC_IMMED_SQL, string, length);

      /* set up blr */

      ips_blr = &comm->ips_buffers[IPS_DSQL_EXEC_IMMED_BLR];
      in_blr_length = (USHORT) ips_blr->ips_cl_size;
      in_blr = get_buffer(comm, in_blr_length, IPS_DSQL_EXEC_IMMED_BLR);
      NOT_NULL(in_blr, in_blr_length);
      IPS_SERVER(comm, IPS_DSQL_EXEC_IMMED_BLR, in_blr, in_blr_length);

      /* set up message */

      ips_msg = &comm->ips_buffers[IPS_DSQL_EXEC_IMMED_MSG];
      in_msg_length = (USHORT) ips_msg->ips_cl_size;
      in_msg = get_buffer(comm, in_msg_length, IPS_DSQL_EXEC_IMMED_MSG);
      NOT_NULL(in_msg, in_msg_length);
      IPS_SERVER(comm, IPS_DSQL_EXEC_IMMED_MSG, in_msg, in_msg_length);

      /* set up output message and blr if necessary */

      if (operation == op_exec_immediate2)
      {
            out_msg_type = ips->ips_msg_out;
            ips_blr_out = &comm->ips_buffers[IPS_DSQL_EXEC_IMMED2_BLR_OUT];
            out_blr_length = (USHORT) ips_blr_out->ips_cl_size;
            out_blr =
                  get_buffer(comm, out_blr_length, IPS_DSQL_EXEC_IMMED2_BLR_OUT);
            NOT_NULL(out_blr, out_blr_length);
            IPS_SERVER(comm, IPS_DSQL_EXEC_IMMED2_BLR_OUT, out_blr,
                           out_blr_length);
            ips_msg_out = &comm->ips_buffers[IPS_DSQL_EXEC_IMMED2_MSG_OUT];
            out_msg_length = (USHORT) ips_msg_out->ips_cl_size;
            out_msg =
                  get_buffer(comm, out_msg_length, IPS_DSQL_EXEC_IMMED2_MSG_OUT);
            NOT_NULL(out_msg, out_msg_length);
            IPS_SERVER(comm, IPS_DSQL_EXEC_IMMED2_MSG_OUT, out_msg,
                           out_msg_length);
      }
      else
      {
            out_blr_length = out_msg_type = out_msg_length = 0;
            out_blr = out_msg = NULL;
      }
      if (!transfer_buffers(icc, comm))
            return;

      /* NT Local access:  Client and server are always the same version */
      /* Since the API to GDS_DSQL_EXECUTE_IMMED is public and can not be changed, there needs to
       * be a way to send the parser version to DSQL so that the parser can compare the keyword
       * version to the parser version.  To accomplish this, the parser version is combined with
       * the client dialect and sent across that way.  In dsql8_execute_immediate, the parser version
       * and client dialect are separated and passed on to their final desintations.  The information
       * is combined as follows:
       *     Dialect * 10 + parser_version
       *
       * and is extracted in dsql8_execute_immediate as follows:
       *      parser_version = ((dialect *10)+parser_version)%10
       *      client_dialect = ((dialect *10)+parser_version)/10
       *
       * For example, parser_version = 1 and client dialect = 1
       *
       *  combined = (1 * 10) + 1 == 11
       *
       *  parser = (combined) %10 == 1
       *  dialect = (combined) / 19 == 1
       */

      parser_version = 2;

      /* execute the statement, respond and clean up */

      GDS_DSQL_EXECUTE_IMMED(status_vector,
                                       &idb->idb_handle,
                                       &handle,
                                       length,
                                       reinterpret_cast < char *>(string),
                                       (USHORT) ((dialect * 10) + parser_version),
                                       in_blr_length,
                                       reinterpret_cast < char *>(in_blr),
                                       in_msg_type,
                                       in_msg_length,
                                       reinterpret_cast < char *>(in_msg),
                                       out_blr_length,
                                       reinterpret_cast < char *>(out_blr),
                                       out_msg_type,
                                       out_msg_length,
                                       reinterpret_cast < char *>(out_msg));
      if (transaction && !handle)
      {
            release_transaction(transaction);
            transaction = NULL;
      }
      else if (!transaction && handle)
      {
            transaction = make_transaction(idb, handle);
            NOT_NULL(transaction, TRUE);
      }
      ips->ips_tr_handle = (UCHAR *) transaction;
      send_response(icc, status_vector);
}


static void execute_statement( ICC icc, P_OP operation)
{
/**************************************
 *
 *      e x e c u t e _ s t a t e m e n t
 *
 **************************************
 *
 * Functional description
 *      Execute a non-SELECT dynamic SQL statement.
 *
 **************************************/
      ITR transaction;
      IPSERVER_ISR statement;
      FRBRD* handle;
      USHORT in_blr_length, in_msg_type, in_msg_length,
            out_blr_length, out_msg_type, out_msg_length;
      UCHAR *in_blr, *in_msg, *out_blr, *out_msg;
      ISC_STATUS_ARRAY status_vector;
      ips_dsql *ips;
      ips_string *ips_blr;
      ips_string *ips_msg;
      ips_string *ips_blr_out;
      ips_string *ips_msg_out;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      transaction = (ITR) ips->ips_tr_handle;
      if (transaction)
      {
            CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
            handle = transaction->itr_handle;
      }
      else
            handle = NULL;
      statement = (IPSERVER_ISR) ips->ips_st_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      in_msg_type = ips->ips_msg_type;

      /* set up blr */

      ips_blr = &comm->ips_buffers[IPS_DSQL_EXEC_BLR];
      in_blr_length = (USHORT) ips_blr->ips_cl_size;
      in_blr = get_buffer(comm, in_blr_length, IPS_DSQL_EXEC_BLR);
      NOT_NULL(in_blr, in_blr_length);
      IPS_SERVER(comm, IPS_DSQL_EXEC_BLR, in_blr, in_blr_length);

      /* set up message */

      ips_msg = &comm->ips_buffers[IPS_DSQL_EXEC_MSG];
      in_msg_length = (USHORT) ips_msg->ips_cl_size;
      in_msg = get_buffer(comm, in_msg_length, IPS_DSQL_EXEC_MSG);
      NOT_NULL(in_msg, in_msg_length);
      IPS_SERVER(comm, IPS_DSQL_EXEC_MSG, in_msg, in_msg_length);

      /* set up output message and blr if necessary */

      if (operation == op_execute2)
      {
            out_msg_type = ips->ips_msg_out;
            ips_blr_out = &comm->ips_buffers[IPS_DSQL_EXEC2_BLR_OUT];
            out_blr_length = (USHORT) ips_blr_out->ips_cl_size;
            out_blr = get_buffer(comm, out_blr_length, IPS_DSQL_EXEC2_BLR_OUT);
            NOT_NULL(out_blr, out_blr_length);
            IPS_SERVER(comm, IPS_DSQL_EXEC2_BLR_OUT, out_blr, out_blr_length);
            ips_msg_out = &comm->ips_buffers[IPS_DSQL_EXEC2_MSG_OUT];
            out_msg_length = (USHORT) ips_msg_out->ips_cl_size;
            out_msg = get_buffer(comm, out_msg_length, IPS_DSQL_EXEC2_MSG_OUT);
            NOT_NULL(out_msg, out_msg_length);
            IPS_SERVER(comm, IPS_DSQL_EXEC2_MSG_OUT, out_msg, out_msg_length);
      }
      else
      {
            out_blr_length = out_msg_type = out_msg_length = 0;
            out_blr = out_msg = NULL;
      }
      if (!transfer_buffers(icc, comm))
            return;

      /* execute, get results and clean up */

      GDS_DSQL_EXECUTE(status_vector,
                               &handle,
                               &statement->isr_handle,
                               in_blr_length,
                               reinterpret_cast < char *>(in_blr),
                               in_msg_type,
                               in_msg_length,
                               reinterpret_cast < char *>(in_msg),
                               out_blr_length,
                               reinterpret_cast < char *>(out_blr),
                               out_msg_type,
                               out_msg_length, reinterpret_cast < char *>(out_msg));
      if (transaction && !handle)
      {
            release_transaction(transaction);
            transaction = NULL;
      }
      else if (!transaction && handle)
      {
            transaction = make_transaction(statement->isr_idb, handle);
            NOT_NULL(transaction, TRUE);
      }
      ips->ips_tr_handle = (UCHAR *) transaction;
      send_response(icc, status_vector);
}


static void fetch( ICC icc)
{
/**************************************
 *
 *      f e t c h
 *
 **************************************
 *
 * Functional description
 *      Fetch next record from a dynamic SQL cursor.
 *
 **************************************/
      IPSERVER_ISR statement;
      USHORT records, total_length, blr_length, msg_type, msg_length;
      UCHAR *blr, *msg;
      ISC_STATUS s;
      ISC_STATUS_ARRAY status_vector;
      ips_dsql *ips;
      ips_string *ips_blr;
      ips_string *ips_msg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      statement = (IPSERVER_ISR) ips->ips_st_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      msg_type = ips->ips_msg_type;

      /* set up blr */

      ips_blr = &comm->ips_buffers[IPS_DSQL_FETCH_BLR];
      blr_length = (USHORT) ips_blr->ips_cl_size;
      blr = get_buffer(comm, blr_length, IPS_DSQL_FETCH_BLR);
      NOT_NULL(blr, blr_length);
      IPS_SERVER(comm, IPS_DSQL_FETCH_BLR, blr, blr_length);

      /* set up message */

      ips_msg = &comm->ips_buffers[IPS_DSQL_FETCH_MSG];
      total_length = (USHORT) ips_msg->ips_cl_size;
      msg_length = total_length / ips->ips_rec_count;
      msg = get_buffer(comm, total_length, IPS_DSQL_FETCH_MSG);
      NOT_NULL(msg, msg_length);
      IPS_SERVER(comm, IPS_DSQL_FETCH_MSG, msg, total_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* pack in records, keeping final result of operation for eof */

      for (records = 0; records < ips->ips_rec_count; records++)
      {
            s = GDS_DSQL_FETCH(status_vector,
                                       &statement->isr_handle,
                                       blr_length,
                                       reinterpret_cast < char *>(blr),
                                       msg_type,
                                       msg_length, reinterpret_cast < char *>(msg));

            /* point to next packed record */

            msg += msg_length;
            if (s)
                  break;
      }
      ips->ips_rec_count = records;
      ips->ips_parameter = s;
      send_response(icc, status_vector);
}


static UCHAR *get_buffer( IPS comm, USHORT length, USHORT n)
{
/**************************************
 *
 *      g e t _ b u f f e r
 *
 **************************************
 *
 * Functional description
 *      Get a buffer "big enough".
 *
 **************************************/

      if (comm->ips_buffers[n].ips_sv_alloced < length)
      {
            if (comm->ips_buffers[n].ips_sv_buffer)
                  gds__free(comm->ips_buffers[n].ips_sv_buffer);
            comm->ips_buffers[n].ips_sv_alloced = length;
            comm->ips_buffers[n].ips_sv_buffer = (UCHAR*)gds__alloc((SLONG) length);
      }

      /* Make sure that the buffer returned is empty */
      if (comm->ips_buffers[n].ips_sv_buffer)
            memset(comm->ips_buffers[n].ips_sv_buffer, 0,
                     comm->ips_buffers[n].ips_sv_alloced);

      return comm->ips_buffers[n].ips_sv_buffer;
}


static void get_segment( ICC icc)
{
/**************************************
 *
 *      g e t _ s e g m e n t
 *
 **************************************
 *
 * Functional description
 *      Get a single blob segment.
 *
 **************************************/
      IBL blob;
      USHORT buffer_length, length;
      UCHAR *buffer;
      ISC_STATUS_ARRAY status_vector;
      ips_segment *ips;
      ips_string *ips_seg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_segment;
      blob = (IBL) ips->ips_bl_handle;
      CHECK_HANDLE(blob, type_ibl, isc_bad_segstr_handle);

      /* set up segment buffer */

      ips_seg = &comm->ips_buffers[IPS_BLOB_SEGMENT];
      buffer_length = (USHORT) ips_seg->ips_cl_size;
      buffer = get_buffer(comm, buffer_length, IPS_BLOB_SEGMENT);
      NOT_NULL(buffer, buffer_length);
      IPS_SERVER(comm, IPS_BLOB_SEGMENT, buffer, buffer_length);

      /* get the segment data - return it regardless of result
       * as that is what the client side expects
       * isc_segment can be returned in the status vector,
       * but that the data is still valid.
       */

      length = 0;
      GDS_GET_SEGMENT(status_vector,
                              GDS_REF(blob->ibl_handle),
                              GDS_REF(length),
                              buffer_length,
                              reinterpret_cast < char *>(GDS_VAL(buffer)));
      if (!transfer_buffers(icc, comm))
            return;
      ips->ips_length = length;
      send_response(icc, status_vector);
}


static void get_slice( ICC icc)
{
/**************************************
 *
 *      g e t _ s l i c e
 *
 **************************************
 *
 * Functional description
 *      Get slice of array;
 *
 **************************************/
      IDB idb;
      ITR transaction;
      ISC_STATUS_ARRAY status_vector;
      SSHORT sdl_length, param_length;
      ULONG slice_length, return_length, *params, parameters[32];
      UCHAR *sdl, sdl_buf[256], *slice;
      struct bid array_id;
      ips_slice *ips;
      ips_string *ips_sdl;
      ips_string *ips_parms;
      ips_string *ips_data;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_slice;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
      array_id.bid_relation_id = ips->ips_rel_id;
      array_id.bid_number = ips->ips_number;

      /* set up sdl */

      ips_sdl = &comm->ips_buffers[IPS_SLICE_SDL];
      sdl_length = (SSHORT) ips_sdl->ips_cl_size;
      if (sdl_length <= (SLONG) sizeof(sdl_buf))
            sdl = sdl_buf;
      else
            sdl = get_buffer(comm, sdl_length, IPS_SLICE_SDL);
      NOT_NULL(sdl, sdl_length);
      IPS_SERVER(comm, IPS_SLICE_SDL, sdl, sdl_length);

      /* set up parameters */

      ips_parms = &comm->ips_buffers[IPS_SLICE_PARAM];
      param_length = (SSHORT) ips_parms->ips_cl_size;
      if (param_length <= (SLONG) sizeof(parameters))
            params = parameters;
      else
            params = (ULONG *) get_buffer(comm, param_length, IPS_SLICE_PARAM);
      NOT_NULL(params, param_length);
      IPS_SERVER(comm, IPS_SLICE_PARAM, (UCHAR *) params, param_length);

      /* set up slice data buffer */

      ips_data = &comm->ips_buffers[IPS_SLICE_BUFFER];
      slice_length = ips_data->ips_cl_size;
      if (slice_length)
      {
            slice = (UCHAR*)gds__alloc(slice_length);
            NOT_NULL(slice, TRUE);
            memset(slice, 0, slice_length);
      }
      else
            slice = NULL;
      IPS_SERVER(comm, IPS_SLICE_BUFFER, slice, slice_length);
      if (!transfer_buffers(icc, comm))
      {
            if (slice)
            {
                  gds__free(slice);
            }
            return;
      }

      /* get and return slice */

      GDS_GET_SLICE(status_vector,
                          GDS_REF(idb->idb_handle),
                          GDS_REF(transaction->itr_handle),
                          (GDS_QUAD *) & array_id,
                          sdl_length,
                          reinterpret_cast < char *>(sdl),
                          param_length,
                          reinterpret_cast < long *>(params),
                          slice_length,
                          slice, reinterpret_cast < long *>(GDS_REF(return_length)));
      ips->ips_length = return_length;
      send_response(icc, status_vector);
      if (slice)
      {
            gds__free(slice);
      }
}


static void info( ICC icc, P_OP operation)
{
/**************************************
 *
 *      i n f o
 *
 **************************************
 *
 * Functional description
 *      Issue information.
 *
 **************************************/
      FRBRD* handle;
      ISC_STATUS_ARRAY status_vector;
      USHORT item_length, recv_item_length, buffer_length, incarnation;
      UCHAR *buffer, *items, *recv_items;
      UCHAR items_buf[128], recv_items_buf[128];
      IDB idb;
      ITR transaction;
      IBL blob;
      IRQ request;
      IPSERVER_ISR statement;
      ips_object *ips;
      ips_string *ips_items;
      ips_string *ips_data;
      ips_string *ips_rcv_items;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      comm->ips_operation = op_info_blob;
      ips = &comm->ips_operations.ips_op_object;
      handle = (FRBRD *)ips->ips_handle;
      if (operation == op_info_request)
            incarnation = (USHORT) ips->ips_parameter;

      /* set up items buffer */

      ips_items = &comm->ips_buffers[IPS_INFO_ITEMS];
      item_length = (USHORT) ips_items->ips_cl_size;
      if (item_length <= sizeof(items_buf))
            items = items_buf;
      else
            items = get_buffer(comm, item_length, IPS_INFO_ITEMS);
      NOT_NULL(items, item_length);
      IPS_SERVER(comm, IPS_INFO_ITEMS, items, item_length);

      /* set up data buffer */

      ips_data = &comm->ips_buffers[IPS_INFO_DATA];
      buffer_length = (USHORT) ips_data->ips_cl_size;
      buffer = get_buffer(comm, buffer_length, IPS_INFO_DATA);
      NOT_NULL(buffer, buffer_length);
      IPS_SERVER(comm, IPS_INFO_DATA, buffer, buffer_length);

      /* set up receive items buffer if necessary */

      if (operation == op_service_info)
      {
            ips_rcv_items = &comm->ips_buffers[IPS_QUERY_RECV_ITEMS];
            recv_item_length = (USHORT) ips_rcv_items->ips_cl_size;
            if (recv_item_length <= sizeof(recv_items_buf))
                  recv_items = recv_items_buf;
            else
                  recv_items =
                        get_buffer(comm, recv_item_length, IPS_QUERY_RECV_ITEMS);
            NOT_NULL(recv_items, recv_item_length);
            IPS_SERVER(comm, IPS_QUERY_RECV_ITEMS, recv_items, recv_item_length);
      }
      if (!transfer_buffers(icc, comm))
            return;

      /* perform operation */

      switch (operation)
      {
      case op_info_database:
            idb = (IDB) handle;
            CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
            GDS_DATABASE_INFO(status_vector,
                                      GDS_REF(idb->idb_handle),
                                      item_length,
                                      reinterpret_cast < char *>(items),
                                      buffer_length,
                                      reinterpret_cast < char *>(GDS_VAL(buffer)));
            break;

      case op_info_blob:
            blob = (IBL) handle;
            CHECK_HANDLE(blob, type_ibl, isc_bad_segstr_handle);
            GDS_BLOB_INFO(status_vector,
                                GDS_REF(blob->ibl_handle),
                                item_length,
                                reinterpret_cast < char *>(items),
                                buffer_length,
                                reinterpret_cast < char *>(GDS_VAL(buffer)));
            break;

      case op_info_request:
            request = (IRQ) handle;
            CHECK_HANDLE(request, type_irq, isc_bad_req_handle);
            GDS_REQUEST_INFO(status_vector,
                                     GDS_REF(request->irq_handle),
                                     incarnation,
                                     item_length,
                                     reinterpret_cast < char *>(items),
                                     buffer_length,
                                     reinterpret_cast < char *>(GDS_VAL(buffer)));
            break;

      case op_info_transaction:
            transaction = (ITR) handle;
            CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
            GDS_TRANSACTION_INFO(status_vector,
                                           GDS_REF(transaction->itr_handle),
                                           item_length,
                                           reinterpret_cast < char *>(items),
                                           buffer_length,
                                           reinterpret_cast < char *>(GDS_VAL(buffer)));
            break;

      case op_service_info:
            idb = (IDB) handle;
            CHECK_HANDLE(idb, type_idb, isc_bad_svc_handle);
            GDS_SERVICE_QUERY(status_vector, &idb->idb_handle, 0, /* reserved */
                                      item_length,
                                      reinterpret_cast < char *>(items),
                                      recv_item_length,
                                      reinterpret_cast < char *>(recv_items),
                                      buffer_length, reinterpret_cast < char *>(buffer));
            break;

      case op_info_sql:
            statement = (IPSERVER_ISR) handle;
            CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
            GDS_DSQL_SQL_INFO(status_vector,
                                      &statement->isr_handle,
                                      item_length,
                                      reinterpret_cast < char *>(items),
                                      buffer_length, reinterpret_cast < char *>(buffer));
            break;
      }

      /* return results */

      send_response(icc, status_vector);
}


static void insert( ICC icc)
{
/**************************************
 *
 *      i n s e r t
 *
 **************************************
 *
 * Functional description
 *      Insert next record into a dynamic SQL cursor.
 *
 **************************************/
      IPSERVER_ISR statement;
      USHORT blr_length, msg_type, msg_length;
      UCHAR *blr, *msg;
      ISC_STATUS_ARRAY status_vector;
      ips_dsql *ips;
      ips_string *ips_blr;
      ips_string *ips_msg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      statement = (IPSERVER_ISR) ips->ips_st_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      msg_type = ips->ips_msg_type;

      /* set up blr */

      ips_blr = &comm->ips_buffers[IPS_DSQL_INSERT_BLR];
      blr_length = (USHORT) ips_blr->ips_cl_size;
      blr = get_buffer(comm, blr_length, IPS_DSQL_INSERT_BLR);
      NOT_NULL(blr, blr_length);
      IPS_SERVER(comm, IPS_DSQL_INSERT_BLR, blr, blr_length);

      /* set up message */

      ips_msg = &comm->ips_buffers[IPS_DSQL_INSERT_MSG];
      msg_length = (USHORT) ips_msg->ips_cl_size;
      msg = get_buffer(comm, msg_length, IPS_DSQL_INSERT_MSG);
      NOT_NULL(msg, msg_length);
      IPS_SERVER(comm, IPS_DSQL_INSERT_MSG, msg, msg_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* insert the record and return */

      GDS_DSQL_INSERT(status_vector,
                              &statement->isr_handle,
                              blr_length,
                              reinterpret_cast < char *>(blr),
                              msg_type, msg_length, reinterpret_cast < char *>(msg));
      send_response(icc, status_vector);
}


static IPM make_map(USHORT map_number)
{
/**************************************
 *
 *      m a k e _ m a p
 *
 **************************************
 *
 * Functional description
 *      Create a mapped file.   This
 *      returns -
 *          0 - mapped file already exists
 *         -1 - mapping  or creation error
 *      other - pointer to new structure
 *
 **************************************/

      HANDLE map_handle;
      LPVOID map_address;
      // Here we better be 100% SURE this won't overflow!
      TEXT name_buffer[128];

      /* create the mapped file name and try to open it */

      sprintf(name_buffer, IPI_MAPPED_FILE_NAME, Config::getIpcName(), map_number);
      map_handle = CreateFileMapping((HANDLE) 0xFFFFFFFF,
                                                   ISC_get_security_desc(),
                                                   PAGE_READWRITE,
                                                   0L,
                                                   IPS_MAPPED_SIZE(ipserver_private_data.users_per_map,
                                                                           ipserver_private_data.pages_per_user),
                                                   name_buffer);
      if (map_handle && GetLastError() == ERROR_ALREADY_EXISTS) {
            return NULL;
      }
      if (!map_handle) {
            return (IPM) -1;
      }
      map_address = MapViewOfFile(map_handle, FILE_MAP_WRITE, 0L, 0L,
                                                IPS_MAPPED_SIZE(ipserver_private_data.users_per_map,
                                                                        ipserver_private_data.pages_per_user));
      if (!map_address) {
            CloseHandle(map_handle);
            return (IPM) -1;
      }

      /* allocate a structure and initialize it */

      IPM ipm = (IPM) ALLI_alloc(sizeof(struct ipm));
      if (!ipm) {
            UnmapViewOfFile(map_address);
            CloseHandle(map_handle);
            return (IPM) -1;
      }
      ipm->ipm_handle = map_handle;
      ipm->ipm_address = map_address;
      ipm->ipm_number = map_number;
      ipm->ipm_count = 0;
      for (USHORT i = 0; i < ipserver_private_data.users_per_map; i++) {
            ipm->ipm_ids[i] = 0;
      }
      ipm->ipm_next = ipserver_private_data.ipms;
      ipserver_private_data.ipms = ipm;
      return ipm;
}


static ITR make_transaction( IDB idb, FRBRD * handle)
{
/**************************************
 *
 *      m a k e _ t r a n s a c t i o n
 *
 **************************************
 *
 * Functional description
 *      Create a local transaction handle.
 *
 **************************************/
      ITR transaction;


      transaction = (ITR) ALLOC(type_itr);
      if (transaction)
      {
            transaction->itr_idb = idb;
            transaction->itr_handle = handle;
            transaction->itr_next = idb->idb_transactions;
            idb->idb_transactions = transaction;
      }
      return transaction;
}


static void open_blob( ICC icc, P_OP op)
{
/**************************************
 *
 *      o p e n _ b l o b
 *
 **************************************
 *
 * Functional description
 *      Open or create a new blob.
 *
 **************************************/
      IDB idb;
      IBL blob;
      ITR transaction;
      FRBRD * handle;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      USHORT bpb_length;
      UCHAR *bpb, bpb_buf[128];
      struct bid blob_id;
      ips_blob *ips;
      ips_string *ips_bpb;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_blob;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
      handle = NULL;
      bpb_length = 0;

      /* set up bpb if needed */

      if (op == op_open_blob2 || op == op_create_blob2)
      {
            ips_bpb = &comm->ips_buffers[IPS_BLOB_BPB];
            bpb_length = (USHORT) ips_bpb->ips_cl_size;
            if (bpb_length <= sizeof(bpb_buf))
                  bpb = bpb_buf;
            else
                  bpb = get_buffer(comm, bpb_length, IPS_BLOB_BPB);
            NOT_NULL(bpb, bpb_length);
            IPS_SERVER(comm, IPS_BLOB_BPB, bpb, bpb_length);
      }
      if (!transfer_buffers(icc, comm))
            return;

      /* perform operation and return handle */

      switch (op)
      {
      case op_open_blob:
            blob_id.bid_relation_id = ips->ips_rel_id;
            blob_id.bid_number = ips->ips_bid_number;
            result = GDS_OPEN_BLOB(status_vector,
                                             GDS_REF(idb->idb_handle),
                                             GDS_REF(transaction->itr_handle),
                                             GDS_REF(handle), (GDS_QUAD *) & blob_id);
            break;

      case op_create_blob:
            result = GDS_CREATE_BLOB(status_vector,
                                                 GDS_REF(idb->idb_handle),
                                                 GDS_REF(transaction->itr_handle),
                                                 GDS_REF(handle), (GDS_QUAD *) & blob_id);
            ips->ips_rel_id = blob_id.bid_relation_id;
            ips->ips_bid_number = blob_id.bid_number;
            break;

      case op_open_blob2:
            blob_id.bid_relation_id = ips->ips_rel_id;
            blob_id.bid_number = ips->ips_bid_number;
            result = GDS_OPEN_BLOB2(status_vector,
                                                GDS_REF(idb->idb_handle),
                                                GDS_REF(transaction->itr_handle),
                                                GDS_REF(handle),
                                                (GDS_QUAD *) & blob_id,
                                                bpb_length, reinterpret_cast<UCHAR*>(bpb));
            break;

      case op_create_blob2:
            result = GDS_CREATE_BLOB2(status_vector,
                                                  GDS_REF(idb->idb_handle),
                                                  GDS_REF(transaction->itr_handle),
                                                  GDS_REF(handle),
                                                  (GDS_QUAD *) & blob_id,
                                                  bpb_length,
                                                  reinterpret_cast < char *>(bpb));
            ips->ips_rel_id = blob_id.bid_relation_id;
            ips->ips_bid_number = blob_id.bid_number;
            break;
      }
      if (!result)
      {
            blob = (IBL) ALLOC(type_ibl);
            ips->ips_bl_handle = (UCHAR *) blob;
            NOT_NULL(blob, TRUE);
            blob->ibl_handle = handle;
            blob->ibl_idb = idb;
            blob->ibl_itr = transaction;
            blob->ibl_next = transaction->itr_blobs;
            transaction->itr_blobs = blob;
      }
      else
            ips->ips_bl_handle = NULL;
      send_response(icc, status_vector);
}


static void prepare_statement( ICC icc)
{
/**************************************
 *
 *      p r e p a r e _ s t a t e m e n t
 *
 **************************************
 *
 * Functional description
 *      Prepare a dynamic SQL statement for execution.
 *
 **************************************/
      ITR transaction;
      IPSERVER_ISR statement;
      USHORT length, item_length, buffer_length, dialect, parser_version;
      UCHAR *string, *items, items_buf[128], *buffer;
      ISC_STATUS_ARRAY status_vector;
      FRBRD * handle;
      ips_dsql *ips;
      ips_string *ips_prep_string;
      ips_string *ips_prep_items;
      ips_string *ips_prep_buffer;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      transaction = (ITR) ips->ips_tr_handle;
      if (transaction)
      {
            CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
            handle = transaction->itr_handle;
      }
      else
            handle = NULL;
      statement = (IPSERVER_ISR) ips->ips_st_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      dialect = (USHORT) ips->ips_parameter;

      /* set up string */

      ips_prep_string = &comm->ips_buffers[IPS_DSQL_PREP_STRING];
      length = (USHORT) ips_prep_string->ips_cl_size;
      string = get_buffer(comm, length, IPS_DSQL_PREP_STRING);
      NOT_NULL(string, length);
      IPS_SERVER(comm, IPS_DSQL_PREP_STRING, string, length);

      /* set up items */

      ips_prep_items = &comm->ips_buffers[IPS_DSQL_PREP_ITEMS];
      item_length = (USHORT) ips_prep_items->ips_cl_size;
      if (item_length <= sizeof(items_buf))
            items = items_buf;
      else
            items = get_buffer(comm, item_length, IPS_DSQL_PREP_ITEMS);
      NOT_NULL(items, item_length);
      IPS_SERVER(comm, IPS_DSQL_PREP_ITEMS, items, item_length);

      /* set up buffer */

      ips_prep_buffer = &comm->ips_buffers[IPS_DSQL_PREP_BUFFER];
      buffer_length = (USHORT) ips_prep_buffer->ips_cl_size;
      buffer = get_buffer(comm, buffer_length, IPS_DSQL_PREP_BUFFER);
      NOT_NULL(buffer, buffer_length);
      IPS_SERVER(comm, IPS_DSQL_PREP_BUFFER, buffer, buffer_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* NT Local access:  Client and server are always the same version */
      /* Since the API to GDS_DSQL_PREPARE is public and can not be changed, there needs to
       * be a way to send the parser version to DSQL so that the parser can compare the keyword
       * version to the parser version.  To accomplish this, the parser version is combined with
       * the client dialect and sent across that way.  In dsql8_prepare_statement, the parser version
       * and client dialect are separated and passed on to their final desintations.  The information
       * is combined as follows:
       *     Dialect * 10 + parser_version
       *
       * and is extracted in dsql8_prepare_statement as follows:
       *      parser_version = ((dialect *10)+parser_version)%10
       *      client_dialect = ((dialect *10)+parser_version)/10
       *
       * For example, parser_version = 1 and client dialect = 1
       *
       *  combined = (1 * 10) + 1 == 11
       *
       *  parser = (combined) %10 == 1
       *  dialect = (combined) / 19 == 1
       */

      parser_version = 2;

      /* prepare statement and return data */

      GDS_DSQL_PREPARE(status_vector,
                               &handle,
                               &statement->isr_handle,
                               length,
                               reinterpret_cast < char *>(string),
                               (USHORT) ((dialect * 10) + parser_version),
                               item_length,
                               reinterpret_cast < char *>(items),
                               buffer_length, reinterpret_cast < char *>(buffer));
      statement->isr_batch_flag = check_statement_type(statement);
      ips->ips_parameter = statement->isr_batch_flag;
      send_response(icc, status_vector);
}


static void put_segment( ICC icc)
{
/**************************************
 *
 *      p u t _ s e g m e n t
 *
 **************************************
 *
 * Functional description
 *      Write a single blob segment.
 *
 **************************************/
      IBL blob;
      USHORT buffer_length;
      UCHAR *buffer;
      ISC_STATUS_ARRAY status_vector;
      ips_segment *ips;
      ips_string *ips_seg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_segment;
      blob = (IBL) ips->ips_bl_handle;
      CHECK_HANDLE(blob, type_ibl, isc_bad_segstr_handle);

      /* set up segment buffer */

      ips_seg = &comm->ips_buffers[IPS_BLOB_SEGMENT];
      buffer_length = (USHORT) ips_seg->ips_cl_size;
      buffer = get_buffer(comm, buffer_length, IPS_BLOB_SEGMENT);
      NOT_NULL(buffer, buffer_length);
      IPS_SERVER(comm, IPS_BLOB_SEGMENT, buffer, buffer_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* put the segment into the database */

      GDS_PUT_SEGMENT(status_vector,
                              GDS_REF(blob->ibl_handle),
                              buffer_length,
                              reinterpret_cast < char *>(GDS_VAL(buffer)));
      send_response(icc, status_vector);
}


static void put_slice( ICC icc)
{
/**************************************
 *
 *      p u t _ s l i c e
 *
 **************************************
 *
 * Functional description
 *      Put slice of array;
 *
 **************************************/
      IDB idb;
      ITR transaction;
      ISC_STATUS_ARRAY status_vector;
      USHORT sdl_length, param_length;
      ULONG slice_length, *params, parameters[32];
      UCHAR *sdl, sdl_buf[256], *slice;
      struct bid array_id;
      ips_slice *ips;
      ips_string *ips_sdl;
      ips_string *ips_parms;
      ips_string *ips_data;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_slice;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
      array_id.bid_relation_id = ips->ips_rel_id;
      array_id.bid_number = ips->ips_number;

      /* set up sdl */

      ips_sdl = &comm->ips_buffers[IPS_SLICE_SDL];
      sdl_length = (USHORT) ips_sdl->ips_cl_size;
      if (sdl_length <= sizeof(sdl_buf))
            sdl = sdl_buf;
      else
            sdl = get_buffer(comm, sdl_length, IPS_SLICE_SDL);
      NOT_NULL(sdl, sdl_length);
      IPS_SERVER(comm, IPS_SLICE_SDL, sdl, sdl_length);

      /* set up parameters */

      ips_parms = &comm->ips_buffers[IPS_SLICE_PARAM];
      param_length = (USHORT) ips_parms->ips_cl_size;
      if (param_length <= sizeof(parameters))
            params = parameters;
      else
            params = (ULONG *) get_buffer(comm, param_length, IPS_SLICE_PARAM);
      NOT_NULL(params, param_length);
      IPS_SERVER(comm, IPS_SLICE_PARAM, (UCHAR *) params, param_length);

      /* set up slice data buffer */

      ips_data = &comm->ips_buffers[IPS_SLICE_BUFFER];
      slice_length = ips_data->ips_cl_size;
      if (slice_length)
      {
            slice = (UCHAR*)gds__alloc(slice_length);
            NOT_NULL(slice, TRUE);
            memset(slice, 0, slice_length);
      }
      else
            slice = NULL;
      IPS_SERVER(comm, IPS_SLICE_BUFFER, slice, slice_length);
      if (!transfer_buffers(icc, comm))
      {
            if (slice)
            {
                  gds__free(slice);
            }
            return;
      }

      /* put the slice into the database */

      GDS_PUT_SLICE(status_vector,
                          GDS_REF(idb->idb_handle),
                          GDS_REF(transaction->itr_handle),
                          (GDS_QUAD *) & array_id,
                          sdl_length,
                          reinterpret_cast < char *>(sdl),
                          param_length,
                          reinterpret_cast < long *>(params),
                          slice_length, GDS_VAL(slice));
      ips->ips_rel_id = array_id.bid_relation_id;
      ips->ips_number = array_id.bid_number;
      send_response(icc, status_vector);
      if (slice)
      {
            gds__free(slice);
      }
}


static void que_events( ICC icc)
{
/**************************************
 *
 *      q u e _ e v e n t s
 *
 **************************************
 *
 * Functional description
 *      Que events for asynchronous notification.
 *
 **************************************/
      IDB idb;
      USHORT length;
      UCHAR *events;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      IVNT event;
      ips_que_events *ips;
      ips_string *ips_event;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_que_evnt;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

      /* set up event string */

      ips_event = &comm->ips_buffers[IPS_QUEUE_EVENT];
      length = (USHORT) ips_event->ips_cl_size;
      events = get_buffer(comm, length, IPS_QUEUE_EVENT);
      NOT_NULL(events, length);
      IPS_SERVER(comm, IPS_QUEUE_EVENT, events, length);

      /* find or allocate event block */

      for (event = idb->idb_events; event; event = event->ivnt_next)
            if (!event->ivnt_ast)
                  break;
      if (!event)
      {
            event = (IVNT) ALLOC(type_ivnt);
            NOT_NULL(event, TRUE);
            event->ivnt_next = idb->idb_events;
            event->ivnt_idb = idb;
            idb->idb_events = event;
      }

      /* Get data, etc */

      event->ivnt_window = ips->ips_event_hwnd;
      event->ivnt_id = ips->ips_event_id;
      event->ivnt_ast = (void (*)()) ips->ips_ast;
      event->ivnt_arg = (void *) ips->ips_arg;
      if (!transfer_buffers(icc, comm))
            return;

      /* queue the event and return id */

      result = GDS_QUE_EVENTS(status_vector,
                                          GDS_REF(idb->idb_handle),
                                          GDS_REF(event->ivnt_handle),
                                          length,
                                          reinterpret_cast<char*>(GDS_VAL(events)),
                                          reinterpret_cast < void (*)() > (event_ast),
                                          GDS_VAL(event));
      if (!result)
            ips->ips_event_id = event->ivnt_handle;
      else
            event->ivnt_ast = NULL;
      send_response(icc, status_vector);
}


static void receive_msg( ICC icc)
{
/**************************************
 *
 *      r e c e i v e _ m s g
 *
 **************************************
 *
 * Functional description
 *      Receive a message.
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      IRQ request;
      USHORT length, number, level;
#ifdef SCROLLABLE_CURSORS
      USHORT direction;
      ULONG offset;
#endif
      UCHAR *buffer;
      ips_request *ips;
      ips_string *ips_message;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_request;
      request = (IRQ) ips->ips_rq_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);

      /* set up message */

      ips_message = &comm->ips_buffers[IPS_RECEIVE_MESSAGE];
      length = (USHORT) ips_message->ips_cl_size;
      buffer = get_buffer(comm, length, IPS_RECEIVE_MESSAGE);
      NOT_NULL(buffer, length);
      IPS_SERVER(comm, IPS_RECEIVE_MESSAGE, buffer, length);
      number = ips->ips_msg_type;
      level = ips->ips_req_level;
#ifdef SCROLLABLE_CURSORS
      direction = ips->ips_direction;
      offset = ips->ips_offset;
#endif

      /* receive message and send response */

      GDS_RECEIVE(status_vector,
                        GDS_REF(request->irq_handle),
                        number, length, GDS_VAL(buffer), level
#ifdef SCROLLABLE_CURSORS
                        , direction, offset
#endif
            );

      send_response(icc, status_vector);
}


static void reconnect( ICC icc)
{
/**************************************
 *
 *      r e c o n n e c t
 *
 **************************************
 *
 * Functional description
 *      Reconnect to a transaction in limbo.
 *
 **************************************/
      IDB idb;
      ITR transaction;
      USHORT length;
      UCHAR *buffer;
      FRBRD * handle;
      ISC_STATUS_ARRAY status_vector;
      ips_reconnect *ips;
      ips_string *ips_recon;
      ips_comm_area *comm;
      TEXT *comm_ptr;

/* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_recon_trans;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

/* set up string */

      ips_recon = &comm->ips_buffers[IPS_RECONNECT_ID];
      length = (USHORT) ips_recon->ips_cl_size;
      buffer = get_buffer(comm, length, IPS_RECONNECT_ID);
      NOT_NULL(buffer, length);
      IPS_SERVER(comm, IPS_RECONNECT_ID, buffer, length);
      handle = NULL;
      if (!transfer_buffers(icc, comm))
            return;

/* reconnect transaction and return id */

      if (!GDS_RECONNECT(status_vector,
                                 GDS_REF(idb->idb_handle),
                                 GDS_REF(handle),
                                 length, reinterpret_cast < char *>(GDS_VAL(buffer))))
      {
            if (transaction = make_transaction(idb, handle))
            {
                  ips->ips_tr_handle = (UCHAR *) transaction;
                  transaction->itr_flags |= ITR_limbo;
            }
            else
            {
                  gds__handle_cleanup(status_vector,
                                                (FRBRD **) GDS_REF(handle));
                  NOT_NULL(transaction, TRUE);
            }
      }

      send_response(icc, status_vector);
}


static void release_blob( IBL blob)
{
/**************************************
 *
 *      r e l e a s e _ b l o b
 *
 **************************************
 *
 * Functional description
 *      Release a blob block and friends.
 *
 **************************************/
      IDB idb;
      ITR transaction;
      IBL *p;


      idb = blob->ibl_idb;
      transaction = blob->ibl_itr;

      for (p = &transaction->itr_blobs; *p; p = &(*p)->ibl_next)
      {
            if (*p == blob)
            {
                  *p = blob->ibl_next;
                  break;
            }
      }
      ALLI_release((BLK) blob);
}


static void release_event( IVNT event)
{
/**************************************
 *
 *      r e l e a s e _ e v e n t s
 *
 **************************************
 *
 * Functional description
 *      Release a event block and friends.
 *
 **************************************/
      IDB idb;
      IVNT *p;


      idb = event->ivnt_idb;
      for (p = &idb->idb_events; *p; p = &(*p)->ivnt_next)
      {
            if (*p == event)
            {
                  *p = event->ivnt_next;
                  break;
            }
      }
      ALLI_release((BLK) event);
}


static void release_request( IRQ request)
{
/**************************************
 *
 *      r e l e a s e _ r e q u e s t
 *
 **************************************
 *
 * Functional description
 *      Release a request block and friends.
 *
 **************************************/
      IDB idb;
      IRQ *p;


      idb = request->irq_idb;
      for (p = &idb->idb_requests; *p; p = &(*p)->irq_next)
            if (*p == request)
            {
                  *p = request->irq_next;
                  break;
            }
      ALLI_release((BLK) request);
}


static void release_sql_request( IPSERVER_ISR stmt)
{
/**************************************
 *
 *      r e l e a s e _ s q l _ r e q u e s t
 *
 **************************************
 *
 * Functional description
 *      Release an SQL request block.
 *
 **************************************/
      IDB idb;
      IPSERVER_ISR *p;


      idb = stmt->isr_idb;
      for (p = &idb->idb_sql_requests; *p; p = &(*p)->isr_next)
            if (*p == stmt)
            {
                  *p = stmt->isr_next;
                  break;
            }
      ALLI_release((BLK) stmt);
}


static void release_transaction( ITR transaction)
{
/**************************************
 *
 *      r e l e a s e _ t r a n s a c t i o n
 *
 **************************************
 *
 * Functional description
 *      Release a transaction block and friends.
 *
 **************************************/
      IDB idb;
      ITR *p;


      idb = transaction->itr_idb;
      while (transaction->itr_blobs)
            release_blob(transaction->itr_blobs);
      for (p = &idb->idb_transactions; *p; p = &(*p)->itr_next)
            if (*p == transaction)
            {
                  *p = transaction->itr_next;
                  break;
            }
      ALLI_release((BLK) transaction);
}


static void seek_blob( ICC icc)
{
/**************************************
 *
 *      s e e k _ b l o b
 *
 **************************************
 *
 * Functional description
 *      Perform a blob seek.
 *
 **************************************/
      IBL blob;
      SSHORT mode;
      SLONG offset, result;
      ISC_STATUS_ARRAY status_vector;
      ips_seek_blob *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_sk_blob;
      blob = (IBL) ips->ips_bl_handle;
      CHECK_HANDLE(blob, type_ibl, isc_bad_segstr_handle);
      offset = ips->ips_offset;
      mode = ips->ips_mode;

      /* seek blob and return result */

      GDS_SEEK_BLOB(status_vector,
                          GDS_REF(blob->ibl_handle), mode, offset, GDS_REF(result));
      ips->ips_result = result;
      send_response(icc, status_vector);
}


static BOOL send_and_wait( ICC icc)
{
/**************************************
 *
 *      s e n d _ a n d _ w a i t
 *
 **************************************
 *
 * Functional description
 *      This function responds to the client by releasing
 *      the client's semaphore, and then waits for more
 *      work by waiting on the server's semaphore.
 *
 **************************************/
      DWORD result;


      /* already in shutdown mode */

      if (icc->icc_flags & ICCF_SHUTDOWN)
            return FALSE;

      /* first, signal the client that the communications
         area in mapped memory is filed and ready */

      if (!ReleaseSemaphore(icc->icc_client_sem, 1L, NULL))
      {
            icc->icc_flags |= ICCF_SHUTDOWN;
#ifdef  DEBUG_IP_TRACE
            gds__log("ipserver send of send and wait failed %lX %ld",
                         (long) icc, (long) GetLastError());
#endif
            return FALSE;
      }

      /* next, wait for the client to signal us back */

      result =
            WaitForMultipleObjects((DWORD) 2, icc->icc_waits, FALSE, INFINITE);
      if (result != WAIT_OBJECT_0)
      {
            icc->icc_flags |= ICCF_SHUTDOWN;
#ifdef  DEBUG_IP_TRACE
            gds__log("ipserver wait of send and wait failed %lX %ld",
                         (long) icc, (long) GetLastError());
#endif
            return FALSE;
      }
      else
            return TRUE;
}


static void send_msg( ICC icc)
{
/**************************************
 *
 *      s e n d _ m s g
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      IRQ request;
      UCHAR *buffer;
      USHORT length, number, level;
      ips_request *ips;
      ips_string *ips_message;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_request;
      request = (IRQ) ips->ips_rq_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);

      /* set up message */

      ips_message = &comm->ips_buffers[IPS_SEND_MESSAGE];
      length = (USHORT) ips_message->ips_cl_size;
      buffer = get_buffer(comm, length, IPS_SEND_MESSAGE);
      NOT_NULL(buffer, length);
      IPS_SERVER(comm, IPS_SEND_MESSAGE, buffer, length);
      number = ips->ips_msg_type;
      level = ips->ips_req_level;
      if (!transfer_buffers(icc, comm))
            return;

      /* send message */

      GDS_SEND(status_vector,
                   GDS_REF(request->irq_handle),
                   number, length, GDS_VAL(buffer), level);
      send_response(icc, status_vector);
}


static void send_no_wait( ICC icc)
{
/**************************************
 *
 *      s e n d _ n o _ w a i t
 *
 **************************************
 *
 * Functional description
 *      This function sends the client the server's
 *      last filled in communications area, indicating
 *      that the server is done.   No wait is done here.
 *
 **************************************/


      /* already in shutdown mode */

      if (icc->icc_flags & ICCF_SHUTDOWN)
            return;

      /* signal the client that the communications
         area in mapped memory is filed and ready */

      if (!ReleaseSemaphore(icc->icc_client_sem, 1L, NULL))
      {
#ifdef  DEBUG_IP_TRACE
            gds__log("ipserver send no wait failed %lX %ld",
                         (long) icc, (long) GetLastError());
#endif
            icc->icc_flags |= ICCF_SHUTDOWN;
      }
}


static void send_response( ICC icc, ISC_STATUS * status_vector)
{
/**************************************
 *
 *      s e n d _ r e s p o n s e
 *
 **************************************
 *
 * Functional description
 *      Send a response packet.
 *
 **************************************/
      ISC_STATUS *comm_status;
      USHORT i, length;
      ULONG to_copy, size_left;
      TEXT *p, buffer[1024];
      TEXT *acursor, *scursor, *abase;
      ips_comm_area *comm;
      TEXT *comm_ptr;
      ips_string *string;
      ULONG l, *lp;


      /* first, initialize the status vector in the comm area */

      comm = (ips_comm_area *) icc->icc_mapped_addr;
      comm_ptr = (TEXT *) comm->ips_data;
      comm_status = reinterpret_cast < ISC_STATUS * >(comm->ips_status);
      abase = comm_ptr;

      /* if there's no error or warning, just put in a clean status vector */

      if (!status_vector[1] && !status_vector[2])
      {
            *comm_status++ = *status_vector++;
            *comm_status++ = *status_vector++;
      }
      else
      {
            /* transfer the status vector, putting strings in the comm area's
               data area */

            while (*status_vector)
            {
                  switch (*status_vector)
                  {
                  case isc_arg_interpreted:
                  case isc_arg_string:

                        /* for strings, put the length, followed by the
                           string in the comm data area */

                        *comm_status++ = *status_vector++;
                        p = (TEXT *) * status_vector++;
                        if (p)
                              length = strlen(p);
                        else
                              length = 0;
                        lp = (ULONG *) comm_ptr;
                        *lp = length;
                        comm_ptr += sizeof(ULONG);
                        for (i = 0; i < length; i++)
                              *comm_ptr++ = *p++;
                        l = length % sizeof(ULONG);
                        if (l)
                              comm_ptr += (sizeof(ULONG) - l);
                        break;

                  case isc_arg_cstring:
                        *comm_status++ = isc_arg_string;
                        status_vector++;
                        length = (USHORT) * status_vector++;
                        lp = (ULONG *) comm_ptr;
                        *lp = length;
                        comm_ptr += sizeof(ULONG);
                        p = (TEXT *) * status_vector++;
                        for (i = 0; i < length; i++)
                              *comm_ptr++ = *p++;
                        l = length % sizeof(ULONG);
                        if (l)
                              comm_ptr += (sizeof(ULONG) - l);
                        break;

                  case isc_arg_warning:
                  case isc_arg_number:
                  case isc_arg_gds:
                        *comm_status++ = *status_vector++;
                        *comm_status++ = *status_vector++;
                        break;

                  default:
                        length = (USHORT) isc_interprete(buffer, &status_vector);
                        if (!length)
                        {
                              *status_vector = 0;
                              break;
                        }
                        *comm_status++ = isc_arg_interpreted;
                        lp = (ULONG *) comm_ptr;
                        *lp = length;
                        comm_ptr += sizeof(ULONG);
                        for (i = 0; i < length; i++)
                              *comm_ptr++ = buffer[i];
                        l = length % sizeof(ULONG);
                        if (l)
                              comm_ptr += (sizeof(ULONG) - l);
                        break;
                  }
            }
      }
      *comm_status++ = isc_arg_end;

      /* point to the current first available byte and figure space available */

      acursor = comm_ptr;
      size_left = (ULONG) (IPS_USEFUL_SPACE(ipserver_private_data.pages_per_user)) -
            (abase - reinterpret_cast < TEXT * >(comm->ips_data));
      for (i = 0; i < MAX_IPS_STRINGS; i++)
      {
            string = &comm->ips_buffers[i];

            /* see if this buffer needs sending */

            if ((string->ips_flags & IPS_OUTPUT_BUFFER) && string->ips_sv_size)
            {
                  string->ips_sv_copied = 0;
                  scursor = reinterpret_cast < char *>(string->ips_sv_addr);

                  /* keep packing it in */

                  while (string->ips_sv_copied < string->ips_sv_size)
                  {

                        /* put in the whole thing, or whatever will fit */

                        to_copy = string->ips_sv_size - string->ips_sv_copied;
                        if (size_left <= to_copy)
                              to_copy = size_left;
                        string->ips_com_offset = acursor - abase;
                        string->ips_com_size = to_copy;
                        memcpy(acursor, scursor, (USHORT) to_copy);
                        acursor += to_copy;
                        scursor += to_copy;
                        size_left -= to_copy;
                        string->ips_sv_copied += to_copy;

                        /* if all the space has been used up, send it and wait */

                        if (!size_left && string->ips_sv_copied < string->ips_sv_size)
                        {
                              comm->ips_sequence++;
                              if (!send_and_wait(icc))
                                    return;
                              size_left = (ULONG) (IPS_USEFUL_SPACE(ipserver_private_data.pages_per_user));
                              acursor = reinterpret_cast < char *>(comm->ips_data);
                        }
                  }
            }
      }

      /* sending the last buffer, all done */

      comm->ips_sequence++;
      send_no_wait(icc);
}


static void service_attach( ICC icc)
{
/**************************************
 *
 *      s e r v i c e _ a t t a c h 
 *
 **************************************
 *
 * Functional description
 *      Connect to an Interbase service.
 *
 **************************************/
      USHORT service_length, spb_length;
      UCHAR service_name[256], *spb, spb_buf[256];
      FRBRD * handle;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      IDB idb;
      ips_object *ips;
      ips_string *ips_name;
      ips_string *ips_spb;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;

      /* figure out service name buffer */

      ips_name = &comm->ips_buffers[IPS_ATTACH_SVC_NAME];
      service_length = (USHORT) ips_name->ips_cl_size;
      IPS_SERVER(comm, IPS_ATTACH_SVC_NAME, service_name, service_length);

      /* figure out spb buffer */

      ips_spb = &comm->ips_buffers[IPS_ATTACH_SVC_SPB];
      spb_length = (USHORT) ips_spb->ips_cl_size;
      if (spb_length <= sizeof(spb_buf))
            spb = spb_buf;
      else
            spb = get_buffer(comm, spb_length, IPS_ATTACH_SVC_SPB);
      NOT_NULL(spb, spb_length);
      IPS_SERVER(comm, IPS_ATTACH_SVC_SPB, spb, spb_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* get handle */

      handle = NULL;
      result = GDS_SERVICE_ATTACH(status_vector,
                                                service_length,
                                                reinterpret_cast < char *>(service_name),
                                                &handle,
                                                spb_length, reinterpret_cast < char *>(spb));

      /* allocate structure and return handle */

      if (!result)
      {
            idb = (IDB) ALLOC(type_idb);
            ips->ips_handle = (UCHAR *) idb;
            NOT_NULL(idb, TRUE);
            idb->idb_flags = IDBF_SERVICE_ATTACHMENT;
            idb->idb_handle = handle;
            idb->idb_next = icc->icc_databases;
            icc->icc_databases = idb;
      }
      else
            ips->ips_handle = NULL;
      send_response(icc, status_vector);
}


static void service_end( ICC icc)
{
/**************************************
 *
 *      s e r v i c e _ e n d
 *
 **************************************
 *
 * Functional description
 *      Close down a service.
 *
 **************************************/
      IDB idb, *ptr;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      idb = (IDB) ips->ips_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);

      /* detach request, return response and free resources */

      result = GDS_SERVICE_DETACH(status_vector, &idb->idb_handle);
      send_response(icc, status_vector);
      if (!result)
      {
            for (ptr = &icc->icc_databases; *ptr; ptr = &(*ptr)->idb_next)
                  if (*ptr == idb)
                  {
                        *ptr = idb->idb_next;
                        break;
                  }
            ALLI_release((BLK) idb);
      }
}


static void service_start( ICC icc)
{
/**************************************
 *
 *      s e r v i c e _ s t a r t
 *
 **************************************
 *
 * Functional description
 *      Start a service.
 *
 **************************************/
      IDB idb;
      ISC_STATUS_ARRAY status_vector;
      ISC_STATUS result;
      ips_object *ips;
      ips_comm_area *comm;
      ips_string *ips_spb;
      TEXT *comm_ptr;
      USHORT spb_length;
      UCHAR *spb, spb_buf[256];
      HANDLE handle;
      ULONG *reserved = 0;

/* set up communications area */
      GET_COMM_OBJECT;
      comm->ips_operation = op_service_start;
      ips = &comm->ips_operations.ips_op_object;
      handle = (HANDLE) ips->ips_handle;

/* figure out spb buffer */
      ips_spb = &comm->ips_buffers[IPS_START_SVC_SPB];
      spb_length = (USHORT) ips_spb->ips_cl_size;
      if (spb_length <= sizeof(spb_buf))
            spb = spb_buf;
      else
            spb = get_buffer(comm, spb_length, IPS_START_SVC_SPB);
      NOT_NULL(spb, spb_length);

      IPS_SERVER(comm, IPS_START_SVC_SPB, spb, spb_length);

      if (!transfer_buffers(icc, comm))
            return;

      idb = (IDB) handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_svc_handle);
      result = GDS_SERVICE_START(status_vector,
                                             &idb->idb_handle,
                                             reinterpret_cast < long *>(reserved),
                                             spb_length, reinterpret_cast < char *>(spb));

      send_response(icc, status_vector);
}


static void set_cursor( ICC icc)
{
/**************************************
 *
 *      s e t _ c u r s o r
 *
 **************************************
 *
 * Functional description
 *      Set a cursor name for a dynamic request.
 *
 **************************************/
      IPSERVER_ISR statement;
      USHORT length, type;
      UCHAR *cursor;
      ISC_STATUS_ARRAY status_vector;
      ips_dsql *ips;
      ips_string *ips_name;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_dsql;
      statement = (IPSERVER_ISR) ips->ips_st_handle;
      CHECK_HANDLE(statement, type_ipserver_isr, isc_bad_stmt_handle);
      type = (USHORT) ips->ips_parameter;

      /* set up cursor name */

      ips_name = &comm->ips_buffers[IPS_DSQL_SET_CURSOR];
      length = (USHORT) ips_name->ips_cl_size;
      cursor = get_buffer(comm, length, IPS_DSQL_SET_CURSOR);
      NOT_NULL(cursor, length);
      IPS_SERVER(comm, IPS_DSQL_SET_CURSOR, cursor, length);
      if (!transfer_buffers(icc, comm))
            return;

      /* set cursor name */

      GDS_DSQL_SET_CURSOR(status_vector,
                                    &statement->isr_handle,
                                    reinterpret_cast < char *>(cursor), type);
      send_response(icc, status_vector);
}


static void shutdown_attachments( ICC icc)
{
/**************************************
 *
 *      s h u t d o w n _ a t t a c h m e n t s
 *
 **************************************
 *
 * Functional description
 *      About to exit, detach any remaining database
 *      and service attachments.
 *
 **************************************/
      IDB idb, nextidb;
      ISC_STATUS_ARRAY status_vector;
      ips_comm_area *comm;
      USHORT i;

      for (idb = icc->icc_databases; idb; idb = nextidb)
      {
            /* save next pointer here because the structure's getting freed */

            nextidb = idb->idb_next;
            if (idb->idb_flags | IDBF_DATABASE_ATTACHMENT)
            {
                  while (idb->idb_transactions)
                  {
                        if (!(idb->idb_transactions->itr_flags & ITR_limbo))
                              GDS_ROLLBACK(status_vector,
                                                 GDS_REF(idb->idb_transactions->itr_handle));
                        else
                              gds__handle_cleanup(status_vector,
                                                            (FRBRD **) GDS_REF(idb->
                                                                                                 idb_transactions->
                                                                                                 itr_handle));

                        release_transaction(idb->idb_transactions);
                  }

                  GDS_DETACH(status_vector, GDS_REF(idb->idb_handle));
                  while (idb->idb_events)
                        release_event(idb->idb_events);

                  while (idb->idb_requests)
                        release_request(idb->idb_requests);

                  while (idb->idb_sql_requests)
                        release_sql_request(idb->idb_sql_requests);
            }
            else
            {
                  GDS_SERVICE_DETACH(status_vector, &idb->idb_handle);
            }

            ALLI_release((BLK) idb);
      }

/* free any allocated server buffers */

      comm = (ips_comm_area *) icc->icc_mapped_addr;
      for (i = 0; i < MAX_IPS_STRINGS; i++)
            if (comm->ips_buffers[i].ips_sv_buffer)
                  gds__free(comm->ips_buffers[i].ips_sv_buffer);
}


static void start( ICC icc)
{
/**************************************
 *
 *      s t a r t
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      IRQ request;
      ITR transaction;
      USHORT level;
      ips_request *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_request;
      request = (IRQ) ips->ips_rq_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
      level = ips->ips_req_level;

      /* start the request */

      GDS_START(status_vector,
                    GDS_REF(request->irq_handle),
                    GDS_REF(transaction->itr_handle), level);
      send_response(icc, status_vector);
}


static void start_and_send( ICC icc)
{
/**************************************
 *
 *      s t a r t _ a n d _ s e n d
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      ISC_STATUS_ARRAY status_vector;
      IRQ request;
      ITR transaction;
      UCHAR *buffer;
      USHORT length, number, level;
      ips_request *ips;
      ips_string *ips_message;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_request;
      request = (IRQ) ips->ips_rq_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);
      number = ips->ips_msg_type;
      level = ips->ips_req_level;

      /* set up message */

      ips_message = &comm->ips_buffers[IPS_SEND_MESSAGE];
      length = (USHORT) ips_message->ips_cl_size;
      buffer = get_buffer(comm, length, IPS_SEND_MESSAGE);
      NOT_NULL(buffer, length);
      IPS_SERVER(comm, IPS_SEND_MESSAGE, buffer, length);
      number = ips->ips_msg_type;
      level = ips->ips_req_level;
      if (!transfer_buffers(icc, comm))
            return;

      /* start the request */

      GDS_START_AND_SEND(status_vector,
                                 GDS_REF(request->irq_handle),
                                 GDS_REF(transaction->itr_handle),
                                 number, length, GDS_VAL(buffer), level);
      send_response(icc, status_vector);
}


static void start_transaction( ICC icc)
{
/**************************************
 *
 *      s t a r t _ t r a n s a c t i o n
 *
 **************************************
 *
 * Functional description
 *      Start a transaction.
 *
 **************************************/
      IDB idb;
      ITR transaction;
      USHORT count, c;
      UCHAR *buffer;
      FRBRD * handle;
      ULONG **v, *vector[3 * 16];
      ISC_STATUS_ARRAY status_vector;
      ips_start_trans *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;
      ULONG length, l, *lp;


      /* set up communications area */

      GET_COMM_OBJECT;
      lp = (ULONG *) comm_ptr;
      ips = &comm->ips_operations.ips_op_strt_trans;
      v = vector;
      count = ips->ips_db_count;
      buffer = get_buffer(comm, 1024, 0);
      NOT_NULL(buffer, TRUE);

      /* set up all the handles */

      for (c = 0; c < count; c++)
      {
            idb = (IDB) * lp++;
            CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
            *v++ = reinterpret_cast < ULONG * >(&idb->idb_handle);
            length = *lp++;
            *v++ = reinterpret_cast < ULONG * >(length);
            *v++ = reinterpret_cast < ULONG * >(buffer);
            comm_ptr = (TEXT *) lp;
            for (l = 0; l < length; l++)
                  *buffer++ = *comm_ptr++;
            lp = lp + ((length + (sizeof(ULONG) - 1)) / sizeof(ULONG));
      }
      handle = NULL;

      /* start the transaction and return the handle */

      GDS_START_MULTIPLE(status_vector,
                                 GDS_REF(handle), count, (UCHAR *) vector);
      transaction = make_transaction(idb, handle);
      ips->ips_tr_handle = (UCHAR *) transaction;
      NOT_NULL(transaction, TRUE);
      send_response(icc, status_vector);
}


static void transact_request( ICC icc)
{
/**************************************
 *
 *      t r a n s a c t _ r e q u e s t
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      IDB idb;
      ITR transaction;
      USHORT blr_length, in_msg_length, out_msg_length;
      UCHAR *blr, *in_buffer, *out_buffer;
      ISC_STATUS_ARRAY status_vector;
      ips_transact_request *ips;
      ips_string *ips_blr;
      ips_string *ips_in_msg;
      ips_string *ips_out_msg;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_trans_req;
      idb = (IDB) ips->ips_db_handle;
      CHECK_HANDLE(idb, type_idb, isc_bad_db_handle);
      transaction = (ITR) ips->ips_tr_handle;
      CHECK_HANDLE(transaction, type_itr, isc_bad_trans_handle);

      /* set up blr */

      ips_blr = &comm->ips_buffers[IPS_TRANS_REQ_BLR];
      blr_length = (USHORT) ips_blr->ips_cl_size;
      blr = get_buffer(comm, blr_length, IPS_TRANS_REQ_BLR);
      NOT_NULL(blr, blr_length);
      IPS_SERVER(comm, IPS_TRANS_REQ_BLR, blr, blr_length);

      /* set up input message */

      ips_in_msg = &comm->ips_buffers[IPS_TRANS_REQ_IN_MSG];
      in_msg_length = (USHORT) ips_in_msg->ips_cl_size;
      in_buffer = get_buffer(comm, in_msg_length, IPS_TRANS_REQ_IN_MSG);
      NOT_NULL(in_buffer, in_msg_length);
      IPS_SERVER(comm, IPS_TRANS_REQ_IN_MSG, in_buffer, in_msg_length);

      /* set up blr */

      ips_out_msg = &comm->ips_buffers[IPS_TRANS_REQ_OUT_MSG];
      out_msg_length = (USHORT) ips_out_msg->ips_cl_size;
      out_buffer = get_buffer(comm, out_msg_length, IPS_TRANS_REQ_OUT_MSG);
      NOT_NULL(out_buffer, out_msg_length);
      IPS_SERVER(comm, IPS_TRANS_REQ_OUT_MSG, out_buffer, out_msg_length);
      if (!transfer_buffers(icc, comm))
            return;

      /* transact request and return output */

      GDS_TRANSACT_REQUEST(status_vector,
                                     &idb->idb_handle,
                                     &transaction->itr_handle,
                                     blr_length,
                                     reinterpret_cast < char *>(blr),
                                     in_msg_length,
                                     reinterpret_cast < char *>(in_buffer),
                                     out_msg_length,
                                     reinterpret_cast < char *>(out_buffer));
      send_response(icc, status_vector);
}


static BOOL transfer_buffers( ICC icc, ips_comm_area * comm)
{
/**************************************
 *
 *      t r a n s f e r _ b u f f e r s
 *
 **************************************
 *
 * Functional description
 *
 **************************************/
      TEXT *acursor, *scursor;
      ULONG to_copy;
      USHORT i;
      ips_string *string;


      /* for each buffer, see if the client is sending stuff, and if so,
         put the data in the server's buffers */

      for (i = 0; i < MAX_IPS_STRINGS; i++)
      {
            string = &comm->ips_buffers[i];

            /* see if this buffer needs extraction */

            if ((string->ips_flags & IPS_INPUT_BUFFER) && string->ips_sv_size)
            {
                  string->ips_sv_copied = 0;
                  scursor = reinterpret_cast < char *>(string->ips_sv_addr);

                  /* start extraction */

                  while (string->ips_sv_copied < string->ips_sv_size)
                  {

                        /* extract all or whatever's available */

                        to_copy = string->ips_sv_size - string->ips_sv_copied;
                        if (string->ips_com_size <= to_copy)
                              to_copy = string->ips_com_size;
                        acursor = (TEXT *) comm->ips_data + string->ips_com_offset;
                        memcpy(scursor, acursor, (USHORT) to_copy);
                        scursor += to_copy;
                        string->ips_sv_copied += to_copy;

                        /* if there is more to copy, wait for it */

                        if (string->ips_sv_copied < string->ips_sv_size)
                        {
                              comm->ips_sequence++;
                              if (!send_and_wait(icc))
                                    return FALSE;
                        }
                  }
            }
      }
      return TRUE;
}


static void unwind( ICC icc)
{
/**************************************
 *
 *      u n w i n d
 *
 **************************************
 *
 * Functional description
 *      Unwind a request.
 *
 **************************************/
      IRQ request;
      USHORT level;
      ISC_STATUS_ARRAY status_vector;
      ips_object *ips;
      ips_comm_area *comm;
      TEXT *comm_ptr;


      /* set up communications area */

      GET_COMM_OBJECT;
      ips = &comm->ips_operations.ips_op_object;
      request = (IRQ) ips->ips_handle;
      CHECK_HANDLE(request, type_irq, isc_bad_req_handle);
      level = (USHORT) ips->ips_parameter;

      /* unwind the request */

      GDS_UNWIND(status_vector, GDS_REF(request->irq_handle), level);
      send_response(icc, status_vector);
}


static BOOL wait_no_send( ICC icc)
{
/**************************************
 *
 *      w a i t _ n o _ s e n d
 *
 **************************************
 *
 * Functional description
 *      This function waits for the client's next
 *      request by waiting on the server's semaphore.
 *
 **************************************/
      DWORD result1;
      DWORD result2;
      DWORD error1;
      DWORD error2;
      USHORT ret;


      /* already in shutdown mode */

      if (icc->icc_flags & ICCF_SHUTDOWN)
            return FALSE;

      /* wait for the server to signal us back */

      ret = TRUE;
      while (ret)
      {

            /* wait for semaphore for a minute */

            result1 = WaitForSingleObject(icc->icc_waits[0], 10000L);
            error1 = GetLastError();

            /* check for success */

            if (result1 == WAIT_OBJECT_0)
                  break;

            /* a non-timeout result is an error */

            if (result1 != WAIT_TIMEOUT)
            {
#ifdef  DEBUG_IP_TRACE
                  gds__log("ipserver wait no send failed %lX %ld %ld",
                               (long) icc, result1, error1);
                  gds__log("ipserver ids %lX %lX",
                               icc->icc_waits[0], icc->icc_waits[1]);
#endif
                  ret = 0;
                  break;
            }

            /* see if the client is still alive */

            result2 = WaitForSingleObject(icc->icc_waits[1], 1L);
            error2 = GetLastError();

            /* if process is gone, or there's some error, bag it */

            if (result2 != WAIT_TIMEOUT)
            {
#ifdef  DEBUG_IP_TRACE
                  gds__log("ip server wait no send failed again %lX %ld %ld",
                               (long) icc, result2, error2);
#endif
                  ret = 0;
                  break;
            }
      }
      if (!ret)
            icc->icc_flags |= ICCF_SHUTDOWN;
      return ret;
}

Generated by  Doxygen 1.6.0   Back to index