## root/dbeng/dbeng.c

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

## DEFINITIONS

This source file includes following definitions.

1. dbeng_pack_table
2. dbeng_open_systable
3. dbeng_open_table
4. dbeng_ll_open_table
5. dbeng_close_table
6. dbeng_goto_top
7. dbeng_rewrite_recd
8. dbeng_delete_recd
9. dbeng_write_new_recd
10. dbeng_write_string_recd
11. dbeng_write_recd
12. dbeng_new
13. dbeng_get_rec
14. dbeng_get_field
15. dbeng_get_subfield
16. dbeng_get_subsubfield
17. dbeng_put_field
18. dbeng_put_subfield
19. dbeng_put_subsubfield
20. dbeng_find
21. dbeng_find_field
22. dbeng_find_part
23. dbeng_find_field_part
24. dbeng_nfields
25. dbeng_nsubfields
26. dbeng_nsubsubfields
27. dbeng_count_rec
28. dbeng_rec_count
29. dbeng_goto_record
30. dbeng_get_recd
31. dbeng_rec_resize
32. dbeng_rec_size
33. dbeng_field_size
34. dbeng_subfield_size
35. dbeng_subsubfield_size
36. dbeng_rec_num
37. dbeng_pos
38. dbeng_set_pos
39. dbeng_change_rec_flag
40. dbeng_set_change_rec_flag
41. dbeng_delete_flag
42. dbeng_set_delete_flag
43. dbeng_enf_change_rec_flag
44. dbeng_set_enf_change_rec_flag
45. dbeng_autopack
46. dbeng_set_autopack
47. dbeng_is_table_locked
48. dbeng_set_is_table_locked
49. dbeng_lock_table
50. dbeng_unlock_table
51. dbeng_tmp_systable
52. dbeng_new_systable
53. dbeng_new_table
54. dbeng_ll_new_table
55. dbeng_delete_table
56. dbeng_open_table_list
57. dbeng_exist
58. dbeng_clear_table
59. dbeng_ll_open
60. dbeng_ll_close
61. dbeng_is_systable
62. dbeng_copy_table
63. dbeng_record_header
64. dbeng_put_which_field
65. dbeng_put_which_subfield
66. dbeng_put_which_subsubfield
67. dbeng_ll_copy_table
68. dbeng_delete_field
69. dbeng_delete_subfield
70. dbeng_delete_subsubfield
71. dbeng_set_record_count
72. dbeng_terminate
73. dbeng_is_active_rec
74. dbeng_initialize
75. dbeng_get_tid
76. dbeng_table_in_use
77. dbeng_compare_field
78. dbeng_atid_lookup
79. dbeng_ferror

    /* DBENG (Bbuuzzb) - A database engine. Library functions.
       Rick Smereka, Copyright (C) 1995-2006.

       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 DOS version Feb/95, Rick Smereka

       Added call to 'setvbuf' to set I/O buffer size. Dec/96, Rick Smereka

       Updated code to have same functionality as Pascal (Delphi) code.
       Also ported to QNX V4.23a. Jan/97, Rick Smereka

       Complete re-write removing all known errors. Changed to remove 'alias'
       and add 'tid'. Oct/97, Rick Smereka

       Added logic to support 'is_table_locked' flag. If this flag is
       on, no read, write or file pointer movement will be allowed.
       Nov/98, Rick Smereka

       Ported to 32bit Windows under CodeWarrior V4.
       Dec/98, Rick Smereka

       Ported to HP-UX under GNU C V2.8.1.
       Jan/99, Rick Smereka

       Added function 'dbeng_ferror', changed function
       'dbeng_get_recd' to use this new function. Fixed bug in
       function 'dbeng_count_rec' that did not properly trap
       an error from 'dbeng_get_recd'. Feb/99, Rick Smereka

       Added functions 'dbeng_get_tmp_path' and 'dbeng_get_error_log'.
       Moved these functions to 'dbengcfg.c' and renamed them. Modified
       'dbeng_pack' to get the temporary path from function
       'dbeng_config_get_tmp_path'. Modified function 'dbeng_ferror'
       to get the log name (in the case of stand-alone compile)
       from the function 'dbeng_config_get_log'.
       Feb/99, Rick Smereka

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

       Added include of 'flsocket.h'. Mar/2000, Rick Smereka

       Cleaned up debug messages. Apr/2000, Rick Smereka

       Added function 'dbeng_open_table_list'. Jun/2001,
       Rick Smereka

       Added session table support. Oct/2001, Rick Smereka

       Added system catalog support and implemented support for
       new 'dbeng_table' member 'current_position'. Modified
       function 'dbeng_pack_table' to goto the top of the table
       after packing. Added new functions 'dbeng_ll_open' and
       'dbeng_ll_close'. Changed function 'dbeng_set_pos' to
       goto the top of the table before positioning.
       Nov/2001, Rick Smereka

       Added new functions 'dbeng_nsubfields', 'dbeng_subfield_size',
       'dbeng_get_subfield', 'dbeng_put_subfield', 'dbeng_nsubsubfields',
       'dbeng_subsubfield_size', 'dbeng_get_subsubfield' and
       'dbeng_put_subsubfield'. Dec/2001, Rick Smereka

       Added function 'dbeng_record_header'. Changed 'dbeng_get_recd' to
       read and validate the record header through the new function.
       Changed 'dbeng_delete_recd' to make sure that a valid record
       is present before updating it. If the record position has been
       changed (possible other user/tid packed the table changing some of
       the record positions) 'dbeng_delete_recd' will return
       'DBENG_RECORD_POSITION_CHANGED' along with forcing the file position
       back to the top of the table. This situation should never happen
       as 'dbeng_pack_table' and 'dbeng_delete_recd' have now been modified
       to reconcile any other tid pointing to the record being modified.
       Changed 'dbeng_write_string_recd' to maintain the start of record
       file position and current file position instead of going to the
       top of the table after the write of a record. Jan/2002, Rick Smereka

       Added functions 'dbeng_open_systable', 'dbeng_ll_open_table',
       'dbeng_new_systable', 'dbeng_ll_new_table' and
       'dbeng_is_systable'. Placed calls to 'dbengrep_send' from the
       functions 'dbeng_write_new_recd', 'dbeng_write_string_recd',
       'dbeng_delete_recd' and 'dbeng_pack_table'. Feb/2002, Rick Smereka

       Changed function 'dbeng_ll_new_table' to create the new table only
       in single user mode as the catalog module 'dbeng_catalog_new_table'
       will create the table in multiuser mode. Mar/2002, Rick Smereka

       Changed function 'dbeng_record_header' to be private. Apr/2002,
       Rick Smereka

       Changed function 'dbeng_get_tid' to obtain the next tid based
       on the number of current tables in use. Added private functions
       'dbeng_put_which_field', 'dbeng_put_which_subfield' and
       'dbeng_put_which_subsubfield'. Modified functions
       'dbeng_put_field', 'dbeng_put_subfield' and
       'dbeng_put_subsubfield' to call their respective
       'dbeng_put_which' function. Added function 'dbeng_exist'.
       May/2002, Rick Smereka

       Added functions 'dbeng_ll_copy_table' and 'dbeng_clear_table'
       and 'dbeng_copy_table'. Jun/2002, Rick Smereka

       Changed function 'dbeng_ferror' to obtain the application name
       (in the case of multi-user [database server]) by calling
       'appinit_get_name'. Jul/2002, Rick Smereka

       Added a parameter to the function 'dbeng_open_table_list'.
       Aug/2002, Rick Smereka

       Added function 'dbeng_tmp_systable' and modified 'dbeng_pack_table'
       to create a new system table using 'dbeng_tmp_systable'. Sep/2002,
       Rick Smereka

       Added include of 'dbengsrt.h'. Added initialization code for new
       'dbeng_table' members 'sort_max_open_bin' and 'sort_max_mem' in
       function 'dbeng_ll_open_table'. Ported to Debian Linux.
       Nov/2002, Rick Smereka

       Fixed bug in 'dbeng_tmp_systable' that caused a memory leak.
       Added private function 'dbeng_compare_field' and modified
       functions 'dbeng_find_field' and 'dbeng_find' to use this
       new function.  Added functions 'dbeng_delete_field',
       'dbeng_delete_subfield' and 'dbeng_delete_subsubfield'.
       Mar/2003, Rick Smereka

       Changed function 'dbeng_get_recd' to goto the top of the
       table when the attempt to read a record header results in
       any error code (anything other than 'DBENG_OK'). Jun/2003,
       Rick Smereka

       Changed counting logic in 'dbeng_count_rec' and added an additional
       parameter. Changed calls to 'dbeng_count_rec'. Changed all references
       to the 'dbeng_table' member 'record_count' to either the active
       count or the deleted record count. Changed function 'dbeng_rec_count'
       by adding an additional parameter. Dec/2003, Rick Smereka

       Added save of 'process_deleted' flag, force of flag to high
       before count in 'dbeng_count_rec' and restore of flag after
       count. Jan/2004, Rick Smereka

       Added function 'dbeng_set_record_count'. Feb/2004,
       Rick Smereka

       Modified function 'dbeng_ll_open_table' to acquire table
       catalog details from function 'dbeng_catalog_table_details'.
       Added functions 'dbeng_autopack' and 'dbeng_set_autopack'.
       Added logic to perform automatic pack in function
       'dbeng_delete_recd' if the autopack threshold is exceeded.
       Changed all logging calls from 'sys_log' to 'logman'.
       Modified function 'dbeng_ferror' to use 'logman' functions.
       Any application linking with this API should register its
       name with the function 'appinit_register_name'. The function
       'dbeng_ferror' obtains the application name by calling the
       function 'appinit_get_name'. Mar/2004, Rick Smereka

       Fixed bug in 'dbeng_ll_new_table' that caused a 'segment fault'
       crash because the output table name was not big enough. Changed
       'dbeng_write_recd' to detect when writing a record already marked
       for deletion. In this case, a new record will be created.
       Jan/2005, Rick Smereka

       Plugged memory leak in 'dbeng_ll_open_table' when the open failed
       due to no catalog entry. Feb/2005, Rick Smereka

       Modified 'dbeng_ll_new_table' to only open the new table when the
       passed tid flag is a positive number. Mar/2006, Rick Smereka */

    #include "stdhead.h"
    #include "dbmess.h"
    #include "dbeng.h"
    #include "dbiocode.h"
    #include "dbengcfg.h"
    #include "dbengsrt.h"
    #include "dbengcat.h"
    #ifdef MULTIUSER
    #include "dbengses.h"
    #ifdef IPC_TCP
    #include "dbengrep.h"
    #endif
    #endif

    /* global data */

    /* pointer to head of open table link list */

    struct dbeng_table *dbeng_head;

    /* number of tables in use */

    int dbeng_table_count;

    /* private functions */

    static int dbeng_ll_open_table(char *, int *, int);
    static int dbeng_ll_new_table(char *, int *, int);
    static int dbeng_record_header(struct dbeng_table *, int *, char *);
    static int dbeng_put_which_field(struct dbeng_table *, int, int *);
    static int dbeng_put_which_subfield(struct dbeng_table *, int, int , int *,
                                        int *);
    static int dbeng_put_which_subsubfield(struct dbeng_table *, int, int, int,
                                           int *, int *, int *);
    static int dbeng_table_in_use(char *);
    static int dbeng_ll_copy_table(struct dbeng_table *, struct dbeng_table *);
    static int dbeng_compare_field(char *, char *, int, int *);

    int dbeng_pack_table(struct dbeng_table *ot)
    {
       /* Pack a table by copying only active records. Function
          returns 'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_table *dest_table;
       char *dest_table_name;
       char mname[] = "dbeng_pack_table";
       int ret, tid, delete_flag, len;

    #ifdef MULTIUSER
    #ifdef IPC_TCP
       int rep_flag;
    #endif
    #endif

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter,tid=%d", mname, ot->tid);

       /* check for locked table */

       if (ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       /* check for changed record */

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       if ((ret = dbeng_tmp_systable(&tid)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_tmp_systable", ret);
          return(ret);
          }

       /* fetch link list pointer from tid */

       if ((dest_table = dbeng_atid_lookup(tid)) == NULL)
          {
          logman("%s:cannot locate tmp table[%d]", mname, tid);
          return(DBENG_CANNOT_CREATE_TABLE);
          }

       logman("%s:output tmp table is %s", mname, dest_table->name);
       len = strlen(dest_table->name);

       if ((dest_table_name = (char *)malloc(len + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[dest_table_name]",
                            DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       strcpy(dest_table_name, dest_table->name);

       /* force source 'process_deleted' flag down (save prev value) */

       delete_flag = ot->process_deleted;
       ot->process_deleted = FALSE;

       /* copy source to destination */

       if ((ret = dbeng_ll_copy_table(ot, dest_table)) != DBENG_OK)
          {
          (void)dbeng_close_table(dest_table);
          unlink(dest_table_name);
          ot->process_deleted = delete_flag;
          dbeng_io_code_log(mname, "bad ret from dbeng_ll_copy_table", ret);
          free(dest_table_name);
          return(ret);
          }

       // fix-up record counts

       ot->active_record_count = dest_table->active_record_count;
       ot->deleted_record_count = 0L;

       /* close output (temporary) table */

       if ((ret = dbeng_close_table(dest_table)) != DBENG_OK)
          {
          unlink(dest_table_name);
          ot->process_deleted = delete_flag;
          dbeng_io_code_log(mname, "error closing tmp table", ret);
          free(dest_table_name);
          return(ret);
          }

       /* close source table (but retain table structure) */

       if ((ret = dbeng_ll_close(ot)) != DBENG_OK)
          {
          unlink(dest_table_name);
          ot->process_deleted = delete_flag;
          dbeng_io_code_log(mname, "error closing source table", ret);
          free(dest_table_name);
          return(ret);
          }

       /* delete source table */

       unlink(ot->name);

       /* rename temporary table to source */

       if (!qrename(dest_table_name, ot->name))
          {
          ot->process_deleted = delete_flag;
          logman("%s:error renaming table", mname);
          free(dest_table_name);
          return(DBENG_RENAME_ERROR);
          }

       free(dest_table_name);

       if ((ret = dbeng_ll_open(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error re-opening file", DBENG_UNABLE_TO_OPEN);
          return(DBENG_UNABLE_TO_OPEN);
          }

       /* since we are now at the bottom of the table, goto top */

       (void)dbeng_goto_top(ot);

       // fix-up record count

       if ((ret = dbeng_set_record_count(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error setting record count",
                            DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

       /* replicate command if requested */

    #ifdef MULTIUSER
    #ifdef IPC_TCP
       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && ot->rep_list != DBENG_ROT_NULL)
          if ((ret = dbengrep_send(ot, DBENG_SEND_REPLICATE_PACK, (char *)NULL))
              != DBENG_OK)
             logman("%s:replication failed,rc=%d", mname, ret);
    #endif
    #endif

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_open_systable(char *fname, int *tid)
    {
       /* Open a system table. At the moment, a system table is one of:

             - temporary table
             - session table
             - catalog

          This function behaves just like 'dbeng_open_table' except
          that the physical file is always left open. Function returns
          a 'dbeng' code. */

       char mname[] = "dbeng_open_systable";
       int ret;

       if (fname == (char *)NULL || !strlen(fname))
          {
          dbeng_io_code_log(mname, "fname parameter error",
                            DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

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

       *tid = 0;

       /* open the table using 'dbeng_ll_open_table' */

       if ((ret = dbeng_ll_open_table(fname, tid, TRUE)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_ll_open_table", ret);
          return(ret);
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_open_table(char *fname, int *tid)
    {
       /* Open a table. Function returns the table 'tid'
          upon success. Function returns a 'dbeng' code. */

       char mname[] = "dbeng_open_table";
       int ret;

       if (fname == (char *)NULL || !strlen(fname))
          {
          dbeng_io_code_log(mname, "fname parameter error",
                            DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

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

       *tid = 0;

       /* open the table using 'dbeng_ll_open_table' */

       if ((ret = dbeng_ll_open_table(fname, tid, FALSE)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_ll_open_table", ret);
          return(ret);
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    static int dbeng_ll_open_table(char *fname, int *tid, int systable_flag)
    {
       /* Open a table. Table ID is returned in 'tid' upon success.
          If the system catalog flag is on, only logical table names
          listed in the system catalog will succeed unless the name
          is prefixed with the 'DBENG_CATALOG_FNAME_IND' character.
          Function returns 'DBENG_OK' upon success, error code otherwise.
          Note that if the code returned is 'DBENG_FILE_DATA_ERROR',
          the 'tid' is still loaded and the file is left open. */

       struct dbeng_table *ot;
       struct dbeng_table *rov;
       struct dbengcat_details *det;
       char mname[] = "dbeng_ll_open_table";
       int ot_size, session_flag, rep_flag, ret;
       int name_type, done = FALSE;

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

       if (fname == (char *)NULL || !strlen(fname))
          {
          dbeng_io_code_log(mname, "fname parameter error",
                            DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

       if (systable_flag != TRUE && systable_flag != FALSE)
          {
          dbeng_io_code_log(mname, "bad flag[systable_flag]",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       *tid = 0;
       ot_size = sizeof(struct dbeng_table);

       if ((ot = malloc(ot_size)) == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[ot]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       ot->handle = (FILE *)NULL;

       // prepare to load table catalog details

       if ((ret = dbeng_catalog_init_table_details(fname, systable_flag, &det)) !=
          DBENG_OK)
          {
          dbeng_io_code_log(mname,
                            "bad rc from dbeng_catalog_init_table_details", ret);
          free(ot);
          return(ret);
          }

       // load table catalog details

       if ((ret = dbeng_catalog_table_details(det)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_catalog_table_details", ret);
          (void)dbeng_catalog_term_table_details(&det);
          free(ot);
          return(ret);
          }

    #ifdef MULTIUSER
       (void)dbeng_config_get_session_flag(&session_flag);
       ot->rep_list = DBENG_ROT_NULL;
    #endif

       /* file name is stored in original case and is compared
          case sensitive */

       if ((ot->name = malloc(strlen(det->physical_name) + 1)) == (char *)NULL)
          {
          free(ot);
          (void)dbeng_catalog_term_table_details(&det);
          dbeng_io_code_log(mname, "alloc fail[ot->name]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       strcpy(ot->name, det->physical_name);

       if ((ret = dbeng_ll_open(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_ll_open", ret);
          free(ot->name);
          free(ot);
          (void)dbeng_catalog_term_table_details(&det);
          return(ret);
          }

       ot->next = DBENG_OT_NULL;             // end of link list
       ot->rec = (char *)NULL;               // no current record
       ot->change_rec_flag = FALSE;          // no change in record
       ot->enforce_change_rec_flag = FALSE;  // do not enforce 'change_rec_flag'
       ot->record_number = 0L;               // no current record
       ot->orig_position = -1L;              // no start rec file pointer position
       ot->current_position = 0L;            // current pos at top of table
       ot->process_deleted = FALSE;          // do not process deleted records
       ot->is_table_locked = FALSE;          // table is not read/write locked
       ot->is_systable = systable_flag;      // set system table flag

       /* if its a system table, use the default autopack threshold
          otherwise use the threshold given in the catalog (if present) */

       ot->autopack = systable_flag ? DBENG_SYSTABLE_MAX_DELETED : det->apack;
       ot->sort_max_open_bin = DBENGSRT_MAX_OPEN_BIN; // max open bin during sort
       ot->sort_max_mem = DBENGSRT_MAX_SORT_ALLOC;    // max mem alloc during sort

       /* set head of list if first file open */

       if (dbeng_head == DBENG_OT_NULL)
          dbeng_head = ot;
       else
          {
          /* insert new table at end of link list */

          rov = dbeng_head;

          while(!done)
             {
             if (rov->next == DBENG_OT_NULL)
                {
                rov->next = ot;
                done = TRUE;
                }
             else
                rov = rov->next;
             }
          }

       ot->tid = dbeng_get_tid();
       *tid = ot->tid;

       if ((ret = dbeng_count_rec(ot, &ot->active_record_count,
            &ot->deleted_record_count)) != DBENG_OK)
          {
          (void)dbeng_catalog_term_table_details(&det);
          return(ret);
          }

       (void)dbeng_goto_top(ot);
       dbeng_table_count++;

    #ifdef MULTIUSER
       /* if session table is active, physically close the file
          unless it is a system table */

       if (session_flag && !systable_flag)
          if ((ret = dbeng_ll_close(ot)) != DBENG_OK)
             dbeng_io_code_log(mname, "error closing table", ret);

    #ifdef IPC_TCP
       /* if replication is active and a catalog table name was used
          to open the table, load replication details from
          catalog into replication link list (do not allow
          replication of any system table) */

       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && fname[0] != DBENG_CATALOG_FNAME_IND && !ot->is_systable)
          if ((ret = dbengrep_rll_load(ot, det->rep_list)) != DBENG_OK)
             {
             if (ret != DBENG_NO_REPLICATION_ENTRY && ret !=
                 DBENG_NO_CATALOG_ENTRY)
                {
                logman("%s:exit:bad rc[%d] from dbengrep_rll_load[%d]",
                              mname, ret, rov->tid);
                (void)dbeng_catalog_term_table_details(&det);
                return(ret);
                }
             }
    #endif
    #endif

       if ((ret = dbeng_catalog_term_table_details(&det)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from "
                            "dbeng_catalog_term_table_details", ret);
          return(ret);
          }

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

    int dbeng_close_table(struct dbeng_table *table)
    {
       /* Close a table. Function returns 'DBENG_OK' upon success,
          error code otherwise. */

       struct dbeng_table *rov;
       struct dbeng_table *prev;
       char mname[] = "dbeng_close_table";
       int ret, session_flag, rep_flag, done = FALSE;

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter:tid=%d", mname, table->tid);

       if (table->enforce_change_rec_flag && table->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

    #ifdef MULTIUSER
       (void)dbeng_config_get_session_flag(&session_flag);

       if (session_flag && !table->is_systable)
          if ((ret = dbeng_session_delete(table->tid)) != DBENG_OK)
             dbeng_io_code_log(mname, "bad rc from dbeng_session_delete", ret);

    #ifdef IPC_TCP
       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && !table->is_systable)
          if ((ret = dbengrep_rll_delete_all(table)) != DBENG_OK)
             dbeng_io_code_log(mname, "bad rc from dbengrep_rll_delete", ret);
    #endif
    #endif

       rov = table;
       rov->change_rec_flag = -1;       /* mark this table to close */
       rov = dbeng_head;                /* goto top of link list */
       prev = DBENG_OT_NULL;
       done = FALSE;

       /* loop to locate the previous table in the link list, close the
          appropiate table and de-allocate all memory used by the
          table structure */

       while(!done)
          {
          /* this should never happen but just in case */

          if (rov == DBENG_OT_NULL)
             {
             logman("%s:unexpected rov NULL", mname);
             break;
             }

          if (rov->change_rec_flag == -1)
             {
             if (prev == DBENG_OT_NULL)
                if (rov->next == DBENG_OT_NULL)
                   dbeng_head = DBENG_OT_NULL;
                else
                   dbeng_head = rov->next;
             else
                prev->next = rov->next;

             if ((ret = dbeng_ll_close(rov)) != DBENG_OK)
                dbeng_io_code_log(mname, "warning:problem closing table", ret);

             free(rov->name);

             if (dbeng_is_active_rec(rov) == DBENG_OK)
                free(rov->rec);

             free(rov);
             done = TRUE;
             }
          else
             {
             prev = rov;
             rov = rov->next;
             }
          }

       dbeng_table_count--;

       if (!done)
          {
          dbeng_io_code_log(mname, "normal exit", DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

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

    int dbeng_goto_top(struct dbeng_table *ot)
    {
       /* Reposition table at the top, with no current record. Function
          returns 'DBENG_OK' upon success, an error code otherwise. */

       char mname[50];

       strcpy(mname, "dbeng_goto_top");

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       /* logman("%s:enter:name=%s", mname, ot->name); */

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       rewind(ot->handle);

       if (dbeng_is_active_rec(ot) == DBENG_OK)
          free(ot->rec);

       ot->rec = (char *)NULL;
       ot->record_number = 0L;
       ot->orig_position = -1L;
       ot->current_position = 0L;
       ot->change_rec_flag = FALSE;
       /* logman("%s:normal exit", mname); */
       return(DBENG_OK);
    }

    int dbeng_rewrite_recd(struct dbeng_table *ot)
    {
       /* Rewrite an existing record. Old record will be marked for
          deletion and a new record will be written. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_table tmp;
       char mname[50];
       int rec_size;
       int ret, enf_change_rec_flag;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       /* make sure there is an already existing record */

       if (ot->orig_position == -1L || ot->record_number == 0L)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_EXISTING_RECORD);
          return(DBENG_NO_EXISTING_RECORD);
          }

       // check for locked table

       if (ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "table is locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       /* save current record values as they are destroyed by
          the delete record function */

       rec_size = strlen(ot->rec);

       if ((tmp.rec = malloc(rec_size + 1)) == NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[tmp.rec]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       strcpy(tmp.rec, ot->rec);
       tmp.record_number = ot->record_number;
       tmp.orig_position = ot->orig_position;
       tmp.current_position = ot->current_position;

       /* force 'enforce_change_rec_flag' down as the delete
          function checks this */

       enf_change_rec_flag = ot->enforce_change_rec_flag;
       ot->enforce_change_rec_flag = FALSE;
       ret = dbeng_delete_recd(ot);

       if (ret != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret[delete_recd]", ret);
          free(tmp.rec);
          ot->enforce_change_rec_flag = enf_change_rec_flag;
          return(ret);
          }

       /* reload record values */

       ret = dbeng_rec_resize(ot, rec_size);

       if (ret != DBENG_OK)
          {
          dbeng_io_code_log(mname, "resize fail", ret);
          free(tmp.rec);
          ot->enforce_change_rec_flag = enf_change_rec_flag;
          return(ret);
          }

       strcpy(ot->rec, tmp.rec);
       ot->record_number = tmp.record_number;
       ot->orig_position = tmp.orig_position;
       ot->current_position = tmp.current_position;
       free(tmp.rec);
       ret = dbeng_write_new_recd(ot);
       ot->enforce_change_rec_flag = enf_change_rec_flag;
       dbeng_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

    int dbeng_delete_recd(struct dbeng_table *ot)
    {
       /* Mark the current record for deletion. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_table *rov;
       char mname[] = "dbeng_delete_recd";
       char rm[2], status;
       int ret, len, rep_flag;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ot->record_number == 0L || ot->orig_position == -1L)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_EXISTING_RECORD);
          return(DBENG_NO_EXISTING_RECORD);
          }

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       // check for locked table

       if (ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "table is locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       /* goto the start of the current record */

       fseek(ot->handle, ot->orig_position, 0);

       /* verify that there is a valid record here */

       if ((ret = dbeng_record_header(ot, &len, &status)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "record header invalid,going to top", ret);

          /* since there no longer is a current reocrd here, goto top */

          (void)dbeng_goto_top(ot);
          return(DBENG_RECORD_POSITION_CHANGED);
          }

       /* go back to the start of the record */

       fseek(ot->handle, ot->orig_position, 0);

       /* rewrite the first byte of the record */

       rm[0] = 'D';
       rm[1] = EOS;

       if (fwrite(rm, 1, 1, ot->handle) != 1)
          {
          dbeng_io_code_log(mname, "write error on delete mark", DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

    #ifdef MULTIUSER
       /* if another tid is also pointing to the same record,
          tell that tid that this record is now deleted by
          setting the record number to zero which will cause
          'dbeng_write_recd' to write a new record */

       rov = dbeng_head;

       while(rov != DBENG_OT_NULL)
          {
          if (ot->tid != rov->tid && !strcmp(ot->name, rov->name))
             if (ot->orig_position == rov->orig_position)
                {
                logman("%s:another tid using record,setting rn to zero:"
                              "tid=%d,ltid=%d,pos=%ld", mname, ot->tid, rov->tid,
                              rov->orig_position);
                rov->record_number = 0;
                }

          rov = rov->next;
          }

    #ifdef IPC_TCP
       /* replicate if requested */

       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && ot->rep_list != DBENG_ROT_NULL)
          if ((ret = dbengrep_send(ot, DBENG_SEND_REPLICATE_DELETE, (char *)NULL))
              != DBENG_OK)
             logman("%s:replication failed,rc=%d", mname, ret);
    #endif
    #endif

       ot->active_record_count--;
       ot->deleted_record_count++;
       ot->change_rec_flag = FALSE;

       // pack if autopack threshold is exceeded

       if (ot->autopack && ot->deleted_record_count >= ot->autopack)
          {
          logman("%s:autopack threshold exceeded,autopacking", mname);

          if ((ret = dbeng_pack_table(ot)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "error autopacking", ret);
             return(ret);
             }
          }
       else
          {
          // fix-up record count

          if ((ret = dbeng_set_record_count(ot)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "error setting record count",
                               DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }
          }

       /* since file position is now unknown, goto top of file */

       (void)dbeng_goto_top(ot);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_write_new_recd(struct dbeng_table *ot)
    {
       /* Write new record. Function returns 'DBENG_OK' upon success,
          error code otherwise. No regard is given to an already existing
          record. If you need to rewrite, use 'dbeng_rewrite_recd'
          instead. */

       char header[DBENG_REC_HEAD_SIZE + 1];
       char rm[2];
       char mname[50];
       int ret, len_with_head, len_no_head, rep_flag;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (dbeng_is_active_rec(ot) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       // check for locked table

       if (ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "table is locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       len_no_head = strlen(ot->rec);
       fseek(ot->handle, 0L, 2);     /* goto end of table */
       ot->orig_position = ftell(ot->handle);
       len_with_head = len_no_head + 1;
       sprintf(header, "A%010d%c", len_with_head, DBENG_FM);
       sprintf(rm, "%c", DBENG_RM);

       if (fwrite(header, 1, DBENG_REC_HEAD_SIZE, ot->handle) !=
                  DBENG_REC_HEAD_SIZE)
          {
          dbeng_io_code_log(mname, "write fail on rec header",
                            DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       if (fwrite(ot->rec, 1, len_no_head, ot->handle) != len_no_head)
          {
          dbeng_io_code_log(mname, "write fail on rec contents",
                            DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       if (fwrite(rm, 1, 1, ot->handle) != 1)
          {
          dbeng_io_code_log(mname, "write fail on rec mark", DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       ++ot->record_number;
       ++ot->active_record_count;
       ot->change_rec_flag = FALSE;

       // fix-up record count

       if ((ret = dbeng_set_record_count(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error setting record count",
                            DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

    #ifdef MULTIUSER
    #ifdef IPC_TCP
       /* replicate this change if requested */

       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && ot->rep_list != DBENG_ROT_NULL)
          if ((ret = dbengrep_send(ot, DBENG_SEND_REPLICATE_WRITE, ot->rec))
              != DBENG_OK)
             logman("%s:replication failed,rc=%d", mname, ret);
    #endif
    #endif

       /* since file position is at end of file, goto top */

       (void)dbeng_goto_top(ot);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_write_string_recd(struct dbeng_table *ot, char *rec)
    {
       /* Write new record from the contents of 'rec'. Unlike
          'dbeng_write_new_recd', this function does not
          force the record pointer to the top of the table.
          Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       char header[DBENG_REC_HEAD_SIZE + 1];
       char rm[2];
       char mname[] = "dbeng_write_string_recd";
       int ret, len_with_head, len_no_head, rep_flag;

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (rec == NULL || !strlen(rec))
          {
          dbeng_io_code_log(mname, "null[rec]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       /* logman("%s:enter", mname); */
       len_no_head = strlen(rec);
       fseek(ot->handle, 0L, 2);     /* goto end of table */
       ot->orig_position = ftell(ot->handle);
       len_with_head = len_no_head + 1;
       sprintf(header, "A%010d%c", len_with_head, DBENG_FM);
       sprintf(rm, "%c", DBENG_RM);

       if (fwrite(header, 1, DBENG_REC_HEAD_SIZE, ot->handle) !=
                  DBENG_REC_HEAD_SIZE)
          {
          dbeng_io_code_log(mname, "write fail on rec header",
                            DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       if (fwrite(rec, 1, len_no_head, ot->handle) != len_no_head)
          {
          dbeng_io_code_log(mname, "write fail on rec contents",
                            DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       if (fwrite(rm, 1, 1, ot->handle) != 1)
          {
          dbeng_io_code_log(mname, "write fail on rec mark", DBENG_WRITE_FAIL);
          return(DBENG_WRITE_FAIL);
          }

       ++ot->active_record_count;
       ot->change_rec_flag = FALSE;
       ot->current_position = ftell(ot->handle);

       // fix-up record count

       if ((ret = dbeng_set_record_count(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error setting record count",
                            DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

    #ifdef MULTIUSER
    #ifdef IPC_TCP
       /* replicate this change if requested */

       (void)dbeng_config_get_replicate_flag(&rep_flag);

       if (rep_flag && ot->rep_list != DBENG_ROT_NULL)
          if ((ret = dbengrep_send(ot, DBENG_SEND_REPLICATE_WRITE, rec))
              != DBENG_OK)
             logman("%s:replication failed,rc=%d", mname, ret);
    #endif
    #endif

       /* logman("%s:normal exit", mname); */
       return(DBENG_OK);
    }

    int dbeng_write_recd(struct dbeng_table *ot)
    {
       /* Write a new reocrd or rewrite an existing record depending on
          the table record counter and the current record status (if any).
          The record contents must already
          be loaded and if a record exists, the file pointer must be
          at the record. Function returns 'DBENG_OK' upon success,
          an error code otherwise. */

       char mname[] = "dbeng_write_recd";
       int ret;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (dbeng_is_active_rec(ot) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no current rec", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       // check for locked table

       if (ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "table is locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       // if there is not a current record in memory write a new one

       if (!ot->record_number)
          {
          logman("%s:writing a new record", mname);
          ret = dbeng_write_new_recd(ot);
          }
       else
          {
          // if the current record status is 'deleted', write a new record

          if (!ot->record_status)
             {
             logman("%s:record already marked for deletion,writing a new rec",
                    mname);
             ret = dbeng_write_new_recd(ot);
             }
          else
             {
             // re-write existing record

             logman("%s:rewriting existing record", mname);
             ret = dbeng_rewrite_recd(ot);
             }
          }

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

    int dbeng_new(struct dbeng_table *ot)
    {
       /* Initialize for a new record. Note that if the current
          record has changed and we are enforcing 'change_rec_flag,
          the function will return that code and the initialization will
          not take place. */

       char mname[50];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       if (dbeng_is_active_rec(ot) == DBENG_OK)
          free(ot->rec);

       ot->rec = (char *)NULL;
       ot->record_number = 0L;
       ot->orig_position = -1L;
       ot->current_position = 0L;
       ot->change_rec_flag = FALSE;
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_get_rec(struct dbeng_table *table, char *rec_out)
    {
       /* Copy and return the current record into 'rec_out' which
          must be already allocated large enough to hold the record.
          Function returns 'DBENG_OK' upon success, an error code
          otherwise. */

       char mname[50];
       int ret;

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

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       rec_out[0] = EOS;

       if ((ret = dbeng_is_active_rec(table)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       strcpy(rec_out, table->rec);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_get_field(struct dbeng_table *table, char *field_out,
                        int field_num)
    {
       /* Obtain the 'field_num' field in the record specified by
          'table'. Function returns 'DBENG_OK' on success,
          error code otherwise. Note that the caller must allocate
          enough memory in 'field_out' to copy the field contents. */

       char mname[50];
       int nwords;

       strcpy(mname, "dbeng_get_field");

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       field_out[0] = EOS;

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

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

       /* if no table or no current record, error */

       if (table == DBENG_OT_NULL || dbeng_is_active_rec(table) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no table or current record", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       nwords = ll_words(table->rec, DBENG_FM);

       /* if field is out of range, error */

       if (field_num > nwords)
          {
          logman("%s:field %d is out of range", mname, field_num);
          return(DBENG_NO_SUCH_FIELD);
          }

       /* get field */

       (void)ll_word(table->rec, field_out, field_num, DBENG_FM);
       /* logman("%s:normal exit", mname); */
       return(DBENG_OK);
    }

    int dbeng_get_subfield(struct dbeng_table *table, char *subfield_out,
                           int field_num, int subfield_num)
    {
       /* Obtain the 'subfield_num' sub-field in the field specified by
          'field_num'. Function returns 'DBENG_OK' on success,
          error code otherwise. Note that the caller must allocate
          enough memory in 'subfield_out' to copy the field contents. */

       char mname[] = "dbeng_get_subfield", *thefield;
       int ret, nwords, fsize, nsubfields;

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

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       subfield_out[0] = EOS;

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

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

       if ((ret = dbeng_is_active_rec(table)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       if ((ret = dbeng_nsubfields(table, field_num, &nsubfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_nsubfields", ret);
          return(ret);
          }

       if (subfield_num > nsubfields)
          {
          dbeng_io_code_log(mname, "out of range[subfield_num]",
                            DBENG_NO_SUCH_SUBFIELD);
          return(DBENG_NO_SUCH_SUBFIELD);
          }

       /* attempt to get the size of the requested field */

       if ((ret = dbeng_field_size(table, field_num, &fsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
          return(ret);
          }

       if ((thefield = (char *)malloc(fsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thefield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_field(table, thefield, field_num)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_field", ret);
          free(thefield);
          return(ret);
          }

       (void)ll_word(thefield, subfield_out, subfield_num, DBENG_SFM);
       free(thefield);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_get_subsubfield(struct dbeng_table *table, char *subsubfield_out,
                              int field_num, int subfield_num,
                              int subsubfield_num)
    {
       /* Obtain the 'subsubfield_num' sub-sub-field in the sub-field specified by
          'subfield_num'. Function returns 'DBENG_OK' on success,
          error code otherwise. Note that the caller must allocate
          enough memory in 'subsubfield_out' to copy the field contents. */

       char mname[] = "dbeng_get_subsubfield", *thesubfield;
       int ret, nwords, sfsize, nsubsubfields;

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

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       subsubfield_out[0] = EOS;

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

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

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

       if ((ret = dbeng_is_active_rec(table)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       if ((ret = dbeng_nsubsubfields(table, field_num, subfield_num,
            &nsubsubfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_nsubsubfields", ret);
          return(ret);
          }

       if (subsubfield_num > nsubsubfields)
          {
          dbeng_io_code_log(mname, "out of range[subsubfield_num]",
                            DBENG_NO_SUCH_SUBSUBFIELD);
          return(DBENG_NO_SUCH_SUBSUBFIELD);
          }

       /* attempt to get the size of the requested sub-field */

       if ((ret = dbeng_subfield_size(table, field_num, subfield_num,
            &sfsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
          return(ret);
          }

       if ((thesubfield = (char *)malloc(sfsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thesubfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_subfield(table, thesubfield, field_num,
            subfield_num)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_subfield", ret);
          free(thesubfield);
          return(ret);
          }

       (void)ll_word(thesubfield, subsubfield_out, subsubfield_num, DBENG_SSFM);
       free(thesubfield);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_put_field(struct dbeng_table *table, char *new_data,
                        int field_num)
    {
       /* Store new data in a field and re-size record as
          appropriate. Function returns 'DBENG_OK' upon success,
          error code otherwise. */

       char *old_rec, *new_rec;
       char mname[] = "dbeng_put_field";
       int new_rec_len, new_field_num;
       int nwords;
       int ret;

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       if ((ret = dbeng_put_which_field(table, field_num, &new_field_num)) !=
           DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_which_field", ret);
          return(ret);
          }

       logman("%s:enter,tid=%d,d=%s,l=%d,f=%d,nf=%d", mname, table->tid,
                     new_data, strlen(new_data), field_num, new_field_num);

       /* record can be empty */
       /* if null record, create a stub */

       if (table->rec == NULL)
          {
          if ((old_rec = malloc(25)) == NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[old_rec]", DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          old_rec[0] = EOS;
          }
       else
          old_rec = initstring(table->rec); /* otherwise, copy the current rec */

       nwords = ll_words(old_rec, DBENG_FM);
       new_rec_len = strlen(old_rec) + strlen(new_data) + 1;

       if (new_field_num > nwords)
          new_rec_len += new_field_num - nwords;

       /* make sure record is not too big */

       if (DBENG_MAXRECFIELD > 0 && new_rec_len >= DBENG_MAXRECFIELD)
          {
          dbeng_io_code_log(mname, "rec too large", DBENG_RECORD_TOO_LARGE);
          free(old_rec);
          return(DBENG_RECORD_TOO_LARGE);
          }

       if ((new_rec = malloc(new_rec_len)) == NULL)
          {
          free(old_rec);
          dbeng_io_code_log(mname, "alloc fail[new_rec]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if (!ll_wordput(old_rec, new_rec, new_data, new_field_num, DBENG_FM))
          {
          free(old_rec);
          free(new_rec);
          dbeng_io_code_log(mname, "bad ret[ll_wordput]",
                            DBENG_PUT_ERROR);
          return(DBENG_PUT_ERROR);
          }

       ret = dbeng_rec_resize(table, strlen(new_rec));

       if (ret != DBENG_OK)
          {
          free(old_rec);
          free(new_rec);
          dbeng_io_code_log(mname, "bad ret[rec_resize]", ret);
          return(ret);
          }

       strcpy(table->rec, new_rec);
       table->change_rec_flag = TRUE;
       free(old_rec);
       free(new_rec);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_put_subfield(struct dbeng_table *table, char *new_data,
                           int field_num, int subfield_num)
    {
       /* Store new data in a sub-field and re-size record as
          appropriate. Function returns 'DBENG_OK' upon success,
          error code otherwise. */

       char *old_field, *new_field;
       char mname[] = "dbeng_put_subfield";
       int field_len, new_field_len, new_fnum, new_sfnum;
       int nwords;
       int ret;

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       if ((ret = dbeng_put_which_subfield(table, field_num, subfield_num,
           &new_fnum, &new_sfnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_which_subfield", ret);
          return(ret);
          }

       logman("%s:enter,tid=%d,d=%s,l=%d,f=%d,sf=%d,nf=%d,nsf=%d", mname,
                     table->tid, new_data, strlen(new_data), field_num,
                     subfield_num, new_fnum, new_sfnum);

       /* if the field does not already exist, create it
          unless there is no current record */

       ret = dbeng_field_size(table, new_fnum, &field_len);

       switch(ret)
          {
          case DBENG_OK:
             break;

          case DBENG_NO_RECORD:
             field_len = 0;
             break;

          case DBENG_NO_SUCH_FIELD:
             logman("%s:field[%d] does not exist,creating it",
                           mname, new_fnum);

             if ((ret = dbeng_put_field(table, "", new_fnum)) != DBENG_OK)
                {
                dbeng_io_code_log(mname, "bad rc from dbeng_put_field", ret);
                return(ret);
                }

             break;

          default:
             dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
             return(ret);
          };

       /* it is possible that the field is empty, in which case, create a stub */

       if (!field_len)
          {
          if ((old_field = (char *)malloc(25)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[old_field]1", DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          old_field[0] = EOS;
          }
       else
          {
          if ((old_field = (char *)malloc(field_len + 1)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[old_field]2", DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          if ((ret = dbeng_get_field(table, old_field, new_fnum)) != DBENG_OK)
             {
             free(old_field);
             dbeng_io_code_log(mname, "bad rc from dbeng_get_field", ret);
             return(ret);
             }
          }

       nwords = ll_words(old_field, DBENG_SFM);
       new_field_len = strlen(old_field) + strlen(new_data) + 1;

       if (new_sfnum > nwords)
          new_field_len += new_sfnum - nwords;

       if ((new_field = malloc(new_field_len)) == NULL)
          {
          free(old_field);
          dbeng_io_code_log(mname, "alloc fail[new_field]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if (!ll_wordput(old_field, new_field, new_data, new_sfnum, DBENG_SFM))
          {
          free(old_field);
          free(new_field);
          dbeng_io_code_log(mname, "bad ret[ll_wordput]",
                            DBENG_PUT_ERROR);
          return(DBENG_PUT_ERROR);
          }

       free(old_field);

       /* replace field */

       if ((ret = dbeng_put_field(table, new_field, new_fnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret[dbeng_put_field]", ret);
          free(new_field);
          return(ret);
          }

       free(new_field);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_put_subsubfield(struct dbeng_table *table, char *new_data,
                           int field_num, int subfield_num, int subsubfield_num)
    {
       /* Store new data in a sub-sub-field and re-size record as
          appropriate. Function returns 'DBENG_OK' upon success,
          error code otherwise. */

       char *old_subfield, *new_subfield;
       char mname[] = "dbeng_put_subsubfield";
       int subfield_len, new_subfield_len, new_fnum, new_sfnum, new_ssfnum;
       int nwords;
       int ret;

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       if ((ret = dbeng_put_which_subsubfield(table, field_num, subfield_num,
           subsubfield_num, &new_fnum, &new_sfnum, &new_ssfnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_which_subsubfield", ret);
          return(ret);
          }

       logman("%s:enter,tid=%d,d=%s,l=%d,f=%d,sf=%d,ssf=%d,nf=%d,nsf=%d,"
                     "nssf=%d", mname, table->tid, new_data, strlen(new_data),
                     field_num, subfield_num, subsubfield_num, new_fnum,
                     new_sfnum, new_ssfnum);

       /* if the field and/or sub-field does not already exist, create it */

       ret = dbeng_subfield_size(table, new_fnum, new_sfnum, &subfield_len);

       switch(ret)
          {
          case DBENG_OK:
             break;

          case DBENG_NO_RECORD:
             subfield_len = 0;
             break;

          case DBENG_NO_SUCH_FIELD:
          case DBENG_NO_SUCH_SUBFIELD:
             logman("%s:field or subfield does not exist,creating it",
                           mname);

             if ((ret = dbeng_put_subfield(table, "", new_fnum, new_sfnum)) !=
                 DBENG_OK)
                {
                dbeng_io_code_log(mname, "bad rc from dbeng_put_field", ret);
                return(ret);
                }

             break;

          default:
             dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
             return(ret);
          };

       /* it is possible that the field is empty, in which case, create a stub */

       if (!subfield_len)
          {
          if ((old_subfield = (char *)malloc(25)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[old_subfield]1",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          old_subfield[0] = EOS;
          }
       else
          {
          if ((old_subfield = (char *)malloc(subfield_len + 1)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[old_subfield]2",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          if ((ret = dbeng_get_subfield(table, old_subfield, new_fnum,
               new_sfnum)) != DBENG_OK)
             {
             free(old_subfield);
             dbeng_io_code_log(mname, "bad rc from dbeng_get_subfield", ret);
             return(ret);
             }
          }

       nwords = ll_words(old_subfield, DBENG_SSFM);
       new_subfield_len = strlen(old_subfield) + strlen(new_data) + 1;

       if (new_ssfnum > nwords)
          new_subfield_len += new_ssfnum - nwords;

       if ((new_subfield = (char *)malloc(new_subfield_len)) == (char *)NULL)
          {
          free(old_subfield);
          dbeng_io_code_log(mname, "alloc fail[new_subfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if (!ll_wordput(old_subfield, new_subfield, new_data, new_ssfnum,
           DBENG_SSFM))
          {
          free(old_subfield);
          free(new_subfield);
          dbeng_io_code_log(mname, "bad ret[ll_wordput]",
                            DBENG_PUT_ERROR);
          return(DBENG_PUT_ERROR);
          }

       free(old_subfield);

       /* replace sub-field */

       if ((ret = dbeng_put_subfield(table, new_subfield, new_fnum,
            new_sfnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret[dbeng_put_subfield]", ret);
          free(new_subfield);
          return(ret);
          }

       free(new_subfield);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_find(struct dbeng_table *ot, int cs_flag, const char *find_str,
                   int *field_num)
    {
       /* Locate 'find_str' within record data. All fields within
          each record will be searched. Find will begin at current
          record and continue until EOF or a record is found. Only whole
          fields will match. String comparison is based on 'cs_flag' for
          case sensitivity ('1' is case insensitivity, '0' is case sensitivity).
          Function returns 'DBENG_OK' upon success, an
          error code otherwise. If the find is successful, 'field_num' will
          be loaded with field number that matched. */

       char *field_data;
       char mname[50];
       int nwords, cmp_result;
       int done;
       int ret;
       int i;

       strcpy(mname, "dbeng_find");

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

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

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

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

       logman("%s:enter,tid=%d,cs=%d,fs=%s,l=%d", mname, ot->tid, cs_flag,
               find_str, strlen(find_str));

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* get a record if none is present (do not count unsaved rec in memory
          if one is present) */

       if (ot->record_number == 0L)
          {
          logman("%s:no current record,getting first", mname);
          (void)dbeng_goto_top(ot);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad ret first[get_recd]", ret);
             return(ret);
             }
          }

       done = FALSE;
       *field_num = 0;
       ot->change_rec_flag = FALSE;

       while(!done)
          {
          if ((field_data = malloc(strlen(ot->rec) + 1)) == NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[field_data]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          nwords = ll_words(ot->rec, DBENG_FM);

          for(i = 1; i <= nwords; i++)
             {
             ret = dbeng_get_field(ot, field_data, i);

             if (ret != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[get_field]", ret);
                return(ret);
                }

             if ((ret = dbeng_compare_field(field_data, (char *)find_str, cs_flag,
                                            &cmp_result)) != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[dbeng_compare_field]", ret);
                return(ret);
                }

             if (!cmp_result)
                {
                *field_num = i;
                free(field_data);
                dbeng_io_code_log(mname, "match found,normal exit", DBENG_OK);
                return(DBENG_OK);
                }
             }

          free(field_data);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             done = TRUE;
          }

       dbeng_io_code_log(mname, "no match,normal exit", ret);
       return(ret);
    }

    int dbeng_find_field(struct dbeng_table *ot, int cs_flag, const char *find_str,
                         int field_num)
    {
       /* Locate 'find_str' within the 'field_num' field of a record.
          Each record will be searched. Find will begin at current
          record and continue until EOF or a record is found. Only
          whole fields will match. String comparison is obtained from
          'cs_flag', for case sensitivity. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       char *field_data;
       char mname[50];
       int nwords, cmp_result;
       int done;
       int ret;

       strcpy(mname, "dbeng_find_field");

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

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

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

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

       logman("%s:enter,tid=%d,cs=%d,fs=%s,l=%d,fn=%d", mname,
                     ot->tid, cs_flag, find_str, strlen(find_str), field_num);

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* get a record if none is present (do not count unsaved rec in memory
          if one is present) */

       if (ot->record_number == 0L)
          {
          logman("%s:no current record,getting first", mname);
          (void)dbeng_goto_top(ot);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad ret first[get_recd]", ret);
             return(ret);
             }
          }

       done = FALSE;
       ot->change_rec_flag = FALSE;

       while(!done)
          {
          if ((field_data = malloc(strlen(ot->rec) + 1)) == NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[field_data]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          nwords = ll_words(ot->rec, DBENG_FM);

          if (nwords >= field_num)
             {
             ret = dbeng_get_field(ot, field_data, field_num);

             if (ret != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[get_field]", ret);
                return(ret);
                }

             if ((ret = dbeng_compare_field(field_data, (char *)find_str, cs_flag,
                                            &cmp_result)) != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[dbeng_compare_field", ret);
                return(ret);
                }

             if (!cmp_result)
                {
                free(field_data);
                dbeng_io_code_log(mname, "match found,normal exit", DBENG_OK);
                return(DBENG_OK);
                }
             }

          free(field_data);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             done = TRUE;
          }

       dbeng_io_code_log(mname, "no match,normal exit", ret);
       return(ret);
    }

    int dbeng_find_part(struct dbeng_table *ot, int cs_flag, const char *find_str,
                        int *field_num)
    {
       /* Locate 'find_str' within record data. All fields within
          each record will be searched. Find will begin at current
          record and continue until EOF or a record is found. Any part
          of a field will match. String comparison is based on 'cs_flag'
          for case sensitivity. Function returns 'DBENG_OK' upon success,
          an error code otherwise. If the find is successful, 'field_num' will
          be loaded with field number that matched. */

       char *field_data;
       char mname[50];
       int nwords;
       int done;
       int ret;
       int process;
       int field_len;
       int len_find_str;
       int i, j;

       strcpy(mname, "dbeng_find_part");

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

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

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

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

       logman("%s:enter,tid=%d,cs=%d,fs=%s,l=%d", mname, ot->tid, cs_flag,
               find_str, strlen(find_str));

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* get a record if none is present (do not count unsaved rec in memory
          if one is present) */

       if (ot->record_number == 0L)
          {
          (void)dbeng_goto_top(ot);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad ret first[get_recd]", ret);
             return(ret);
             }
          }

       done = FALSE;
       len_find_str = strlen(find_str);
       *field_num = 0;
       ot->change_rec_flag = FALSE;

       while(!done)
          {
          if ((field_data = malloc(strlen(ot->rec) + 1)) == NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[field_data]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          nwords = ll_words(ot->rec, DBENG_FM);

          for(i = 1; i <= nwords; i++)
             {
             ret = dbeng_get_field(ot, field_data, i);

             if (ret != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[get_field]", ret);
                return(ret);
                }

             field_len = strlen(field_data);
             process = TRUE;

             /* do not compare if length of field is less than
                length of search string */

             if (field_len < len_find_str)
                process = FALSE;

             if (process)
                {
                for(j = 0; j < field_len; j++)
                   {
                   if (strlen(&field_data[j]) < len_find_str)
                      break;

                   if (cs_flag)
                      ret = strnicmp(&field_data[j], (char *)find_str, len_find_str);
                   else
                      ret = strncmp(&field_data[j], find_str, len_find_str);

                   if (!ret)
                      {
                      *field_num = i;
                      free(field_data);
                      dbeng_io_code_log(mname, "match found,normal exit",
                                        DBENG_OK);
                      return(DBENG_OK);
                      }
                   }
                }
             }

          free(field_data);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             done = TRUE;
          }

       dbeng_io_code_log(mname, "no match,normal exit", ret);
       return(ret);
    }

    int dbeng_find_field_part(struct dbeng_table *ot, int cs_flag,
                              const char *find_str, int field_num)
    {
       /* Locate 'find_str' within the 'field_num' field of a record.
          Each record will be searched. Find will begin at current
          record and continue until EOF or a record is found. Any part
          of the field will match. String comparison is obtained from
          'cs_flag', for case sensitivity. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       char *field_data;
       char mname[50];
       int field_len;
       int len_find_str;
       int process;
       int nwords;
       int done;
       int ret;
       int i;

       strcpy(mname, "dbeng_find_field_part");

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

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

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

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

       logman("%s:enter,tid=%d,cs=%d,fs=%s,l=%d,fn=%d", mname,
                     ot->tid, cs_flag, find_str, strlen(find_str), field_num);

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* get a record if none is present (do not count unsaved rec in memory
          if one is present) */

       if (ot->record_number == 0L)
          {
          (void)dbeng_goto_top(ot);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad ret first[get_recd]", ret);
             return(ret);
             }
          }

       done = FALSE;
       len_find_str = strlen(find_str);
       ot->change_rec_flag = FALSE;

       while(!done)
          {
          if ((field_data = malloc(strlen(ot->rec) + 1)) == NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[field_data]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          process = TRUE;
          nwords = ll_words(ot->rec, DBENG_FM);

          if (field_num > nwords)
             process = FALSE;

          if (process)
             {
             ret = dbeng_get_field(ot, field_data, field_num);

             if (ret != DBENG_OK)
                {
                free(field_data);
                dbeng_io_code_log(mname, "bad ret[get_field]", ret);
                return(ret);
                }

             field_len = strlen(field_data);

             if (field_len < len_find_str)
                process = FALSE;
             }

          if (process)
             {
             for(i = 0; i < field_len; i++)
                {
                if (strlen(&field_data[i]) < len_find_str)
                   break;

                if (cs_flag)
                   ret = strnicmp(&field_data[i], (char *)find_str, len_find_str);
                else
                   ret = strncmp(&field_data[i], find_str, len_find_str);

                if (!ret)
                   {
                   free(field_data);
                   dbeng_io_code_log(mname, "match found,normal exit", DBENG_OK);
                   return(DBENG_OK);
                   }
                }
             }

          free(field_data);
          ret = dbeng_get_recd(ot);

          if (ret != DBENG_OK)
             done = TRUE;
          }

       dbeng_io_code_log(mname, "no match,normal exit", ret);
       return(ret);
    }

    int dbeng_nfields(struct dbeng_table *ot, int *nfields)
    {
       /* Count the number of fields in the current record. Number
          of fields is loaded into 'nfields' upon success. Function
          returns 'DBENG_OK' upon success, an error code otherwise. */

       char mname[50];
       int ret;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *nfields = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       *nfields = ll_words(ot->rec, DBENG_FM);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_nsubfields(struct dbeng_table *ot, int fieldno, int *nsubfields)
    {
       /* Count the number of sub-fields in the 'fieldno' field. Number
          of sub-fields is loaded into 'nsubfields' upon success. Function
          returns 'DBENG_OK' upon success, an error code otherwise. */

       char mname[] = "dbeng_nsubfields", *thefield;
       int ret, fsize;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

       *nsubfields = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       /* attempt to get the size of the requested field */

       if ((ret = dbeng_field_size(ot, fieldno, &fsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
          return(ret);
          }

       if ((thefield = (char *)malloc(fsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thefield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_field(ot, thefield, fieldno)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_field", ret);
          free(thefield);
          return(ret);
          }

       *nsubfields = ll_words(thefield, DBENG_SFM);
       free(thefield);
       logman("%s:normal exit:nsubfields=%d", mname, *nsubfields);
       return(DBENG_OK);
    }

    int dbeng_nsubsubfields(struct dbeng_table *ot, int fieldno,
                            int sfieldno, int *nsubsubfields)
    {
       /* Count the number of sub-sub-fields in the 'sfieldno' sub-field. Number
          of sub-sub-fields is loaded into 'nsubsubfields' upon success. Function
          returns 'DBENG_OK' upon success, an error code otherwise. */

       char mname[] = "dbeng_nsubsubfields", *thesfield;
       int ret, sfsize;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

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

       *nsubsubfields = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       /* attempt to get the size of the requested sub-field */

       if ((ret = dbeng_subfield_size(ot, fieldno, sfieldno, &sfsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
          return(ret);
          }

       if ((thesfield = (char *)malloc(sfsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thesfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_subfield(ot, thesfield, fieldno, sfieldno)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_subfield", ret);
          free(thesfield);
          return(ret);
          }

       *nsubsubfields = ll_words(thesfield, DBENG_SSFM);
       free(thesfield);
       logman("%s:normal exit:nsubsubfields=%d", mname, *nsubsubfields);
       return(DBENG_OK);
    }

    int dbeng_count_rec(struct dbeng_table *table, long *active_count,
                        long *deleted_count)
    {
       /* Physically count the number active and deleted records in 'table'
          and return upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       char mname[] = "dbeng_count_rec";
       long active_num = 0L;
       long deleted_num = 0L;
       int done = FALSE;
       int ret, delete_flag;

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

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

       *active_count = *deleted_count = 0L;
       logman("%s:enter:tid=%d", mname, table->tid);

       if (table->enforce_change_rec_flag && table->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* get current value of table 'delete_flag' */

       (void)dbeng_delete_flag(table, &delete_flag);

       /* set delete flag */

       (void)dbeng_set_delete_flag(table, TRUE);
       ret = dbeng_goto_top(table);

       if (ret != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error going top", ret);
          return(ret);
          }

       while(!done)
          {
          ret = dbeng_get_recd(table);

          if (ret == DBENG_EOF)
             done = TRUE;
          else
             if (ret != DBENG_OK)
                return(ret);
             else
                table->record_status ? active_num++ : deleted_num++;
          }

       /* set flag back to original value */

       (void)dbeng_set_delete_flag(table, delete_flag);
       logman("%s:normal exit:%ld active,%ld deleted records counted",
                     mname, active_num, deleted_num);
       *active_count = active_num;
       *deleted_count = deleted_num;
       return(DBENG_OK);
    }

    int dbeng_rec_count(struct dbeng_table *ot, long *active_count,
                        long *deleted_count)
    {
       /* Obtain the current active and deleted record counts. Function returns the
          record count values and 'DBENG_OK' upon success, an error
          code otherwise. */

       char mname[75];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

       *active_count = ot->active_record_count;
       *deleted_count = ot->deleted_record_count;
       logman("%s:normal exit[ok]", mname);
       return(DBENG_OK);
    }

    int dbeng_goto_record(struct dbeng_table *table, long rec_num)
    {
       /* Goto a specific record in the file. */

       char mname[50];
       long i;
       int ret;

       strcpy(mname, "dbeng_goto_record");

       if (table == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[table]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (rec_num <= 0L)
          {
          dbeng_io_code_log(mname, "out of range[rec_num]",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter:rn=%ld", mname, rec_num);

       if (table->enforce_change_rec_flag && table->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       ret = dbeng_goto_top(table);

       if (ret != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error going top", ret);
          return(ret);
          }

       ret = dbeng_get_recd(table);

       if (ret != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret first[get_recd]", ret);
          return(ret);
          }

       for(i = 1L; i < rec_num; i++)
          if (dbeng_get_recd(table) == DBENG_EOF && i < rec_num)
             {
             dbeng_io_code_log(mname, "exit with eof", DBENG_EOF);
             return(DBENG_EOF);
             }

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

    int dbeng_get_recd(struct dbeng_table *ot)
    {
       /* Obtain the next record in the file. */

       char mname[] = "dbeng_get_recd";
       char ferror[128], rec_status;
       long file_pos;
       int bytes_read, len;
       int done = FALSE, process;
       int ret;

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       /* loop to get record */

       while(!done)
          {
          file_pos = ftell(ot->handle);
          /* logman("%s:fp=%ld,op=%ld,cp=%ld", mname, file_pos,
                 ot->orig_position, ot->current_position); */

          /* read and verify record header */

          if ((ret = dbeng_record_header(ot, &len, &rec_status)) != DBENG_OK)
             {
             if (ret == DBENG_FILE_DATA_ERROR)
                {
                sprintf(ferror, "%s:error in header of "
                        "record %ld,tid=%d", mname,
                        ot->record_number + 1L, ot->tid);
                dbeng_ferror(ferror);
                }

             /* goto top of table on any error code */

             (void)dbeng_goto_top(ot);
             return(ret);
             }

          /* store starting position of this record */

          ot->orig_position = file_pos;

          if (DBENG_MAXRECFIELD > 0 && len >= DBENG_MAXRECFIELD)
             {
             sprintf(ferror, "%s:error:record %ld too large(%d)\n"
                           "must be no greater than %d,table=%s",
                           mname, ot->record_number, len, DBENG_MAXRECFIELD,
                           ot->name);
             dbeng_ferror(ferror);
             return(DBENG_RECORD_TOO_LARGE);
             }

          /* get status and process only if active record considering the
             'process_deleted' flag */

          process = (rec_status == 'A') ? TRUE : FALSE;
          ot->record_status = process;

          if (!process)
             if (rec_status == 'D' && ot->process_deleted)
                process = TRUE;

          /* if marked for deletion, skip */

          if (!process && rec_status == 'D')
             {
             ret = dbeng_rec_resize(ot, len);

             if (ret != DBENG_OK)
                return(ret);

             bytes_read = fread(ot->rec, 1, len, ot->handle);

             if (bytes_read < len)
                {
                sprintf(ferror, "%s:unexpected end of file reading record %ld"
                        " of table %s", mname, ot->record_number + 1L,
                        ot->name);
                dbeng_ferror(ferror);
                return(DBENG_FILE_DATA_ERROR);
                }

             /* error if record mark is not the last byte */

             if ((unsigned char)ot->rec[len - 1] != DBENG_RM)
                {
                sprintf(ferror, "%s:error, no record mark "
                        "on record %ld,table=%s", mname, ot->record_number + 1L,
                        ot->name);
                dbeng_ferror(ferror);
                return(DBENG_FILE_DATA_ERROR);
                }
             }

          if (process)
             {
             ret = dbeng_rec_resize(ot, len);

             if (ret != DBENG_OK)
                return(ret);

             done = TRUE;
             bytes_read = fread(ot->rec, 1, len, ot->handle);

             if (bytes_read < len)
                {
                sprintf(ferror, "%s:unexpected end of file reading record %ld"
                              ",table=%s", mname, ot->record_number + 1L,
                              ot->name);
                dbeng_ferror(ferror);
                return(DBENG_FILE_DATA_ERROR);
                }

             if ((unsigned char)ot->rec[len - 1] != DBENG_RM)
                {
                sprintf(ferror, "%s:error, no record mark on record %ld,"
                              "table=%s", mname, ot->record_number + 1L,
                              ot->name);
                dbeng_ferror(ferror);
                return(DBENG_FILE_DATA_ERROR);
                }

             ot->rec[len - 1] = EOS;        /* remove record separator */
             ot->current_position = ftell(ot->handle);
             // logman("%s:cp=%ld", mname, ot->current_position);
             ot->record_number++;
             }
          }

       return(DBENG_OK);
    }

    int dbeng_rec_resize(struct dbeng_table *ot, const int new_size)
    {
       /* Resize the table record to 'new_size'. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       char mname[50];

       strcpy(mname, "dbeng_rec_resize");

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       /* delete current record, if one present */

       if (dbeng_is_active_rec(ot) == DBENG_OK)
          free(ot->rec);

       /* allocate new record */

       if ((ot->rec = malloc(new_size + 1)) == (char *)NULL)
          return(DBENG_MEMORY_FAIL);

       return(DBENG_OK);
    }

    int dbeng_rec_size(struct dbeng_table *ot, int *rec_size)
    {
       /* Get the current record size or length. Upon success,
          'rec_size' will be loaded with the size and function
          will return 'DBENG_OK'. Function will return an
          error code (and 'rec_size' will be set to zero) upon
          failure. */

       char mname[50];
       int ret;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *rec_size = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       *rec_size = strlen(ot->rec);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_field_size(struct dbeng_table *ot, const int field_num,
                         int *field_size)
    {
       /* Get the size or length of field 'field_num' 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. */

       char mname[] = "dbeng_field_size";
       int ret;
       int nfields;

       logman("%s:enter:fn=%d", mname, field_num);

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

       *field_size = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       if ((ret = dbeng_nfields(ot, &nfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret[nfields]", ret);
          return(ret);
          }

       if (field_num > nfields)
          {
          dbeng_io_code_log(mname, "field out of range", DBENG_NO_SUCH_FIELD);
          return(DBENG_NO_SUCH_FIELD);
          }

       *field_size = ll_wordlen(ot->rec, field_num, DBENG_FM);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_subfield_size(struct dbeng_table *ot, int field_num,
                            int subfield_num, int *subfield_size)
    {
       /* Get the size or length of sub-field 'subfield_num'
          within the field 'field_num' in the current
          record. Variable 'subfield_size' will be loaded with the
          sub-field size upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       char mname[] = "dbeng_subfield_size", *thefield;
       int ret, nsubfields, fsize;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

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

       *subfield_size = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       if ((ret = dbeng_nsubfields(ot, field_num, &nsubfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_nsubfields", ret);
          return(ret);
          }

       if (subfield_num > nsubfields)
          {
          dbeng_io_code_log(mname, "out of range[subfield_num]",
                            DBENG_NO_SUCH_SUBFIELD);
          return(DBENG_NO_SUCH_SUBFIELD);
          }

       /* attempt to get the size of the requested field */

       if ((ret = dbeng_field_size(ot, field_num, &fsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
          return(ret);
          }

       if ((thefield = (char *)malloc(fsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thefield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_field(ot, thefield, field_num)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_field", ret);
          free(thefield);
          return(ret);
          }

       *subfield_size = ll_wordlen(thefield, subfield_num, DBENG_SFM);
       free(thefield);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_subsubfield_size(struct dbeng_table *ot, int field_num,
                               int subfield_num, int subsubfield_num,
                               int *subsubfield_size)
    {
       /* Get the size or length of sub-sub-field 'subsubfield_num'
          within the sub-field 'subfield_num' in the current
          record. Variable 'subsubfield_size' will be loaded with the
          sub-sub-field size upon success. Function returns 'DBENG_OK'
          upon success, an error code otherwise. */

       char mname[] = "dbeng_subsubfield_size", *thesubfield;
       int ret, nsubsubfields, sfsize;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

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

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

       *subsubfield_size = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       if ((ret = dbeng_nsubsubfields(ot, field_num, subfield_num,
            &nsubsubfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_nsubsubfields", ret);
          return(ret);
          }

       if (subsubfield_num > nsubsubfields)
          {
          dbeng_io_code_log(mname, "out of range[subsubfield_num]",
                            DBENG_NO_SUCH_SUBSUBFIELD);
          return(DBENG_NO_SUCH_SUBSUBFIELD);
          }

       /* attempt to get the size of the requested sub-field */

       if ((ret = dbeng_subfield_size(ot, field_num, subfield_num,
            &sfsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
          return(ret);
          }

       if ((thesubfield = (char *)malloc(sfsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[thesubfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_subfield(ot, thesubfield, field_num,
            subfield_num)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_get_subfield", ret);
          free(thesubfield);
          return(ret);
          }

       *subsubfield_size = ll_wordlen(thesubfield, subsubfield_num, DBENG_SSFM);
       free(thesubfield);
       logman("%s:normal exit", mname);
       return(DBENG_OK);
    }

    int dbeng_rec_num(struct dbeng_table *ot, long *rec_num)
    {
       /* Obtain the current record number. Function returns the
          current record number in 'rec_num' and 'DBENG_OK' upon
          success, an error code otherwise. */

       char mname[50];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *rec_num = 0L;

       if (ot->record_number <= 0L)
          {
          dbeng_io_code_log(mname, "no current rec", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       *rec_num = ot->record_number;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_pos(struct dbeng_table *ot, long *pos)
    {
       /* Obtain the current file position. Function returns the
          position of the start of the current record in 'pos' and
          'DBENG_OK' upon success, an error code otherwise. */

       char mname[50];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *pos = 0L;

       if (ot->orig_position < 0L)
          {
          dbeng_io_code_log(mname, "no current rec", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       *pos = ot->orig_position;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_pos(struct dbeng_table *ot, long pos)
    {
       /* Set the current file position. 'dbeng' structure
          member 'orig_position' will be updated along with
          moving the file pointer. BE VERY CAREFUL. You
          be sure there is a record at the specified
          'pos'. Current record contents are
          not changed. Only the file pointer position is moved.
          A record is not read into the buffer. Function
          returns 'DBENG_OK' upon success, an error code
          otherwise. */

       char mname[] = "dbeng_set_pos";

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       if (ot->enforce_change_rec_flag && ot->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       ot->orig_position = ot->current_position = pos;
       fseek(ot->handle, ot->orig_position, 0);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_change_rec_flag(struct dbeng_table *ot, int *flag)
    {
       /* Obtain the current 'change_rec_flag' value. Function returns the
          value of the flag in 'flag' and 'DBENG_OK' upon success, an error
          code otherwise. */

       char mname[50];
       int ret;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *flag = 0;

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       *flag = ot->change_rec_flag;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_change_rec_flag(struct dbeng_table *ot, int flag)
    {
       /* Set a new 'change_rec_flag' value. Function returns 'DBENG_OK' upon
          success, an error code otherwise. Flag must be either 'TRUE' or
          'FALSE'. */

       char mname[50];
       int ret;

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

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

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

       if ((ret = dbeng_is_active_rec(ot)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "no current rec", ret);
          return(ret);
          }

       ot->change_rec_flag = flag;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_delete_flag(struct dbeng_table *ot, int *flag)
    {
       /* Obtain the current 'process_deleted' value. Function returns the
          value of the flag in 'flag' and 'DBENG_OK' upon success, an error
          code otherwise. */

       char mname[50];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *flag = ot->process_deleted;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_delete_flag(struct dbeng_table *ot, int flag)
    {
       /* Set a new 'process_deleted' value. Function returns 'DBENG_OK' upon
          success, an error code otherwise. Flag must be either 'TRUE' or
          'FALSE'. */

       char mname[50];

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

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

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

       ot->process_deleted = flag;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_enf_change_rec_flag(struct dbeng_table *ot, int *flag)
    {
       /* Obtain the current 'enforce_change_rec_flag' value. Function
          returns the value of the flag in 'flag' and 'DBENG_OK' upon
          success, an error code otherwise. */

       char mname[50];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *flag = ot->enforce_change_rec_flag;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_enf_change_rec_flag(struct dbeng_table *ot, int flag)
    {
       /* Set a new 'enforce_change_rec_flag' value. Function returns
          'DBENG_OK' upon success, an error code otherwise. 'Flag' must
          be either 'TRUE' or 'FALSE'. */

       char mname[50];

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

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

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

       ot->enforce_change_rec_flag = flag;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_autopack(struct dbeng_table *ot, int *pvalue)
    {
       /* Obtain the current 'autopack' value. Function returns the
          value in 'pvalue' and 'DBENG_OK' upon success, an error
          code otherwise. */

       char mname[] = "dbeng_autopack";
       int ret;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *pvalue = ot->autopack;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_autopack(struct dbeng_table *ot, int pvalue)
    {
       /* Set a new 'autopack' value. Function returns 'DBENG_OK' upon
          success, an error code otherwise. */

       char mname[] = "dbeng_set_autopack";
       int ret;

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

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

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

       ot->autopack = pvalue;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_is_table_locked(struct dbeng_table *ot, int *flag)
    {
       /* Obtain the current 'is_table_locked' value. Function
          returns the value of the flag in 'flag' and 'DBENG_OK' upon
          success, an error code otherwise. */

       char mname[25];

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *flag = ot->is_table_locked;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_is_table_locked(struct dbeng_table *ot, int flag)
    {
       /* Set a new 'is_table_locked' value. Function returns
          'DBENG_OK' upon success, an error code otherwise. 'Flag' must
          be either 'TRUE' or 'FALSE'. */

       char mname[30];
       int ret;

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

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

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

       if (flag && ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "table already locked", DBENG_TABLE_LOCKED);
          return(DBENG_TABLE_LOCKED);
          }

       if (flag == ot->is_table_locked)
          {
          dbeng_io_code_log(mname, "flag is already same value", DBENG_OK);
          return(DBENG_OK);
          }

       if (flag)
          ret = dbeng_lock_table(ot);
       else
          ret = dbeng_unlock_table(ot);

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

    int dbeng_lock_table(struct dbeng_table *ot)
    {
       /* Lock a table. This low level function should only be called
          by the function 'dbeng_set_is_table_locked'. This function
          does not check the status of the flag first. Every table with
          the exact file name will be locked. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_table *rov;
       int done = FALSE;

       if (dbeng_head == DBENG_OT_NULL)
          return(DBENG_NOT_EXIST);

       rov = dbeng_head;

       while(!done)
          {
          if (rov == DBENG_OT_NULL)
             done = TRUE;
          else
             {
             if (!strcmp(rov->name, ot->name))
                rov->is_table_locked = TRUE;
             }

          if (!done)
             rov = rov->next;
          }

       return(DBENG_OK);
    }

    int dbeng_unlock_table(struct dbeng_table *ot)
    {
       /* Unlock a table. This low level function should only be called
          by the function 'dbeng_set_is_table_locked'. This function
          does not check the status of the flag first. Every table with
          the exact file name will be unlocked. Function returns
          'DBENG_OK' upon success, an error code otherwise. */

       struct dbeng_table *rov;
       int done = FALSE;

       if (dbeng_head == DBENG_OT_NULL)
          return(DBENG_NOT_EXIST);

       rov = dbeng_head;

       while(!done)
          {
          if (rov == DBENG_OT_NULL)
             done = TRUE;
          else
             {
             if (!strcmp(rov->name, ot->name))
                rov->is_table_locked = FALSE;
             }

          if (!done)
             rov = rov->next;
          }

       return(DBENG_OK);
    }

    int dbeng_tmp_systable(int *tid)
    {
       /* Create a new system table with a unique file name.
          Function exists with the table open and the table
          tid placed into 'tid'. Function returns a 'dbeng' code. */

       char mname[] = "dbeng_tmp_systable", *tmp_path, rname[13];
       char *table_name;
       int ret, tmp_len;

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

       /* get temporary path from config data */

       if ((tmp_path = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[tmp_path]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_config_get_tmp_path(tmp_path)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error getting tmp path", DBENG_INTERNAL_ERROR);
          free(tmp_path);
          return(DBENG_INTERNAL_ERROR);
          }

       logman("%s:before unique", mname);

       /* generate a unique file name */

       if (!unique(tmp_path, rname))
          {
          logman("%s:cannot create tmp table[%s%c%s]", mname, tmp_path,
                  PATH_SEP, rname);
          free(tmp_path);
          return(DBENG_CANNOT_CREATE_TABLE);
          }

       logman("%s:after unique", mname);
       tmp_len = strlen(tmp_path) + strlen(rname) + 2;

       if ((table_name = (char *)malloc(tmp_len)) == (char *) NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[table_name]",
                            DBENG_MEMORY_FAIL);
          free(tmp_path);
          return(DBENG_MEMORY_FAIL);
          }

       /* build complete path and file name */

       sprintf(table_name, "%s%c%s", tmp_path, PATH_SEP, rname);
       free(tmp_path);
       logman("%s:tmp table is %s", mname, table_name);

       /* indicate to also open table in 'dbeng_new_systable' */

       *tid = 1;

       /* create new empty temporary system table */

       if ((ret = dbeng_new_systable(table_name, tid)) != DBENG_OK)
          {
          logman("%s:new table[%s] create error", mname, table_name);
          free(table_name);
          return(ret);
          }

       free(table_name);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_new_systable(char *fname, int *tid)
    {
       /* Create a new system table. If the 'tid' is a positive
          integer, also open the table. Function returns
          the table 'tid' upon success. Function returns
          a 'dbeng' code. */

       char mname[] = "dbeng_new_systable";
       int ret;

       if (fname == (char *)NULL || !strlen(fname))
          {
          dbeng_io_code_log(mname, "fname parameter error",
                            DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

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

       if ((ret = dbeng_ll_new_table(fname, tid, TRUE)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_ll_new_table", ret);
          return(ret);
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_new_table(char *fname, int *tid)
    {
       /* Create a new table. If the 'tid' is a positive
          integer, also open the table. Function returns
          the table 'tid' upon success. Function returns
          a 'dbeng' code. */

       char mname[] = "dbeng_new_table";
       int ret;

       if (fname == (char *)NULL || !strlen(fname))
          {
          dbeng_io_code_log(mname, "fname parameter error",
                            DBENG_INVALID_FILE_NAME);
          return(DBENG_INVALID_FILE_NAME);
          }

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

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

       if ((ret = dbeng_ll_new_table(fname, tid, FALSE)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_ll_new_table", ret);
          return(ret);
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    static int dbeng_ll_new_table(char *table_name, int *tid,
                                  int systable_flag)
    {
       /* Create a new empty table. If 'tid' is a positive
          number, the table will be opened and the TID will be
          returned in same. 'table_name' may be in two parts separated
          by a comma which will indicate the logical table name
          followed by the physical name, ie:

             test.primary,/hd1/bbuuzzb/tables/test.buz

          Do not put spaces before or after the comma.
          If the two part form is used, an entry in the system
          catalog will be made (whether or not the catalog flag
          is on). An entry with the same logical name must not
          already exist. If the two part form is not used, a
          physical file name is assumed (do not prefix
          the physical file name with 'DBENG_CATALOG_FNAME_IND'
          as in 'dbeng_open_table'. Function returns 'DBENG_OK' upon
          success, an engine i/o code otherwise. */

       char mname[] = "dbeng_ll_new_table";
       char *ntable;
       int ret;

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

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

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

       /* add an extra byte to the output table name in case the form
          '%name' is returned */

       if ((ntable = (char *)malloc(strlen(table_name) + 2)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[ntable]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* derive proper name that will be given to 'new.table',
          create the new empty file and catalog entry (if required) */

       if ((ret = dbeng_catalog_new_table(table_name, ntable, systable_flag))
          != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_catalog_new_table", ret);
          free(ntable);
          return(ret);
          }

       /* strcpy(ntable, table_name);

       if (!zcreate(ntable))
          {
          dbeng_io_code_log(mname, "error creating empty table",
                            DBENG_CANNOT_CREATE_TABLE);
          free(ntable);
          return(DBENG_CANNOT_CREATE_TABLE);
          } */

       if (*tid > 0)
          {
          ret = dbeng_ll_open_table(ntable, tid, systable_flag);
          dbeng_io_code_log(mname, "normal exit[with open]", ret);
          free(ntable);
          return(ret);
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       free(ntable);
       return(DBENG_OK);
    }

    int dbeng_delete_table(char *fname)
    {
       /* Delete a 'dbeng' (Bbuuzzb) table by name. The catalog will be
          used to obtain the physical file name if the catalog
          flag is high. Both catalog entry and physical file
          will be deleted. The table must not be in current use by
          any other 'tid'. Function returns a 'dbeng' code. */

       struct dbeng_table *ot;
       char mname[] = "dbeng_delete_table", *pname;
       int ret, tid, catalog_flag;

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

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

       /* verify table (and possibly catalog entry) by attempting
          to open the table */

       if ((ret = dbeng_open_table(fname, &tid)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_open_table", ret);
          return(ret);
          }

       /* get pointer to structure and get physical file name */

       if ((ot = dbeng_atid_lookup(tid)) == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "error getting table pointer",
                            DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

       /* copy physical file name */

       if (ot->name == (char *)NULL || !strlen(ot->name))
          {
          dbeng_io_code_log(mname, "null or empty[ot->name]", DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

       if ((pname = initstring(ot->name)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[pname]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* close table (if the session flag is high the physical file
          will already have been closed) */

       if ((ret = dbeng_close_table(ot)) != DBENG_OK)
          /* we might get a non-ok if the session table is active */

          dbeng_io_code_log(mname, "bad rc from dbeng_close_table", ret);

       /* make sure table is not in use by other tid's */

       if (dbeng_table_in_use(pname))
          {
          free(pname);
          dbeng_io_code_log(mname, "unable to delete table", DBENG_TABLE_IN_USE);
          return(DBENG_TABLE_IN_USE);
          }

       unlink(pname);
       free(pname);

       /* if catalog flag is high, attempt to obtain and delete
          the catalog entry (might fail if only the physical
          file name was supplied) */

       (void)dbeng_config_get_catalog_flag(&catalog_flag);

       if (catalog_flag)
          if ((ret = dbeng_catalog_delete(fname)) != DBENG_OK)
             dbeng_io_code_log(mname, "bad rc from dbeng_catalog_delete", ret);

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_open_table_list(char *pat, char *list_out)
    {
       /* Return the list of open tables. List is
          returned in 'list_out' which must be already
          allocated to sufficient size. The list is delimited
          by 'DEBNG_LIST_DELIM'. Each entry in the list contains
          the tid in use followed by the file path/name. The two
          fields in the list are delimited by a comma.
          The parameter 'pat' may contain a table path/file pattern
          acceptable to the 'pmatch' function. If 'pat' is
          'NULL' or empty, all tables in use
          will be returned. Function returns a 'dbeng' code. */

       struct dbeng_table *rov;
       char *before, *after, *wdata;
       char mname[] = "dbeng_open_table_list";
       int cnt, is_pat, process;

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

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

       is_pat = (pat == (char *)NULL || !strlen(pat)) ? FALSE : TRUE;

       if (is_pat)
          logman("%s:pat=%s", mname, pat);

       list_out[0] = EOS;
       rov = dbeng_head;

       /* it is not an error if the list is empty */

       if (rov == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "list is empty", DBENG_OK);
          return(DBENG_OK);
          }

       if ((before = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[before]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((after = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[after]", DBENG_MEMORY_FAIL);
          free(before);
          return(DBENG_MEMORY_FAIL);
          }

       if ((wdata = (char *)malloc(1023)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[wdata]", DBENG_MEMORY_FAIL);
          free(before);
          free(after);
          return(DBENG_MEMORY_FAIL);
          }

       before[0] = after[0] = EOS;
       cnt = 1;

       while(rov != DBENG_OT_NULL)
          {
          process = TRUE;

          /* match physical file name to pattern (if supplied) */

          if (is_pat)
             if (!pmatch(pat, rov->name))
                {
                process = FALSE;
                logman("%s:%s is no match", mname, rov->name);
                }
            else
               logman("%s:%s is a match", mname, rov->name);

          if (process)
             {
             sprintf(wdata, "%d,%s", rov->tid, rov->name);

             if (!ll_wordput(before, after, wdata, cnt, DBENG_LIST_DELIM))
                {
                dbeng_io_code_log(mname, "error putting list item",
                                  DBENG_INTERNAL_ERROR);
                free(before);
                free(after);
                free(wdata);
                return(DBENG_INTERNAL_ERROR);
                }

             cnt++;
             strcpy(before, after);
             }

          rov = rov->next;
          }

       strcpy(list_out, after);
       free(before);
       free(after);
       free(wdata);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_exist(char *tname, int *exist_flag)
    {
       /* Determine whether a database table exists. Naming
          convention is the same as for 'dbeng_open_table'.
          The flag 'exist_flag' will be loaded with the
          exist flag (TRUE=table exists). Function returns
          a 'dbeng' code. */

       struct dbeng_table *ot;
       char mname[] = "dbeng_exist";
       int ret, tid;

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

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

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

       *exist_flag = FALSE;

       if ((ret = dbeng_open_table(tname, &tid)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:table does not exist", DBENG_OK);
          return(DBENG_OK);
          }

       if ((ot = dbeng_atid_lookup(tid)) == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_atid_lookup",
                            DBENG_INTERNAL_ERROR);
          return(DBENG_INTERNAL_ERROR);
          }

       (void)dbeng_close_table(ot);
       *exist_flag = TRUE;
       dbeng_io_code_log(mname, "exit:table does exist", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_clear_table(char *tname)
    {
       /* Clear a table of all records by deleting the table and
          re-creating it. The table must not already be in use.
          Function returns a 'dbeng' code. */

       struct dbeng_table *rov;
       char mname[] = "dbeng_clear_table", *fname;
       int ret, tid, len, systable_flag = FALSE;

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

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

    #ifdef MULTIUSER
       // check for session table

       if (dbeng_config_is_session(tname))
          systable_flag = TRUE;
    #endif

       // check for catalog

       if (!systable_flag && dbeng_config_is_catalog(tname))
          systable_flag = TRUE;

       if (systable_flag)
          {
          /* if it is either the session table or the catalog,
             the name is already the physical file name, just
             copy it */

          if ((fname = (char *)malloc(strlen(tname) + 1)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[fname]", DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          strcpy(fname, tname);
          }

       /* open the table to make sure we access it's physical file name */

       if (!systable_flag)
          {
          if ((ret = dbeng_open_table(tname, &tid)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_open_table", ret);
             return(ret);
             }

          if ((rov = dbeng_atid_lookup(tid)) == DBENG_OT_NULL)
             {
             dbeng_io_code_log(mname, "bad rc[NULL] from dbeng_atid_lookup",
                               DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if ((len = strlen(rov->name)) == 0)
             {
             dbeng_io_code_log(mname, "table physical file name is empty",
                               DBENG_INTERNAL_ERROR);
             return(DBENG_INTERNAL_ERROR);
             }

          if ((fname = (char *)malloc(len + 1)) == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[fname]", DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          strcpy(fname, rov->name);
          (void)dbeng_close_table(rov);
          }

       /* if the table is in use by other tid's, refuse */

       if (dbeng_table_in_use(fname))
          {
          free(fname);
          dbeng_io_code_log(mname, "unable to clear table", DBENG_TABLE_IN_USE);
          return(DBENG_TABLE_IN_USE);
          }

       unlink(fname);

       if (!zcreate(fname))
          {
          dbeng_io_code_log(mname, "error creating new empty table",
                            DBENG_INTERNAL_ERROR);
          free(fname);
          return(DBENG_INTERNAL_ERROR);
          }

       free(fname);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_ll_open(struct dbeng_table *ot)
    {
       /* Open the file associated with the table 'ot'.
          File will only be open if the 'handle' is
          null. Function returns 'DBENG_OK' upon
          success, an engine i/o code otherwise. */

       char mname[] = "dbeng_ll_open";

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:null ot", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (ot->handle != (FILE *)NULL)
          {
          dbeng_io_code_log(mname, "exit:file already open",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if ((ot->handle = fopen(ot->name, "rb+")) == (FILE *)NULL)
          {
          dbeng_io_code_log(mname, "exit:cannot open file", DBENG_UNABLE_TO_OPEN);
          return(DBENG_UNABLE_TO_OPEN);
          }

       /* assign a buffer */

       /* if (setvbuf(ot->handle, NULL, _IOFBF, DBENG_IO_BUF_SIZE))
          {
          fclose(ot->handle);
          dbeng_io_code_log(mname, "exit:error 'setvbuf' on file",
                            DBENG_UNABLE_TO_OPEN);
          return(DBENG_UNABLE_TO_OPEN);
          } */

       /* reset file position */

       if (ot->current_position > 0L)
          fseek(ot->handle, ot->current_position, 0);

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_ll_close(struct dbeng_table *ot)
    {
       /* Close the file handle associated with the table 'ot'.
          Function returns 'DBENG_OK' upon success, an engine
          i/o code otherwise. */

       char mname[] = "dbeng_ll_close";

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:null ot", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:enter:tid=%d", mname, ot->tid);

       if (ot->handle == (FILE *)NULL)
          {
          dbeng_io_code_log(mname, "exit:file not open", DBENG_NO_SUCH_FILE);
          return(DBENG_NO_SUCH_FILE);
          }

       fclose(ot->handle);
       ot->handle = (FILE *)NULL;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_is_systable(struct dbeng_table *ot, int *flag)
    {
       /* Obtain the current 'is_systable' value. Function
          returns the value of the flag in 'flag' and 'DBENG_OK' upon
          success, an error code otherwise. */

       char mname[] = "dbeng_is_systable";

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *flag = ot->is_systable;
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_copy_table(struct dbeng_table *src, struct dbeng_table *dest)
    {
       /* Copy a table from source ('src') to destination ('dest'). No
          regard is given to the existing contents of the destination
          table (if any). Function returns a 'dbeng' code. */

       char mname[] = "dbeng_copy_table";
       int ret, session_flag;

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

       if (src == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[src]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (dest == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[dest]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       /* check for changed records in both source and destination table */

       if (src->enforce_change_rec_flag && src->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

       if (dest->enforce_change_rec_flag && dest->change_rec_flag)
          {
          dbeng_io_code_log(mname, "record changed", DBENG_RECORD_CHANGED);
          return(DBENG_RECORD_CHANGED);
          }

    #ifdef MULTIUSER
       /* if the session table is active, the source table will be open
          but not the destination table, we open it here */

       (void)dbeng_config_get_session_flag(&session_flag);

       if (session_flag)
          (void)dbeng_ll_open(dest);
    #endif

       /* use 'dbeng_ll_copy_table' to copy */

       if ((ret = dbeng_ll_copy_table(src, dest)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad ret from dbeng_ll_copy_table", ret);
          return(ret);
          }

    #ifdef MULTIUSER
       if (session_flag)
          (void)dbeng_ll_close(dest);
    #endif

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

    static int dbeng_record_header(struct dbeng_table *ot, int *len, char *status)
    {
       /* Read and verify the record header which is assumed to start
          at the current file position. Upon success, the record
          length is returned in 'len' and the record status is
          returned in 'status'. Function returns a 'dbeng' code. */

       char rec_head[DBENG_REC_HEAD_SIZE + 1];
       char mname[] = "dbeng_record_header";
       int rlen, bytes_read;

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

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

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

       /* read record header, assume record pointer is at start of header */

       bytes_read = fread(rec_head, 1, DBENG_REC_HEAD_SIZE, ot->handle);

       if (bytes_read < DBENG_REC_HEAD_SIZE)
          {
          logman("%s:exit:eof detected,tid=%d", mname, ot->tid);
          return(DBENG_EOF);
          }

       /* error if field mark not present */

       if ((unsigned char)rec_head[DBENG_REC_HEAD_SIZE - 1] != DBENG_FM)
          {
          rec_head[DBENG_REC_HEAD_SIZE - 1] = EOS;
          logman("%s:no field mark at end of header:tid=%d,rh=%s,l=%d,rc=%d",
                        mname, ot->tid, rec_head, strlen(rec_head),
                        DBENG_FILE_DATA_ERROR);
          return(DBENG_FILE_DATA_ERROR);
          }

       /* remove field separator */

       rec_head[DBENG_REC_HEAD_SIZE - 1] = EOS;

       /* attempt to get record length */

       if (!qatoi(&rec_head[1], &rlen))
          {
          dbeng_io_code_log(mname, "error:header length is non-numeric",
                            DBENG_FILE_DATA_ERROR);
          return(DBENG_FILE_DATA_ERROR);
          }

       *len = rlen;
       *status = rec_head[0];
       /* dbeng_io_code_log(mname, "normal exit", DBENG_OK); */
       return(DBENG_OK);
    }

    static int dbeng_put_which_field(struct dbeng_table *ot, int fnum,
                                     int *new_fnum)
    {
       /* Determine which field to put data into. A positive integer
          is assumed to be the absolute field number. A zero indicates
          to append a new field to the record. Function returns the
          field number loaded into 'new_fnum' upon success. Function
          returns a 'dbeng' code. */

       char mname[] = "dbeng_put_which_field";
       int nfields, ret;

       logman("%s:enter:fnum=%d", mname, fnum);

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       *new_fnum = 0;

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

       if (fnum > 0)
          {
          logman("%s:normal exit,new_fnum=%d", mname, fnum);
          *new_fnum = fnum;
          return(DBENG_OK);
          }

       if ((ret = dbeng_nfields(ot, &nfields)) != DBENG_OK)
          if (ret == DBENG_NO_RECORD)
             nfields = 0;
          else
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_nfields", ret);
             return(ret);
             }

       logman("%s:normal exit,new_fnum=%d", mname, nfields + 1);
       *new_fnum = nfields + 1;
       return(DBENG_OK);
    }

    static int dbeng_put_which_subfield(struct dbeng_table *ot, int fnum,
                                        int sfnum, int *new_fnum, int *new_sfnum)
    {
       /* Determine which field and subfield to put data into. A positive
          integer is assumed to be the absolute field/subfield number. A zero
          indicates to append a new field/subfield to the record. Function
          returns the field number loaded into 'new_fnum' and the new
          subfield number loaded into 'new_sfnum' upon success. Function
          returns a 'dbeng' code. */

       char mname[] = "dbeng_put_which_subfield";
       int nsubfields, ret;

       logman("%s:enter:fnum=%d,sfnum=%d", mname, fnum, sfnum);

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

       *new_fnum = *new_sfnum = 0;

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

       if ((ret = dbeng_put_which_field(ot, fnum, new_fnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_which_field", ret);
          return(ret);
          }

       /* if the field number was zero, set subfield number and exit */

       if (!fnum)
          {
          if (!sfnum)
             *new_sfnum = 1;
          else
             *new_sfnum = sfnum;

          logman("%s:exit[0]:new_fnum=%d,new_sfnum=%d", mname,
                        *new_fnum, *new_sfnum);
          return(DBENG_OK);
          }

       /* if the field number was not zero and subfield was not zero,
          set subfield and exit */

       if (sfnum)
          {
          *new_sfnum = sfnum;
          logman("%s:exit[0]:new_fnum=%d,new_sfnum=%d", mname,
                        *new_fnum, *new_sfnum);
          return(DBENG_OK);
          }

       /* field number was not zero and subfield was zero */

       ret = dbeng_nsubfields(ot, *new_fnum, &nsubfields);

       switch(ret)
          {
          case DBENG_OK:
             *new_sfnum = nsubfields + 1;
             break;

          case DBENG_NO_SUCH_FIELD:
          case DBENG_NO_RECORD:
             *new_sfnum = 1;
             ret = DBENG_OK;
             break;

          default:
             dbeng_io_code_log(mname, "bad rc from dbeng_nsubfields", ret);
             *new_fnum = *new_sfnum = 0;
          };

       logman("%s:exit[0]:new_fnum=%d,new_sfnum=%d", mname,
                     *new_fnum, *new_sfnum);
       return(ret);
    }

    static int dbeng_put_which_subsubfield(struct dbeng_table *ot, int fnum,
                                           int sfnum, int ssfnum, int *new_fnum,
                                           int *new_sfnum, int *new_ssfnum)
    {
       /* Determine which field, subfield and subsubfield to put data into.
          A positive integer is assumed to be the absolute
          field/subfield/subsubfield number. A zero indicates to append a new
          field/subfield/subsubfield to the record. Function returns the field
          number loaded into 'new_fnum', the new subfield number loaded into
          'new_sfnum' and the new subsubfield number loaded into 'new_ssfnum'
          upon success. Function returns a 'dbeng' code. */

       char mname[] = "dbeng_put_which_subsubfield";
       int nsubsubfields, ret;

       logman("%s:enter:fnum=%d,sfnum=%d,ssfnum=%d", mname, fnum, sfnum,
                     ssfnum);

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

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

       *new_fnum = *new_sfnum = *new_ssfnum = 0;

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

       if ((ret = dbeng_put_which_subfield(ot, fnum, sfnum, new_fnum, new_sfnum))
           != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_which_subfield", ret);
          return(ret);
          }

       /* subsubfield number was not zero */

       if (ssfnum)
          {
          *new_ssfnum = ssfnum;
          logman("%s:exit[0]:new_fnum=%d,new_sfnum=%d,new_ssfnum=%d", mname,
                        *new_fnum, *new_sfnum, *new_ssfnum);
          return(DBENG_OK);
          }

       /* subsubfield number zero */

       ret = dbeng_nsubsubfields(ot, *new_fnum, *new_sfnum, &nsubsubfields);

       switch(ret)
          {
          case DBENG_OK:
             *new_ssfnum = nsubsubfields + 1;
             break;

          case DBENG_NO_SUCH_SUBFIELD:
          case DBENG_NO_SUCH_FIELD:
          case DBENG_NO_RECORD:
             *new_ssfnum = 1;
             ret = DBENG_OK;
             break;

          default:
             dbeng_io_code_log(mname, "bad rc from dbeng_nsubsubfields", ret);
             *new_fnum = *new_sfnum = *new_ssfnum = 0;
          };

       logman("%s:exit[0]:new_fnum=%d,new_sfnum=%d,new_ssfnum=%d", mname,
                     *new_fnum, *new_sfnum, *new_ssfnum);
       return(ret);
    }

    static int dbeng_ll_copy_table(struct dbeng_table *src,
                                   struct dbeng_table *dest)
    {
       /* Copy a table from source ('src') to destination ('dest').
          Both tables are assumed to be open. No regard is given to
          existing contents of destination table. Records are copied
          in accordance with the current value of the source table
          'process_deleted' flag. Function exits with the tables
          remaining open. Function returns a 'dbeng' code. */

    #ifdef MULTIUSER
       struct dbeng_table *rov;
       int pos_changed;
    #endif
       char mname[] = "dbeng_ll_copy_table";
       int ret, done;

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

       if (src == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[src]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (dest == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[dest]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       logman("%s:src:tid=%d,name=%s:dest:tid=%d,name=%s", mname,
                     src->tid, src->name, dest->tid, dest->name);

       /* goto top of source table */

       if ((ret = dbeng_goto_top(src)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error going to top", ret);
          return(ret);
          }

       done = FALSE;

       /* get first source record */

       ret = dbeng_get_recd(src);

       if (ret != DBENG_OK)
          if (ret == DBENG_EOF)
             {
             done = TRUE;
             dbeng_io_code_log(mname, "source table empty", ret);
             }
          else
             {
             dbeng_io_code_log(mname, "error getting first sourc rec", ret);
             return(ret);
             }

       /* read/write loop */

       while(!done)
          {
          /* reconcile any other tid pointing to this record
             and fix-up any incorrect file positions */
    #ifdef MULTIUSER
          rov = dbeng_head;
          pos_changed = FALSE;

          while(rov != DBENG_OT_NULL)
             {
             /* if the tid's are different and the table file names are the same */

             if (src->tid != rov->tid && !strcmp(src->name, rov->name))
                {
                /* if the start record position is the same */

                if (src->orig_position == rov->orig_position)
                   {
                   logman("%s:orig_position fix-up:itid=%d,ltid=%d"
                                 ",pos=%ld", mname, src->tid, rov->tid,
                                 src->orig_position);

                   /* change link list start record position to match current
                      output position */

                   rov->orig_position = dest->current_position;
                   rov->current_position = -1L;
                   pos_changed = TRUE;
                   }
                }

             rov = rov->next;
             }
    #endif
          // write record

          ret = dbeng_write_string_recd(dest, src->rec);

          if (ret != DBENG_OK)
             {
             dbeng_io_code_log(mname, "error writing record", ret);
             return(ret);
             }

    #ifdef MULTIUSER
          /* fix-up link list current file positions, if required */
          if (pos_changed)
             {
             rov = dbeng_head;

             while(rov != DBENG_OT_NULL)
                {
                /* if the start record position has been modified,
                   fix-up the current/end of record position */

                if (rov->current_position == -1L)
                   {
                   logman("%s:current_position fix-up:itid=%d"
                                 ",ltid=%d,lopos=%ld,lcpos=%ld", mname, src->tid,
                                 rov->tid, rov->orig_position,
                                 dest->current_position);

                   /* change link list current position to match current
                      output position */

                   rov->current_position = dest->current_position;
                   }

                rov = rov->next;
                }
             }
    #endif

          /* get next source record */

          ret = dbeng_get_recd(src);

          switch(ret)
             {
             case DBENG_EOF:
                done = TRUE;
                break;

             case DBENG_OK:
                done = FALSE;
                break;

             /* we should never get here, just in case */

             default:
                dbeng_io_code_log(mname, "error getting record", ret);
                return(ret);
             };
          }

       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_delete_field(struct dbeng_table *ot, int fnum)
    {
       /* Completely remove the 'fnum' field from the current record.
          Note that all field numbers past the field being deleted
          will change. Function returns a 'dbeng' code. */

       char mname[] = "dbeng_delete_field", *new_rec;
       int num_fields;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       if (dbeng_is_active_rec(ot) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       num_fields = ll_words(ot->rec, DBENG_FM);

       if (fnum > num_fields)
          {
          dbeng_io_code_log(mname, "not exist[fnum field]", DBENG_NO_SUCH_FIELD);
          return(DBENG_NO_SUCH_FIELD);
          }

       if ((new_rec = initstring(ot->rec)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[new_rec]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       new_rec[0] = EOS;

       if (!ll_worddel(ot->rec, new_rec, fnum, DBENG_FM))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_worddel",
                            DBENG_INTERNAL_ERROR);
          free(new_rec);
          return(DBENG_INTERNAL_ERROR);
          }

       free(ot->rec);

       if ((ot->rec = initstring(new_rec)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[ot->rec]", DBENG_MEMORY_FAIL);
          free(new_rec);
          return(DBENG_MEMORY_FAIL);
          }

       free(new_rec);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_delete_subfield(struct dbeng_table *ot, int fnum, int sfnum)
    {
       /* Completely remove a subfield from the current record.
          Function returns a 'dbeng' code. */

       char mname[] = "dbeng_delete_subfield", *tfield, *nfield;
       int ret, num_fields, fsize, num_subfields;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

       if (dbeng_is_active_rec(ot) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       num_fields = ll_words(ot->rec, DBENG_FM);

       if (fnum > num_fields)
          {
          dbeng_io_code_log(mname, "not exist[fnum field]", DBENG_NO_SUCH_FIELD);
          return(DBENG_NO_SUCH_FIELD);
          }

       // obtain the field where the subfield is contained

       if ((ret = dbeng_field_size(ot, fnum, &fsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
          return(ret);
          }

       if ((tfield = (char *)malloc(fsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[tfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if (!ll_word(ot->rec, tfield, fnum, DBENG_FM))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_word",
                            DBENG_INTERNAL_ERROR);
          free(tfield);
          return(DBENG_INTERNAL_ERROR);
          }

       num_subfields = ll_words(tfield, DBENG_SFM);

       if (sfnum > num_subfields)
          {
          dbeng_io_code_log(mname, "not exist[sfnum subfield]",
                            DBENG_NO_SUCH_SUBFIELD);
          free(tfield);
          return(DBENG_NO_SUCH_SUBFIELD);
          }

       if ((nfield = initstring(tfield)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[nfield]", DBENG_MEMORY_FAIL);
          free(tfield);
          return(DBENG_MEMORY_FAIL);
          }

       nfield[0] = EOS;

       if (!ll_worddel(tfield, nfield, sfnum, DBENG_SFM))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_worddel",
                            DBENG_INTERNAL_ERROR);
          free(tfield);
          free(nfield);
          return(DBENG_INTERNAL_ERROR);
          }

       free(tfield);

       // put new field back into record

       if ((ret = dbeng_put_field(ot, nfield, fnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_field", ret);
          free(nfield);
          return(ret);
          }

       free(nfield);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_delete_subsubfield(struct dbeng_table *ot, int fnum, int sfnum,
                                 int ssfnum)
    {
       /* Completely remove a subsubfield from the current record.
          Function returns a 'dbeng' code. */

       char mname[] = "dbeng_delete_subsubfield", *tsubfield, *nsubfield;
       int ret, num_subfields, sfsize, num_subsubfields, num_fields;

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

       if (ot == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

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

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

       if (dbeng_is_active_rec(ot) == DBENG_NO_RECORD)
          {
          dbeng_io_code_log(mname, "no current record", DBENG_NO_RECORD);
          return(DBENG_NO_RECORD);
          }

       num_fields = ll_words(ot->rec, DBENG_FM);

       if (fnum > num_fields)
          {
          dbeng_io_code_log(mname, "not exist[fnum field]", DBENG_NO_SUCH_FIELD);
          return(DBENG_NO_SUCH_FIELD);
          }

       if ((ret = dbeng_nsubfields(ot, fnum, &num_subfields)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_nsubfields", ret);
          return(ret);
          }

       if (sfnum > num_subfields)
          {
          dbeng_io_code_log(mname, "not exist[sfnum subfield]",
                            DBENG_NO_SUCH_SUBFIELD);
          return(DBENG_NO_SUCH_SUBFIELD);
          }

       // obtain the subfield where the subsubfield is contained

       if ((ret = dbeng_subfield_size(ot, fnum, sfnum, &sfsize)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
          return(ret);
          }

       if ((tsubfield = (char *)malloc(sfsize + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[tsubfield]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_get_subfield(ot, tsubfield, fnum, sfnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_get_subfield", ret);
          free(tsubfield);
          return(ret);
          }

       num_subsubfields = ll_words(tsubfield, DBENG_SSFM);

       if (ssfnum > num_subsubfields)
          {
          dbeng_io_code_log(mname, "not exist[ssfnum subsubfield]",
                            DBENG_NO_SUCH_SUBSUBFIELD);
          free(tsubfield);
          return(DBENG_NO_SUCH_SUBSUBFIELD);
          }

       if ((nsubfield = initstring(tsubfield)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[nsubfield]", DBENG_MEMORY_FAIL);
          free(tsubfield);
          return(DBENG_MEMORY_FAIL);
          }

       if (!ll_worddel(tsubfield, nsubfield, ssfnum, DBENG_SSFM))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_worddel",
                            DBENG_INTERNAL_ERROR);
          free(tsubfield);
          free(nsubfield);
          return(DBENG_INTERNAL_ERROR);
          }

       free(tsubfield);

       // put new subfield back into record

       if ((ret = dbeng_put_subfield(ot, nsubfield, fnum, sfnum)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_put_subfield", ret);
          free(nsubfield);
          return(ret);
          }

       free(nsubfield);
       dbeng_io_code_log(mname, "normal exit", DBENG_OK);
       return(DBENG_OK);
    }

    int dbeng_set_record_count(struct dbeng_table *ot)
    {
       /* Check all open tables and adjust the active and deleted
          record count to the current table counts. Function
          returns a 'dbeng' code. */

       struct dbeng_table *rov = dbeng_head;

       // logman("%s:enter:tid=%d", mname, ot->tid);

       while(rov != DBENG_OT_NULL)
          {
          if (ot != rov)
             {
             if (!strcmp(ot->name, rov->name))
                {
                rov->active_record_count = ot->active_record_count;
                rov->deleted_record_count = ot->deleted_record_count;
                }
             }

          rov = rov->next;
          }

       // logman("%s:exit[%d]", mname, DBENG_OK);
       return(DBENG_OK);
    }

    void dbeng_terminate(void)
    {
       /* Close all tables and de-allocate
          all memory in use. */

       struct dbeng_table *t_cur;
       char mname[50];
       int done = FALSE;

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

       while(!done)
          {
          t_cur = dbeng_head;

          if (t_cur == DBENG_OT_NULL)
             done = TRUE;
          else
             (void)dbeng_close_table(t_cur);
          }

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

    int dbeng_is_active_rec(struct dbeng_table *ot)
    {
       /* Check for active table record. Function returns
          'DBENG_NO_RECORD' if no active table record is
          present, 'DBENG_OK' otherwise. */

       if (ot == DBENG_OT_NULL)
          return(DBENG_NO_RECORD);

       if (ot->rec == (char *)NULL)
          return(DBENG_NO_RECORD);

       if (!strlen(ot->rec))
          return(DBENG_NO_RECORD);

       return(DBENG_OK);
    }

    int dbeng_initialize(void)
    {
       /* Initialize for database use. Must be called at the beginning
          of each session. */

       dbeng_table_count = 0;
       dbeng_head = DBENG_OT_NULL;

       /* load configuration details */

       return(dbeng_config_read(DBENG_CONFIG_FILE));
    }

    int dbeng_get_tid(void)
    {
       /* Obtain the next table ID. The next tid is based on the
          number of tables currently in use. */

       int tid, done = FALSE;

       tid = dbeng_table_count;

       while(!done)
          {
          tid++;

          /* check the tid against all currently open tables */

          if (dbeng_atid_lookup(tid) == DBENG_OT_NULL)
             done = TRUE;
          }

       return(tid);
    }

    static int dbeng_table_in_use(char *fname)
    {
       /* Determine whether a table is in use by its physical file name.
          Function returns 'TRUE' if the table is in use, 'FALSE' otherwise. */

       struct dbeng_table *rov = dbeng_head;
       char mname[] = "dbeng_table_in_use";

       logman("%s:enter:fname=%s", fname);

       while(rov != DBENG_OT_NULL)
          {
          if (!strcmp(rov->name, fname))
             {
             logman("%s:normal exit[1]:found entry:table is in use", mname);
             return(TRUE);
             }

          rov = rov->next;
          }

       logman("%s:normal exit[0]:table not in use", mname);
       return(FALSE);
    }

    static int dbeng_compare_field(char *field_data, char *find_str, int cs_flag,
                                   int *result)
    {
       /* Compare the field data to the find string according to the
          case sensitivity flag (1=insensitive,0=sensitive). The
          field data and/or the find string may be empty (but not
          NULL). Function returns a 'dbeng' code with the result
          of the comparison (0=equal) in 'result'. */

       char mname[] = "dbeng_compare_field";
       int len_fd, len_fs;

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

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

       len_fd = strlen(field_data);
       len_fs = strlen(find_str);
       *result = -1;

       /* if lengths are not the same, cannot possibly be the same */

       if (len_fd != len_fs)
          {
          *result = -1;
          return(DBENG_OK);
          }

       /* if the field data is empty and the find string is empty,
          comparison is equal */

       if (!len_fd && !len_fs)
          {
          *result = 0;
          return(DBENG_OK);
          }

       if (cs_flag)
          *result = stricmp(field_data, find_str);
       else
          *result = strcmp(field_data, find_str);

       return(DBENG_OK);
    }

    struct dbeng_table *dbeng_atid_lookup(int tid)
    {
       /* Take a table tid and perform a lookup.
          Function returns a pointer to the found table
          upon success, a 'NULL' pointer otherwise. */

       struct dbeng_table *ot;
       int done = FALSE;
       int found = FALSE;

       if (dbeng_head == DBENG_OT_NULL)
          return((struct dbeng_table *)NULL);

       if (tid <= 0)
          return((struct dbeng_table *)NULL);

       ot = dbeng_head;

       while(!done)
          {
          if (ot == DBENG_OT_NULL)
             done = TRUE;
          else
             {
             if (ot->tid == tid)
                {
                done = TRUE;
                found = TRUE;
                }
             }

          if (!done && !found)
             ot = ot->next;
          }

       if (!found)
          ot = (struct dbeng_table *)NULL;

       return(ot);
    }

    void dbeng_ferror(char *mess)
    {
       /* Output a message via the log manager.
          The message will always be logged and the existing status
          of logging will be preserved. */

       int islog;
       char *apname, *err_log;

       islog = logman_is_active();

       if (!islog)
          {
          if ((apname = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
             return;

    #ifndef OS_DOS
          if (!appinit_get_name(apname))
             {
             free(apname);
             return;
             }
    #else
          apname[0] = EOS;
    #endif

          if ((err_log = (char *)malloc(DBENG_PATH_LIMIT)) == (char *)NULL)
             {
             free(apname);
             return;
             }

          if (dbeng_config_get_log(err_log) != DBENG_OK)
             {
             free(apname);
             free(err_log);
             return;
             }

          if (logman_start(err_log, apname))
             {
             free(apname);
             free(err_log);
             return;
             }

          free(apname);
          free(err_log);
          }

       logman_nf(mess);

       if (!islog)
          logman_end();
    }

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