## root/dbeng/dbcs.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. db_initialize
2. db_open
3. db_close
4. db_next
5. db_top
6. db_get_rec
7. db_get_rec_size
8. db_get_field_size
9. db_get_subfield_size
10. db_get_subsubfield_size
11. db_get_field
12. db_get_subfield
13. db_get_subsubfield
14. db_goto
15. db_count
16. db_put_field
17. db_put_subfield
18. db_put_subsubfield
19. db_write
20. db_delete
21. db_get_delete_flag
22. db_set_delete_flag
23. db_pack
24. db_find
25. db_find_field
26. db_find_part
27. db_find_field_part
28. db_get_nfields
29. db_get_nsubfields
30. db_get_nsubsubfields
31. db_get_rec_num
32. db_get_pos
33. db_set_pos
34. db_sort
35. db_get_sort_max_mem
36. db_set_sort_max_mem
37. db_get_sort_max_open_bin
38. db_set_sort_max_open_bin
39. db_get_change_rec_flag
40. db_set_change_rec_flag
41. db_get_enf_change_rec_flag
42. db_set_enf_change_rec_flag
43. db_get_autopack
44. db_set_autopack
45. db_get_rec_count
46. db_new
47. db_get_is_table_locked
48. db_set_is_table_locked
49. db_status
50. db_new_table
51. db_version
52. db_get_open_table_list
53. db_delete_table
54. db_exist
55. db_clear_table
56. db_copy_table
57. db_get_active
58. db_replicate_update
59. db_get_catalog_list
60. db_delete_field
61. db_delete_subfield
62. db_delete_subsubfield
63. db_trans_num
64. db_connect_num
65. db_end
66. db_cs_int
67. db_cs_long
68. db_cs_code
69. db_cs_char
70. db_cs_client_connect
71. db_failover

    /* A high level api module for the client/server version of Bbuuzzb.
       Rick Smereka, Copyright (C) 1997-2004.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       You can contact the author via email at rsmereka@future-lab.com

       Original QNX version Feb/97, Rick Smereka

       Complete re-write for the new client/server API. Dec/97, Rick Smereka

       Added function 'db_set_pos'. Nov/98, Rick Smereka

       Ported to 32bit Windows under CodeWarrior and TCP connection
       oriented. Note that this interface does not use the IPC library
       (with the exception of the 'IPCOMM.C' low level routines) since
       the client of this API might be a TCP server whose executable
       will be bound to the IPC routines in 'IPCSRV.C'.
       Dec/98, Rick Smereka

       Separated platform specific code using '#ifdef' statements. Ported
       to HP-UX under GNU C V2.8.1. Jan/99, Rick Smereka

       Modified 'db_cs_int' and 'db_cs_long' to load the reply
       value even if the i/o code is not 'DBENG_OK'. Added
       functions 'db_get_tmp_path' and 'db_get_error_log'.
       Moved and renamed 'db_get_tmp_path' and
       'db_get_error_log' to 'dbcscfg.c'. Feb/99, Rick Smereka

       Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka

       Removed all references to 'WSA' functions (WinSock) and
       the data structure since it now is the responsibility
       of the application to call these functions. Renamed the
       socket structures by adding the prefix 'db_'. Cleaned
       up debug messages. Modified to use 'socloc' interface.
       Added function 'db_version' to obtain server version.
       Implemented failover detection. This API does not
       provide complete automatic failover. A failover will
       be detected and the return code will be 'DBENG_FAILOVER'
       which means that the original server connected to has
       failed and a connection to another Bbuuzzb server has
       been successfully established. The tables open at the
       time of the failover with the original server will
       not be open with the new server. This is why the
       command is not re-tried. It is up to the application
       to decide what to do when a Bbuuzzb server failover
       ocurrs. Added function 'db_get_active' to obtain the
       host name and TCP/IP port number of the current
       'Bbuuzzb' server. Apr/2000, Rick Smereka

       Added function 'db_get_open_table_list', Added quotes
       around the file name in functions 'db_open' and
       'db_new_table' just in case the file name contains
       spaces. Jun/2001, Rick Smereka

       Modified for use with both sockets and QNX message passing.
       Oct/2001, Rick Smereka

       Added functions 'db_get_nsubfields', 'db_get_subfield_size',
       'db_get_subfield', 'db_put_subfield', 'db_get_nsubsubfields',
       'db_get_subsubfield_size', 'db_get_subsubfields' and
       'db_put_subsubfield'. Jan/2002, Rick Smereka

       Added function 'db_replicate_update' which is available
       only using TCP IPC method. Mar/2002, Rick Smereka

       Added support for automatic field, subfield and subsubfield
       append using the value zero. Functions 'db_put_field',
       'db_put_subfield' and 'db_put_subsubfield' now allow a zero
       value (in the field, subfield and subsubfield number parameter)
       to signify an append. Added function 'db_delete_table'. Added
       function 'db_exist'. May/2002, Rick Smereka

       Added functions 'db_clear_table' and 'db_copy_table'.
       Jun/2002, Rick Smereka

       Added function 'db_get_catalog_list'. Added parameter
       to the function 'db_get_open_table_list'. Aug/2002,
       Rick Smereka

       Added functions 'db_sort', 'db_get_sort_max_mem',
       'db_set_sort_max_mem', 'db_get_sort_max_open_bin'
       and 'db_set_sort_max_open_bin'. Ported to Debian
       Linux. Nov/2002, Rick Smereka

       Added functions 'db_delete_field', 'db_delete_subfield'
       and 'db_delete_subsubfield'. Mar/2003, Rick Smereka

       Re-wrote to keep socket open until either a socket
       communication error or until 'db_end' is called (TCP only).
       Any application using this API must call 'db_initialize'
       upon start and 'db_end' upon stop.

       Added functions 'db_trans_num' and 'db_connect_num'.
       Jun/2003, Rick Smereka

       Added parameter to functions 'db_count' and 'db_get_rec_count'.
       Feb/2004, Rick Smereka

       Added functions 'db_get_autopack' and 'db_set_autopack'.
       Changed all logging calls from 'sys_log' to 'logman'.
       Mar/2004, Rick Smereka

       Added a call to 'db_config_client_init' from 'db_initialize'.
       Apr/2004, Rick Smereka */

    #include "stdhead.h"    /* standard include */
    #include "flsocket.h"   /* standard IPC defines */
    #include "dbmess.h"     /* send and reply message types and codes */
    #include "dbcs.h"       /* this module's header */

    #ifdef IPC_TCP
    #include "ipcomm.h"     /* low level socket send/receive */
    #include "socloc.h"     /* socloc defines */
    #include "sloc.h"       /* socloc main API functions */
    #include "sliocode.h"   /* socloc error translation */
    #endif

    /* global (to module) data */

    #ifdef IPC_TCP
    #ifdef OS_WIN32
    SOCKET db_clientSocket;           /* client socket */
    SOCKADDR_IN db_sockClientAddr;    /* client address structure */
    LPHOSTENT db_lpHostEnt;           /* host info structure */
    #endif

    #ifdef OS_UNIX
    int db_clientSocket;
    struct sockaddr_in db_sockClientAddr;
    struct hostent *db_lpHostEnt;
    #endif
    char dbeng_hostname[128];         /* host name of Bbuuzzb server */
    int dbeng_port = 0;               /* port Bbuuzzb is using */
    int dbeng_is_connect;             /* are we connected to a server? */
    #else
    pid_t dbeng_pid = -1;
    #endif

    /* private functions */

    #ifdef IPC_TCP
    static int db_cs_client_connect(void);
    static int db_failover(void);
    #endif

    int db_initialize(void)
    {
       /* Initialize the client for communication with the
          dbeng server. An attempt to get the dbeng server status
          is made.  Function returns 'DBENG_OK' if a successful
          connection was made an engine i/o code otherwise. */

       char mname[] = "db_initialize";
       char mes[128];
       int ret;

       logman("%s:enter", mname);
       db_config_client_init();

    #ifdef IPC_TCP
       dbeng_hostname[0] = EOS;
       dbeng_port = 0;
       dbeng_is_connect = FALSE;

       /* make sure 'socloc' has already been initialized */

       if ((ret = sloc_is_init()) != SL_OK)
          {
          sl_code_string(ret, mes);
          logman("%s:bad rc[%d,%s][sloc_is_init]", mname,
                        ret, mes);
          return(DBENG_SOCLOC_NO_INIT);
          }

       /* locate a 'Bbuuzzb' server */

       if ((ret = sloc_find(DBENG_SERVICE_NAME, dbeng_hostname,
                            &dbeng_port, (char *)NULL)) != SL_OK)
          {
          /* it is assumed that the failure is 'not found' */

          sl_code_string(ret, mes);
          logman("%s:bad rc[%d,%s][sloc_find]", mname,
                        ret, mes);
          return(DBENG_NO_SERVER);
          }

       /* connect to the server */

       if (!db_cs_client_connect())
          {
          logman("%s:bad rc[FALSE] from db_cs_client_connect", mname);
          return(DBENG_VC_ERROR);
          }
    #else
       /* use QNX 'nameloc' to find the server */

       if ((dbeng_pid = qnx_name_locate(0, DBENG_SERVICE_NAME, 0, NULL)) == -1)
          {
          logman("%s:bad rc[-1][qnx_name_locate]", mname);
          return(DBENG_NO_SERVER);
          }
    #endif

       return(DBENG_OK);
    }

    int db_open(char *filename, int *tid)
    {
       /* Open a table. The 'filename' is mandatory and consists of the
          full path and filename. File name must be no larger than
          1023 bytes each and must not contain spaces. Function returns
          'DBENG_OK' upon success, a error code otherwise. The table
          ID is returned in 'tid' upon success. */

       struct dbeng_send_message *slm;
       char mname[25];
       int ret, size_s_mestruct;

       strcpy(mname, "db_open");
       logman("%s:enter", mname);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if (filename == NULL || !strlen(filename))
          {
          db_io_code_log(mname, "invalid[filename]", DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

       /* put a reasonable 1kb limit on the file name itself */

       if (strlen(filename) > 1023)
          {
          db_io_code_log(mname, "too long[filename]", DBENG_FILENAME_TOO_LONG);
          return(DBENG_FILENAME_TOO_LONG);
          }

       *tid = 0;

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_OPEN;

       /* put quotes around filename in case it contains spaces */

       sprintf(slm->dbc, "'%s'", filename);
       ret = db_cs_int(slm, tid);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_close(int tid)
    {
       /* Close a table. Table ID is expected in 'tid'.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_close");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_CLOSE;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_next(int tid)
    {
       /* Perform next record command. Function returns 'DBENG_OK' upon
          success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_next");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_NEXT;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_top(int tid)
    {
       /* Goto the top of a table. Function returns 'DBENG_OK' upon
          success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_top");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_TOP;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_rec(int tid, char *rec_out)
    {
       /* Get complete table record. The file pointer is not advanced to
          the next record. The current record contents are returned.
          Record data is returned in 'rec_out' upon success.
          'rec_out' must be large enough to hold the record.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_rec");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (rec_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[rec_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       rec_out[0] = EOS;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_REC;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_char(slm, rec_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_rec_size(int tid, int *rec_size)
    {
       /* Get table record size or length. Record size is returned in
          'rec_size' upon success. Function returns 'DBENG_OK' upon
          success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_rec_size");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (rec_size == NULL)
          {
          db_io_code_log(mname, "null[rec_size]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);
       *rec_size = 0;

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_REC_SIZE;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, rec_size);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_field_size(int tid, int fnum, int *field_size)
    {
       /* Get size of a field in the current record. Variable 'field_size'
          will be loaded with the field size upon success.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_field_size");

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "out of range[fnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_size == (int *)NULL)
          {
          db_io_code_log(mname, "null[field_size]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *field_size = 0;
       logman("%s:enter,t=%d,f=%d", mname, tid, fnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_FIELD_SIZE;
       sprintf(slm->dbc, "%d %d", tid, fnum);
       ret = db_cs_int(slm, field_size);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_subfield_size(int tid, int fnum, int sfnum, int *subfield_size)
    {
       /* Get size of a sub-field in a field. Variable 'subfield_size'
          will be loaded with the sub-field size upon success.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_subfield_size";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "out of range[fnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (sfnum <= 0)
          {
          db_io_code_log(mname, "out of range[sfnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_size == (int *)NULL)
          {
          db_io_code_log(mname, "null[subfield_size]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *subfield_size = 0;
       logman("%s:enter,t=%d,f=%d,sf=%d", mname, tid, fnum, sfnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SUBFIELD_SIZE;
       sprintf(slm->dbc, "%d %d %d", tid, fnum, sfnum);
       ret = db_cs_int(slm, subfield_size);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_subsubfield_size(int tid, int fnum, int sfnum, int ssfnum,
                                int *subsubfield_size)
    {
       /* Get size of a sub-sub-field in a sub-field. Variable 'subsubfield_size'
          will be loaded with the sub-sub-field size upon success.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_subsubfield_size";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "out of range[fnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (sfnum <= 0)
          {
          db_io_code_log(mname, "out of range[sfnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ssfnum <= 0)
          {
          db_io_code_log(mname, "out of range[ssfnum]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subsubfield_size == (int *)NULL)
          {
          db_io_code_log(mname, "null[subsubfield_size]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *subsubfield_size = 0;
       logman("%s:enter,t=%d,f=%d,sf=%d,ssf=%d", mname, tid, fnum,
                     sfnum, ssfnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SUBSUBFIELD_SIZE;
       sprintf(slm->dbc, "%d %d %d %d", tid, fnum, sfnum, ssfnum);
       ret = db_cs_int(slm, subsubfield_size);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_field(int tid, int field_num, char *field_out)
    {
       /* Get field data. Field contents are returned in 'field_out'
          upon success. Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_field");

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "illegal field number", DBENG_NO_SUCH_FIELD);
          return(DBENG_NO_SUCH_FIELD);
          }

       if (field_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[field_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       field_out[0] = EOS;
       logman("%s:enter,t=%d,f=%d", mname, tid, field_num);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_FIELD;
       sprintf(slm->dbc, "%d %d", tid, field_num);
       ret = db_cs_char(slm, field_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_subfield(int tid, int field_num, int subfield_num,
                        char *subfield_out)
    {
       /* Get sub-field data. Sub-field contents are returned in 'subfield_out'
          upon success. Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_subfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_num <= 0)
          {
          db_io_code_log(mname, "out of range[subfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[subfield_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       subfield_out[0] = EOS;
       logman("%s:enter,t=%d,f=%d,sf=%d", mname, tid, field_num,
                     subfield_num);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SUBFIELD;
       sprintf(slm->dbc, "%d %d %d", tid, field_num, subfield_num);
       ret = db_cs_char(slm, subfield_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_subsubfield(int tid, int field_num, int subfield_num,
                           int subsubfield_num, char *subsubfield_out)
    {
       /* Get sub-sub-field data. Sub-sub-field contents are returned in
          'subsubfield_out' upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_subsubfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_num <= 0)
          {
          db_io_code_log(mname, "out of range[subfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subsubfield_num <= 0)
          {
          db_io_code_log(mname, "out of range[subsubfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subsubfield_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[subsubfield_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       subsubfield_out[0] = EOS;
       logman("%s:enter,t=%d,f=%d,sf=%d,ssf=%d", mname, tid, field_num,
                     subfield_num, subsubfield_num);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SUBSUBFIELD;
       sprintf(slm->dbc, "%d %d %d %d", tid, field_num, subfield_num,
               subsubfield_num);
       ret = db_cs_char(slm, subsubfield_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_goto(int tid, long rec_num)
    {
       /* Goto a specific record number. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_goto");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (rec_num <= 0L)
          {
          db_io_code_log(mname, "illegal record number", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GOTO;
       sprintf(slm->dbc, "%d %ld", tid, rec_num);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_count(int tid, long *active_num, long *deleted_num)
    {
       /* Count the number of records in a table.
          Function returns 'DBENG_OK' with the record count loaded upon success,
          an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_count", buf[128], wrd[128];
       long tmp;
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (active_num == (long *)NULL)
          {
          db_io_code_log(mname, "null[active_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (deleted_num == (long *)NULL)
          {
          db_io_code_log(mname, "null[deleted_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *active_num = *deleted_num = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_COUNT;
       sprintf(slm->dbc, "%d", tid);

       // server sends back two long int's

       ret = db_cs_char(slm, buf);
       free(slm);

       if (ret == DBENG_OK)
          {
          if (words(buf) < 2)
             {
             db_io_code_log(mname, "expected two counts", DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!word(buf, wrd, 1))
             {
             db_io_code_log(mname, "error extracting active count",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!qatol(wrd, active_num))
             {
             db_io_code_log(mname, "active count not numeric",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!word(buf, wrd, 2))
             {
             db_io_code_log(mname, "error extracting deleted count",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!qatol(wrd, deleted_num))
             {
             db_io_code_log(mname, "deleted count not numeric",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }
          }

       logman("%s:exit[%d]active=%ld,deleted=%ld", mname, ret, *active_num,
                     *deleted_num);
       return(ret);
    }

    int db_put_field(int tid, int field_num, char *field_data)
    {
       /* Place a string into a field in the current record. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_put_field");

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num < 0)
          {
          db_io_code_log(mname, "illegal field number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_data == NULL)
          {
          db_io_code_log(mname, "null[field_data]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d,d=%s,l=%d", mname, tid, field_num,
               field_data, strlen(field_data));

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* surround data with single quotes just in case data
          contains one or more spaces */

       sprintf(slm->dbc, "%d %d '%s'", tid, field_num, field_data);
       slm->type = DBENG_SEND_PUT_FIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_put_subfield(int tid, int field_num, int subfield_num, char *sf_data)
    {
       /* Place a string into a sub-field into a field. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_put_subfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num < 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_num < 0)
          {
          db_io_code_log(mname, "out of range[subfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (sf_data == (char *)NULL)
          {
          db_io_code_log(mname, "null[sf_data]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d,sf=%d,d=%s,l=%d", mname, tid, field_num,
                     subfield_num, sf_data, strlen(sf_data));

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* surround data with single quotes just in case data
          contains one or more spaces */

       sprintf(slm->dbc, "%d %d %d '%s'", tid, field_num, subfield_num, sf_data);
       slm->type = DBENG_SEND_PUT_SUBFIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_put_subsubfield(int tid, int field_num, int subfield_num,
                           int subsubfield_num, char *ssf_data)
    {
       /* Place a string into a sub-sub-field into a sub-field. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_put_subsubfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num < 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_num < 0)
          {
          db_io_code_log(mname, "out of range[subfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subsubfield_num < 0)
          {
          db_io_code_log(mname, "out of range[subsubfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ssf_data == (char *)NULL)
          {
          db_io_code_log(mname, "null[ssf_data]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d,sf=%d,ssf=%d,d=%s,l=%d", mname, tid,
                     field_num, subfield_num, subsubfield_num, ssf_data,
                     strlen(ssf_data));
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* surround data with single quotes just in case data
          contains one or more spaces */

       sprintf(slm->dbc, "%d %d %d %d '%s'", tid, field_num, subfield_num,
               subsubfield_num, ssf_data);
       slm->type = DBENG_SEND_PUT_SUBSUBFIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_write(int tid)
    {
       /* Write a record to the table. If the record already exists, it
          will be re-written, otherwise a new record will be written.
          Field data must have already been placed into the record.
          Function returns 'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_write");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_WRITE;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_delete(int tid)
    {
       /* Delete the current record from a table. Only the current record
          will be deleted. Function returns 'DBENG_OK' upon success, an
          error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_delete");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_DELETE;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_delete_flag(int tid, int *value)
    {
       /* Get the 'process_deleted' flag for a table. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_delete_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (value == (int *)NULL)
          {
          db_io_code_log(mname, "null[value]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *value = FALSE;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_DELETE_FLAG;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, value);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_delete_flag(int tid, int value)
    {
       /* Set the 'process_deleted' flag for a table. 'value' must be one of 'TRUE'
          or 'FALSE'. Function returns 'DBENG_OK' upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_set_delete_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (value != TRUE && value != FALSE)
          {
          db_io_code_log(mname, "not 0|1[value]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_DELETE_FLAG;
       sprintf(slm->dbc, "%d %d", tid, value);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_pack(int tid)
    {
       /* Pack a table to remove deleted records. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_pack");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_PACK;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_find(int tid, int cs_flag, char *fdata, int *field_num)
    {
       /* Find data in a table field. Only whole fields will match.
          All fields in each record will be searched. Comparison
          is based on 'cs_flag' for case sensitivity ('1' is case
          insensitivity, '0' is case sensitivity). Field number that
          matched will be loaded into 'field_num' upon a successful find.
          Function will return 'DBENG_OK' if a record was found, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_find");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (cs_flag != TRUE && cs_flag != FALSE)
          {
          db_io_code_log(mname, "not 0|1[cs_flag]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (fdata == NULL)
          {
          db_io_code_log(mname, "null[fdata]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_num == NULL)
          {
          db_io_code_log(mname, "null[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *field_num = 0;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_FIND;
       sprintf(slm->dbc, "%d '%s' %d", tid, fdata, cs_flag);
       ret = db_cs_int(slm, field_num);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_find_field(int tid, int cs_flag, char *fdata, int field_num)
    {
       /* Find data in a table field. Only whole field will match.
          Only field 'field_num' will be searched in each record. String
          comparison is based on 'cs_flag' for case sensitivity.('1' is case
          insensitivity, '0' is case sensitivity). Function will return
          'DBENG_OK' if a match was found, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_find_field");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (cs_flag != TRUE && cs_flag != FALSE)
          {
          db_io_code_log(mname, "not 0|1[cs_flag]",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (fdata == NULL)
          {
          db_io_code_log(mname, "null[fdata]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "illegal[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_FIND_FIELD;
       sprintf(slm->dbc, "%d %d '%s' %d", tid, field_num, fdata, cs_flag);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_find_part(int tid, int cs_flag, char *fdata, int *field_num)
    {
       /* Find data in a table field. Any part of a field will match.
          All fields in each record will be searched. Comparison
          is based on 'cs_flag' for case sensitivity ('1' is case
          insensitivity, '0' is case sensitivity). Field number that
          matched will be loaded into 'field_num' upon a successful find.
          Function will return 'DBENG_OK' if a record was found, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_find_part");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (cs_flag != TRUE && cs_flag != FALSE)
          {
          db_io_code_log(mname, "not 0|1[cs_flag]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (fdata == NULL)
          {
          db_io_code_log(mname, "null[fdata]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_num == NULL)
          {
          db_io_code_log(mname, "null[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *field_num = 0;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_FIND_PART;
       sprintf(slm->dbc, "%d '%s' %d", tid, fdata, cs_flag);
       ret = db_cs_int(slm, field_num);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_find_field_part(int tid, int cs_flag, char *fdata, int field_num)
    {
       /* Find data in a table field. Any part of the field will match.
          Only field 'field_num' will be searched in each record. String
          comparison is based on 'cs_flag' for case sensitivity.('1' is case
          insensitivity, '0' is case sensitivity). Function will return
          'DBENG_OK' if a match was found, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_find_field_part");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (cs_flag != TRUE && cs_flag != FALSE)
          {
          db_io_code_log(mname, "not 0|1[cs_flag]",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (fdata == NULL)
          {
          db_io_code_log(mname, "null[fdata]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "illegal[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_FIND_FIELD_PART;
       sprintf(slm->dbc, "%d %d '%s' %d", tid, field_num, fdata, cs_flag);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_nfields(int tid, int *nfields)
    {
       /* Get number of fields in the current record. Number of fields is
          returned in 'nfields' upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_nfields");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (nfields == (int *)NULL)
          {
          db_io_code_log(mname, "null[nfields]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);
       *nfields = 0;

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_NFIELDS;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, nfields);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_nsubfields(int tid, int field_num, int *nsubfields)
    {
       /* Get number of sub-fields in a field. Number of sub-fields is
          returned in 'nsubfields' upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_nsubfields";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (nsubfields == (int *)NULL)
          {
          db_io_code_log(mname, "null[nsubfields]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);
       *nsubfields = 0;

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_NSUBFIELDS;
       sprintf(slm->dbc, "%d %d", tid, field_num);
       ret = db_cs_int(slm, nsubfields);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_nsubsubfields(int tid, int field_num, int subfield_num,
                             int *nsubsubfields)
    {
       /* Get number of sub-sub-fields in a sub-field. Number of sub-sub-fields is
          returned in 'nsubsubfields' upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_nsubsubfields";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (field_num <= 0)
          {
          db_io_code_log(mname, "out of range[field_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (subfield_num <= 0)
          {
          db_io_code_log(mname, "out of range[subfield_num]",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (nsubsubfields == (int *)NULL)
          {
          db_io_code_log(mname, "null[nsubsubfields]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);
       *nsubsubfields = 0;

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_NSUBSUBFIELDS;
       sprintf(slm->dbc, "%d %d %d", tid, field_num, subfield_num);
       ret = db_cs_int(slm, nsubsubfields);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_rec_num(int tid, long *rec_num)
    {
       /* Get the current record number. The record number is returned in
          'rec_num' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_rec_num");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (rec_num == (long *)NULL)
          {
          db_io_code_log(mname, "null[rec_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *rec_num = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_REC_NUM;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_long(slm, rec_num);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_pos(int tid, long *pos)
    {
       /* Get the current record starting file position. The position is
          returned in 'pos' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_pos");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (pos == (long *)NULL)
          {
          db_io_code_log(mname, "null[pos]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *pos = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_POS;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_long(slm, pos);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_pos(int tid, long pos)
    {
       /* Set the table file position. BE VERY CAREFUL.
          Make sure there is a record at the specified
          file position. This function, if misused,
          can cause the engine to shutdown. Function
          returns 'DBENG_OK' upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_set_pos");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (pos < 0L)
          {
          db_io_code_log(mname, "invalid[pos]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_POS;
       sprintf(slm->dbc, "%d %ld", tid, pos);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_sort(int tid, char *specs)
    {
       /* Sort a table. Sort specs are expected in 'specs'.
          Function returns 'DBENG_OK' upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_sort";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (specs == (char *)NULL || !strlen(specs))
          {
          db_io_code_log(mname, "null or empty[spces]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SORT;
       sprintf(slm->dbc, "%d %s", tid, specs);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_sort_max_mem(int tid, long *mem)
    {
       /* Get the current allowable sort max memory allocation. The value is
          returned in 'mem' along with 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_sort_max_mem";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (mem == (long *)NULL)
          {
          db_io_code_log(mname, "null[mem]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *mem = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SORT_MEM;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_long(slm, mem);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_sort_max_mem(int tid, long mem)
    {
       /* Set the allowable maximum sort memory.
          Function returns a 'dbeng' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_set_sort_max_mem";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (mem < 0L)
          {
          db_io_code_log(mname, "invalid[mem]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_SORT_MEM;
       sprintf(slm->dbc, "%d %ld", tid, mem);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_sort_max_open_bin(int tid, int *open_bin)
    {
       /* Get the current allowable sort number of open bin tables. The value is
          returned in 'open_bin' along with 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_sort_max_open_bin";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (open_bin == (int *)NULL)
          {
          db_io_code_log(mname, "null[open_bin]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *open_bin = 0;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_SORT_OPEN_BIN;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, open_bin);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_sort_max_open_bin(int tid, int open_bin)
    {
       /* Set the allowable maximum number of open bin tables during sort.
          Function returns a 'dbeng' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_set_sort_max_open_bin";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (open_bin <= 0)
          {
          db_io_code_log(mname, "invalid[open_bin]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_SORT_OPEN_BIN;
       sprintf(slm->dbc, "%d %d", tid, open_bin);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_change_rec_flag(int tid, int *flag)
    {
       /* Get the current value of 'change_rec_flag'. The flag value is
          returned in 'flag' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_change_rec_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag == (int *)NULL)
          {
          db_io_code_log(mname, "null[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *flag = FALSE;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_CHANGE_REC_FLAG;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, flag);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_change_rec_flag(int tid, int flag)
    {
       /* Set the value of 'change_rec_flag'. The function returns
          'DBENG_OK' upon success, an error code upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_set_change_rec_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag != TRUE && flag != FALSE)
          {
          db_io_code_log(mname, "invalid[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_CHANGE_REC_FLAG;
       sprintf(slm->dbc, "%d %d", tid, flag);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_enf_change_rec_flag(int tid, int *flag)
    {
       /* Get the current value of 'enforce_change_rec_flag'. The flag value is
          returned in 'flag' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[30];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_enf_change_rec_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag == (int *)NULL)
          {
          db_io_code_log(mname, "null[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *flag = FALSE;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_ENF_CHANGE_REC_FLAG;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, flag);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_enf_change_rec_flag(int tid, int flag)
    {
       /* Set the value of 'enforce_change_rec_flag'. The function returns
          'DBENG_OK' upon success, an error code upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[30];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_set_enf_change_rec_flag");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag != TRUE && flag != FALSE)
          {
          db_io_code_log(mname, "invalid[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_ENF_CHANGE_REC_FLAG;
       sprintf(slm->dbc, "%d %d", tid, flag);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_autopack(int tid, int *pval)
    {
       /* Get the current value of 'autopack'. The value is
          returned in 'pval' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_autopack";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (pval == (int *)NULL)
          {
          db_io_code_log(mname, "null[pval]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *pval = FALSE;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_AUTOPACK;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, pval);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_autopack(int tid, int pval)
    {
       /* Set the value of 'autopack'. The function returns
          'DBENG_OK' upon success, an error code upon success, an error
          code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_set_autopack";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (pval < 0)
          {
          db_io_code_log(mname, "invalid[pval]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_AUTOPACK;
       sprintf(slm->dbc, "%d %d", tid, pval);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_rec_count(int tid, long *active_num, long *deleted_num)
    {
       /* Get the active and deleted record count. The count value is
          returned along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. Note that this function
          does not actually count the records in the table but reports
          on what the database manager thinks is the record count. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_rec_count", buf[128], wrd[128];
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (active_num == (long *)NULL)
          {
          db_io_code_log(mname, "null[active_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (deleted_num == (long *)NULL)
          {
          db_io_code_log(mname, "null[deleted_num]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *active_num = *deleted_num = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_REC_COUNT;
       sprintf(slm->dbc, "%d", tid);

       // server replies with two counts

       ret = db_cs_char(slm, buf);
       free(slm);

       if (ret == DBENG_OK)
          {
          if (words(buf) < 2)
             {
             db_io_code_log(mname, "expected two counts", DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!word(buf, wrd, 1))
             {
             db_io_code_log(mname, "error extracting active count",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!qatol(wrd, active_num))
             {
             db_io_code_log(mname, "active count not numeric",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!word(buf, wrd, 2))
             {
             db_io_code_log(mname, "error extracting deleted count",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if (!qatol(wrd, deleted_num))
             {
             db_io_code_log(mname, "deleted count not numeric",
                            DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }
          }

       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_new(int tid)
    {
       /* Prepare for a new record. Function will return 'DBENG_OK' upon
          success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_new");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_NEW;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_is_table_locked(int tid, int *flag)
    {
       /* Get the current value of 'is_table_locked'. The flag value is
          returned in 'flag' along with 'DBENG_OK' upon success, an error code
          upon success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_get_is_table_locked");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag == (int *)NULL)
          {
          db_io_code_log(mname, "null[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *flag = FALSE;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_IS_TABLE_LOCKED;
       sprintf(slm->dbc, "%d", tid);
       ret = db_cs_int(slm, flag);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_set_is_table_locked(int tid, int flag)
    {
       /* Set the value of 'is_table_locked'. The function returns
          'DBENG_OK' upon success, an error code upon success, an error
          code otherwise. The value of 'flag' must be zero or one. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_set_is_table_locked");
       logman("%s:enter", mname);

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (flag != TRUE && flag != FALSE)
          {
          db_io_code_log(mname, "invalid[flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_SET_IS_TABLE_LOCKED;
       sprintf(slm->dbc, "%d %d", tid, flag);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_status(void)
    {
       /* Get dbeng server status.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_status");
       logman("%s:enter", mname);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* fill in a dummy parameter just to satisfy
          the engine's minimum two word rule */

       slm->type = DBENG_SEND_STATUS;
       strcpy(slm->dbc, "dummy");
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_new_table(char *filename, int *tid)
    {
       /* Create a new table. If 'tid' is a positive
          number, also open the table. Function
          returns 'DBENG_OK' upon success with
          the table 'tid' loaded if open was
          requested. Function returns an engine
          i/o code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_new_table");
       logman("%s:enter", mname);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if (filename == NULL || !strlen(filename))
          {
          db_io_code_log(mname, "no file name", DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

       /* put a reasonable 1kb limit on the file name itself */

       if (strlen(filename) > 1023)
          {
          db_io_code_log(mname, "too long[filename]", DBENG_FILENAME_TOO_LONG);
          return(DBENG_FILENAME_TOO_LONG);
          }

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_NEW_TABLE;

       /* pass open flag as parameter and put quotes around
          filename in case it contains spaces */

       sprintf(slm->dbc, "'%s' %d", filename, *tid);

       // return new 'tid' of open requested

       ret = db_cs_int(slm, tid);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_version(char *ver)
    {
       /* Get the Bbuuzzb server version string. Version is returned in
          'ver' upon success. Function returns 'DBENG_OK' upon success,
          an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[25];
       int size_s_mestruct;
       int ret;

       strcpy(mname, "db_version");
       logman("%s:enter", mname);

       if (ver == (char *)NULL)
          {
          db_io_code_log(mname, "null[ver]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       ver[0] = EOS;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_VERSION;
       slm->dbc[0] = EOS;
       ret = db_cs_char(slm, ver);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_get_open_table_list(char *pat, char *list_out)
    {
       /* Get and return a list of open tables. List is returned in
          'list_out' upon success. 'list_out' must already be
          allocated to sufficient size. List is delimited by
          'DBENG_LIST_DELIM'. The parameter 'pat' may contain
          an acceptable pattern to the function 'pmatch' consisting
          of physical file names. Function returns 'DBENG_OK' upon
          success, an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_open_table_list";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (list_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[list_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       list_out[0] = EOS;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_OPEN_TABLE_LIST;

       if (pat != (char *)NULL && strlen(pat))
          strcpy(slm->dbc, pat);
       else
          slm->dbc[0] = EOS;

       ret = db_cs_char(slm, list_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_delete_table(char *tname)
    {
       /* Delete a 'Bbuuzzb' table.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_delete_table";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tname == (char *)NULL || !strlen(tname))
          {
          db_io_code_log(mname, "null or empty[tname]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_DELETE_TABLE;
       strcpy(slm->dbc, tname);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_exist(char *tname, int *exist_flag)
    {
       /* Determine whether a table exists.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_exist";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tname == (char *)NULL || !strlen(tname))
          {
          db_io_code_log(mname, "null or empty[tname]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (exist_flag == (int *)NULL)
          {
          db_io_code_log(mname, "null[exist_flag]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_EXIST;
       strcpy(slm->dbc, tname);
       ret = db_cs_int(slm, exist_flag);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_clear_table(char *tname)
    {
       /* Clear a table of all records.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_clear_table";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (tname == (char *)NULL || !strlen(tname))
          {
          db_io_code_log(mname, "null or empty[tname]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_CLEAR_TABLE;
       strcpy(slm->dbc, tname);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_copy_table(int src_tid, int dest_tid)
    {
       /* Copy a table from source to destination.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_copy_table";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (src_tid <= 0)
          {
          db_io_code_log(mname, "out of range[src_tid]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (dest_tid <= 0)
          {
          db_io_code_log(mname, "out of range[dest_tid]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_COPY_TABLE;
       sprintf(slm->dbc, "%d %d", src_tid, dest_tid);
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    #ifdef IPC_TCP
    void db_get_active(char *hname, int *port)
    {
       /* Get and return the active 'Bbuuzzb' server host name
          and port number. 'hname' must be allocated by the
          caller to sufficient size. */

       if (hname == (char *)NULL || port == (int *)NULL)
          return;

       hname[0] = EOS;
       *port = 0;

       if (!strlen(dbeng_hostname) || dbeng_port == 0)
          return;

       strcpy(hname, dbeng_hostname);
       *port = dbeng_port;
    }

    int db_replicate_update(void)
    {
       /* Update all replication details.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_replicate_update";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_REPLICATE_UPDATE;
       strcpy(slm->dbc, "dummy");
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }
    #endif

    int db_get_catalog_list(char *pat, char *list_out)
    {
       /* Get and return a list of catalog entries. List is returned in
          'list_out' upon success. 'list_out' must already be
          allocated to sufficient size. List is delimited by
          'DBENG_LIST_DELIM'. The parameter 'pat' may contain
          an acceptable pattern for the function 'pmatch' of
          logical table names. Function returns 'DBENG_OK' upon success,
          an error code otherwise. */

       struct dbeng_send_message *slm;
       char mname[] = "db_get_catalog_list";
       int size_s_mestruct;
       int ret;

       logman("%s:enter", mname);

       if (list_out == (char *)NULL)
          {
          db_io_code_log(mname, "null[list_out]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       list_out[0] = EOS;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_GET_CATALOG_LIST;

       if (pat != (char *)NULL && strlen(pat))
          strcpy(slm->dbc, pat);
       else
          slm->dbc[0] = EOS;

       ret = db_cs_char(slm, list_out);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_delete_field(int tid, int fnum)
    {
       /* Completly delete a field. Function returns a 'dbeng' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_delete_field";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "illegal field number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d", mname, tid, fnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       sprintf(slm->dbc, "%d %d", tid, fnum);
       slm->type = DBENG_SEND_DELETE_FIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_delete_subfield(int tid, int fnum, int sfnum)
    {
       /* Completly delete a subfield. Function returns a 'dbeng' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_delete_subfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "illegal field number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (sfnum <= 0)
          {
          db_io_code_log(mname, "illegal subfield number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d,sf=%d", mname, tid, fnum, sfnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       sprintf(slm->dbc, "%d %d %d", tid, fnum, sfnum);
       slm->type = DBENG_SEND_DELETE_SUBFIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_delete_subsubfield(int tid, int fnum, int sfnum, int ssfnum)
    {
       /* Completly delete a subsubfield. Function returns a 'dbeng' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_delete_subsubfield";
       int size_s_mestruct;
       int ret;

       if (tid <= 0)
          {
          db_io_code_log(mname, "invalid tid", DBENG_NO_SUCH_TID);
          return(DBENG_NO_SUCH_TID);
          }

       if (fnum <= 0)
          {
          db_io_code_log(mname, "illegal field number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (sfnum <= 0)
          {
          db_io_code_log(mname, "illegal subfield number",DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ssfnum <= 0)
          {
          db_io_code_log(mname, "illegal subsubfield number",
                         DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,t=%d,f=%d,sf=%d,ssf=%d", mname, tid, fnum,
                     sfnum, ssfnum);
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       sprintf(slm->dbc, "%d %d %d %d", tid, fnum, sfnum, ssfnum);
       slm->type = DBENG_SEND_DELETE_SUBSUBFIELD;
       ret = db_cs_code(slm);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_trans_num(long *trans_num)
    {
       /* Get the 'bbuuzzb' server current transaction count.
          Function returns the transaction count in 'trans_num'
          upon success. Function returns a dbeng code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_trans_num";
       int size_s_mestruct, ret;

       logman("%s:enter", mname);

       if (trans_num == (long *)NULL)
          {
          logman("%s:null parm[trans_num]", mname);
          return(DBENG_INVALID_FUNCTION);
          }

       *trans_num = 0L;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_TRANS_NUM;
       strcpy(slm->dbc, "dummy");
       ret = db_cs_long(slm, trans_num);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int db_connect_num(int *connect_num)
    {
       /* Get the bbuuzzb server current connection count.
          Function returns the connection count in 'connect_num'
          upon success. Function returns a 'bbuuzzb' code. */

       struct dbeng_send_message *slm;
       char mname[] = "db_connect_num";
       int size_s_mestruct, ret;

       logman("%s:enter", mname);

       if (connect_num == (int *)NULL)
          {
          logman("%s:null parm[connect_num]", mname);
          return(DBENG_INVALID_FUNCTION);
          }

       *connect_num = 0;
       size_s_mestruct = sizeof(struct dbeng_send_message);

       if ((slm = malloc(size_s_mestruct)) == NULL)
          {
          db_io_code_log(mname, "alloc fail[slm]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       slm->type = DBENG_SEND_CONNECT_NUM;
       strcpy(slm->dbc, "dummy");
       ret = db_cs_int(slm, connect_num);
       free(slm);
       db_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    void db_end(void)
    {
       // Shutdown the API and close any open socket (TCP).

       char mname[] = "db_end";

       logman("%s:enter", mname);

    #ifdef IPC_TCP
       dbeng_hostname[0] = EOS;
       dbeng_port = 0;
       dbeng_is_connect = FALSE;

       // only close socket upon API shutdown

    #ifdef OS_WIN32
       (void)closesocket(db_clientSocket);
    #else
       close(db_clientSocket);
    #endif
    #else
       dbeng_pid = -1;
    #endif

       logman("%s:normal exit", mname);
    }

    /* connect, send and receive functions */

    int db_cs_int(struct dbeng_send_message *slm, int *intvalue)
    {
       /* Send and recveive a message to the DBENG server that returns an
          integer value. Function returns a DBENG io code. The returned
          integer value will be placed into 'intvalue' upon success. */

       char *mess, tmp[50];
       int len, nwords, ret;

    #ifdef IPC_QNX
       char *reply;
       int i, sent = FALSE;
    #endif

    #ifdef IPC_TCP
       if (!dbeng_port)
    #else
       if (dbeng_pid == -1)
    #endif
          return(DBENG_NO_INIT);

       *intvalue = 0;

       // estimate length and alloc send/receive buffer

       len = strlen(slm->dbc) + 15;

       if ((mess = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       memset(mess, 0, len);

       // format send string

       if (len > 5)
          sprintf(mess, "%d %s", slm->type, slm->dbc);
       else
          sprintf(mess, "%d", slm->type);

       // send message

    #ifdef IPC_QNX
       if ((reply = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(dbeng_pid, mess, reply, len, len))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(reply);
          free(mess);
          return(DBENG_VC_ERROR);
          }

       strcpy(mess, reply);
       free(reply);
    #else
       if (!ipc_send(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }

       memset(mess, 0, len);

       // receive reply

       if (!ipc_recv(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }
    #endif

       // s/b two words in reply

       nwords = words(mess);

       // s/b reply code in word one

       if (!word(mess, tmp, 1))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       if (nwords < 2)
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // value is in second word

       if (!word(mess, tmp, 2))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       if (!qatoi(tmp, intvalue))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       free(mess);
       return(ret);
    }

    int db_cs_long(struct dbeng_send_message *slm, long *longvalue)
    {
       /* Send and recveive a message to the DBENG server that returns a
          long value. Function returns a DBENG io code. The returned
          integer value will be placed into 'intvalue' upon success. */

       char *mess, tmp[50];
       int len, nwords, ret;

    #ifdef IPC_QNX
       char *reply;
       int i, sent = FALSE;
    #endif

    #ifdef IPC_TCP
       if (!dbeng_port)
    #else
       if (dbeng_pid == -1)
    #endif
          return(DBENG_NO_INIT);

       *longvalue = 0L;

       // estimate length and alloc send/receive buffer

       len = strlen(slm->dbc) + 15;

       if ((mess = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       memset(mess, 0, len);

       // format send string

       if (len > 5)
          sprintf(mess, "%d %s", slm->type, slm->dbc);
       else
          sprintf(mess, "%d", slm->type);

       // send message

    #ifdef IPC_QNX
       if ((reply = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(dbeng_pid, mess, reply, len, len))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(reply);
          free(mess);
          return(DBENG_VC_ERROR);
          }

       strcpy(mess, reply);
       free(reply);
    #else
       if (!ipc_send(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }

       memset(mess, 0, len);

       // receive reply

       if (!ipc_recv(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }
    #endif

       // s/b two words in reply

       nwords = words(mess);

       // s/b reply code in word one

       if (!word(mess, tmp, 1))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       if (nwords < 2)
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // value is in second word

       if (!word(mess, tmp, 2))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       if (!qatol(tmp, longvalue))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       free(mess);
       return(ret);
    }

    int db_cs_code(struct dbeng_send_message *slm)
    {
       /* Send and recveive a message to the DBENG server that returns no
          other value except for the general io return code. Function makes
          sure that at least one DBENG database server is running. Function
          returns a DBENG io code. */

       char *mess;
       int i, len;

    #ifdef IPC_QNX
       char *reply;
       int sent = FALSE;
    #endif

    #ifdef IPC_TCP
       if (!dbeng_port)
    #else
       if (dbeng_pid == -1)
    #endif
          return(DBENG_NO_INIT);

       // connect to server

       len = strlen(slm->dbc) + 5;

       if ((mess = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       memset(mess, 0, len);

       if (len > 5)
          sprintf(mess, "%d %s", slm->type, slm->dbc);
       else
          sprintf(mess, "%d", slm->type);

       // send message

    #ifdef IPC_QNX
       if ((reply = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(dbeng_pid, mess, reply, len, len))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(reply);
          free(mess);
          return(DBENG_VC_ERROR);
          }

       strcpy(mess, reply);
       free(reply);
    #else
       if (!ipc_send(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }

       memset(mess, 0, len);

       // receive return code

       if (!ipc_recv(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }
    #endif

       // make sure its a number

       if (!qatoi(mess, &i))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       free(mess);
       return(i);
    }

    int db_cs_char(struct dbeng_send_message *slm, char *char_value)
    {
       /* Send and receive a message to the DBENG server that returns a
          string as well as an io return code. 'char_value' must already
          be allocated to sufficient size for the receiving string.
          Function returns a DBENG io code. */

       char *mess, tmp[50];
       int len, nwords, ret;

    #ifdef IPC_QNX
       char *reply;
       int i, sent = FALSE;
    #endif

    #ifdef IPC_TCP
       if (!dbeng_port)
    #else
       if (dbeng_pid == -1)
    #endif
          return(DBENG_NO_INIT);

       char_value[0] = EOS;

       // estimate length and alloc send buffer

       len = strlen(slm->dbc) + 5;

       if ((mess = (char *)malloc(len)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       memset(mess, 0, len);

       // format send string

       if (len > 5)
          sprintf(mess, "%d %s", slm->type, slm->dbc);
       else
          sprintf(mess, "%d", slm->type);

       // send message

    #ifdef IPC_QNX
       if ((reply = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(dbeng_pid, mess, reply, len, DBENG_MAXRECFIELD))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(reply);
          free(mess);
          return(DBENG_VC_ERROR);
          }

       free(mess);

       if ((mess = initstring(reply)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       free(reply);
    #else
       if (!ipc_send(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }

       free(mess);

       // re-allocate 'mess' for receive buffer (max size)

       if ((mess = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       memset(mess, 0, DBENG_MAXRECFIELD);

       // receive reply

       if (!ipc_recv(db_clientSocket, mess))
          {
          free(mess);
          return(db_failover());
          }
    #endif

       // s/b two words in reply

       nwords = command_words(mess);

       // s/b reply code in word one

       if (!command_word(mess, tmp, 1))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          free(mess);
          return(DBENG_INTERNAL_ERROR);
          }

       // if reply is 'ok', load 'char_value'

       if (ret == DBENG_OK)
          {
          if (nwords < 2)
             {
             free(mess);
             return(DBENG_INTERNAL_ERROR);
             }

          // 'char_value' is in second word

          if (!command_word(mess, char_value, 2))
             {
             free(mess);
             return(DBENG_INTERNAL_ERROR);
             }
          }

       free(mess);
       return(ret);
    }

    /* private functions */

    #ifdef IPC_TCP
    static int db_cs_client_connect(void)
    {
       /* Connect to the Bbuuzzb server. An attempt to open the TCP
          socket is made. The host name 'dbeng_hostname' and the
          TCP port 'dbeng_port' are assumed to be already loaded
          via 'db_initialize'. Function returns 'TRUE' if a
          successful connection was made, 'FALSE' otherwise.
          Private function. */

       char mname[] = "db_cs_client_connect";

       if (dbeng_is_connect)
          {
          logman("%s:already connected", mname);
          return(TRUE);
          }

       // resolve server host name

       db_lpHostEnt = gethostbyname(dbeng_hostname);

       if (!db_lpHostEnt)
          {
          logman("%s:unable to resolve bbuuzzb server host name", mname);
          return(FALSE);
          }

       // create the socket

    #ifdef OS_WIN32
       db_clientSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
    #endif

    #ifdef OS_UNIX
       db_clientSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
    #endif

       if (db_clientSocket == INVALID_SOCKET)
          {
          logman("%s:unable to open the bbuuzzb server socket", mname);
          return(FALSE);
          }

       // load client address data

       memset(&db_sockClientAddr, 0, sizeof(db_sockClientAddr));
       db_sockClientAddr.sin_family = AF_INET;
       db_sockClientAddr.sin_port = htons(dbeng_port);

    #ifdef OS_WIN32
       db_sockClientAddr.sin_addr = *((LPIN_ADDR)*db_lpHostEnt->h_addr_list);
    #endif

    #ifdef OS_UNIX
       db_sockClientAddr.sin_addr.s_addr = ((struct in_addr *)(db_lpHostEnt->h_addr))->s_addr;
    #endif

       // connect to server

    #ifdef OS_WIN32
       if (connect(db_clientSocket, (LPSOCKADDR)&db_sockClientAddr,
           sizeof(db_sockClientAddr)))
    #endif

    #ifdef OS_UNIX
       if (connect(db_clientSocket, (SA *)&db_sockClientAddr,
           sizeof(db_sockClientAddr)) == SOCKET_ERROR)
    #endif
          {
          logman("%s:error connecting the socket to the bbuuzzb "
                        "server", mname);
          return(FALSE);
          }

       dbeng_is_connect = TRUE;
       return(TRUE);
    }

    static int db_failover(void)
    {
       /* Failover to another 'Bbuuzzb' server. Function
          returns a 'dbeng' code. */

       char mname[] = "db_failover";
       char mes[128];
       int ret;

       logman("%s:enter", mname);
       dbeng_is_connect = FALSE;

       // close current socket

    #ifdef OS_WIN32
       (void)closesocket(db_clientSocket);
    #else
       close(db_clientSocket);
    #endif

       /* attempt to delete server that has failed */

       if ((ret = sloc_delete(dbeng_port)) != SL_OK)
          {
          /* if delete fails log a message but continue */

          sl_code_string(ret, mes);
          logman("%s:bad rc[%s] from sloc_delete", mname, mes);
          }

       dbeng_hostname[0] = EOS;
       dbeng_port = 0;

       if ((ret = sloc_find(DBENG_SERVICE_NAME, dbeng_hostname, &dbeng_port,
           (char *)NULL)) != SL_OK)
          {
          sl_code_string(ret, mes);
          logman("%s:bad rc[%s] from sloc_find", mname, mes);
          return(DBENG_NO_SERVER);
          }

       if (!db_cs_client_connect())
          {
          logman("%s:bad rc[FALSE] from db_cs_client_connect", mname);
          return(DBENG_VC_ERROR);
          }

       logman("%s:normal exit,rc[0]", mname);
       return(DBENG_FAILOVER);
    }
    #endif

/* [<][>][^][v][top][bottom][index][help] */