## root/dbeng/dbengcat.c

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

## DEFINITIONS

This source file includes following definitions.

1. dbeng_catalog_new
2. dbeng_catalog_new_table
3. dbeng_catalog_delete
4. dbeng_catalog_init_table_details
5. dbeng_catalog_term_table_details
6. dbeng_catalog_table_details
7. dbeng_catalog_fname
8. dbeng_catalog_rep_list
9. dbeng_catalog_list
10. dbeng_catalog_tfind
11. dbeng_catalog_ffind
12. dbeng_catalog_open
13. dbeng_catalog_close

    /* Bbuuzzb low level system catalog management module.
       Rick Smereka, Copyright (C) 2001-2004.

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

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

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

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

       Original QNX version. Nov/2001, Rick Smereka

       Ported to Red Hat Linux, 32-bit Windows and DOS.
       Dec/2001, Rick Smereka

       Changed function 'dbeng_catalog_open' to open the catalog using
       'dbeng_open_systable'. Added a parameter to 'dbeng_catalog_new_table'
       consisting of a 'systable' flag. Feb/2002, Rick Smereka

       Changed function 'dbeng_catalog_new_table' to create the empty table
       and also create the catalog entry (if requested). Mar/2002,
       Rick Smereka

       Added function 'dbeng_catalog_list'. Aug/2002, Rick Smereka

       Ported to Debian Linux. Nov/2002, Rick Smereka

       Added functions 'dbeng_catalog_init_table_details',
       'dbeng_catalog_term_table_details' and 'dbeng_catalog_table_details'.
       Made function 'dbeng_catalog_rep_list' private.
       Changed all logging calls from 'sys_log' to 'logman'.
       Mar/2004, Rick Smereka */

    #include "stdhead.h"
    #include "dbeng.h"
    #include "dbmess.h"
    #include "dbengcfg.h"
    #include "dbengcat.h"
    #include "dbiocode.h"

    /* global catalog table pointer */

    struct dbeng_table *cat = DBENG_OT_NULL;

    /* private functions */

    static int dbeng_catalog_rep_list(char *);
    static int dbeng_catalog_tfind(char *);
    static int dbeng_catalog_ffind(char *);
    static int dbeng_catalog_open(void);
    static int dbeng_catalog_close(void);

    int dbeng_catalog_new(char *tname, char *fname)
    {
       /* Create a new entry in the system catalog. The table name (logical)
          is expected in 'tname' and the physical file name is
          expected in 'fname'. The table name must be unique. Function returns
          'DBENG_OK' if the catalog entry was successfully added,
          an engine i/o code otherwise. */

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

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

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

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

       /* physical file must exist, not */

       /* if (!exist(fname))
          {
          dbeng_io_code_log(mname, "exit:file not found", DBENG_NO_SUCH_FILE);
          return(DBENG_NO_SUCH_FILE);
          } */

       /* open catalog */

       if ((ret = dbeng_catalog_open()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_open", ret);
          return(ret);
          }

       /* attempt to locate an existing catalog entry with the same table name */

       if (dbeng_catalog_tfind(tname) == DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:catalog rec already exists",
                            DBENG_CATALOG_ENTRY_EXISTS);
          (void)dbeng_catalog_close();
          return(DBENG_CATALOG_ENTRY_EXISTS);
          }

       /* add new catalog record */

       if ((ret = dbeng_new(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_new", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_put_field(cat, tname, DBENG_CATALOG_FIELD_TNAME))
           != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_put_field[tname]", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_put_field(cat, fname, DBENG_CATALOG_FIELD_FNAME))
           != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_put_field[fname]", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_write_recd(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_write_recd", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       /* close catalog */

       if ((ret = dbeng_catalog_close()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_close", ret);
          return(ret);
          }

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

    int dbeng_catalog_new_table(char *iname, char *oname, int systable_flag)
    {
       /* Translate new table name(s) to the proper output
          name. 'iname' may contain a single name or two
          names comma delimited. A single name is assumed
          to be the table physical file name. Two names are
          assumed to be the table logical name followed by
          the physical name. Upon success, the translated
          name is placed into 'oname' which must be already
          allocated to sufficient size. Function returns a
          'dbeng' code. */

       char mname[] = "dbeng_catalog_new_table", *pname, *lname;
       int nwords, catalog_flag, ret;

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

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

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

       oname[0] = EOS;
       (void)dbeng_config_get_catalog_flag(&catalog_flag);
       nwords = ll_words(iname, ',');

       if (nwords == 1)
          {
          /* only physical file name given */

          if (systable_flag)
             /* if its a system table, use the same name */

             strcpy(oname, iname);

          if (!systable_flag)
             if (catalog_flag)
                /* if catalog is on, prefix the name with the file indicator */

                sprintf(oname, "%c%s", DBENG_CATALOG_FNAME_IND, iname);
             else
                /* if catalog is not active, use same name */

                strcpy(oname, iname);

          if (!zcreate(iname))
             {
             dbeng_io_code_log(mname, "exit:cannot create table",
                               DBENG_CANNOT_CREATE_TABLE);
             ret = DBENG_CANNOT_CREATE_TABLE;
             }
          else
             {
             if (systable_flag)
                dbeng_io_code_log(mname, "exit:created systen table", DBENG_OK);
             else
                dbeng_io_code_log(mname, "exit:created table", DBENG_OK);

             ret = DBENG_OK;
             }

          return(ret);
          }

       /* both logical and physical file names given, separate the names */

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

       if ((lname = initstring(iname)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[lname]", DBENG_MEMORY_FAIL);
          free(pname);
          return(DBENG_MEMORY_FAIL);
          }

       pname[0] = EOS;
       lname[0] = EOS;

       if (!ll_word(iname, lname, 1, ','))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_word[lname]",
                            DBENG_INTERNAL_ERROR);
          free(lname);
          free(pname);
          return(DBENG_INTERNAL_ERROR);
          }

       if (!ll_word(iname, pname, 2, ','))
          {
          dbeng_io_code_log(mname, "bad rc[FALSE] from ll_word[pname]",
                            DBENG_INTERNAL_ERROR);
          free(lname);
          free(pname);
          return(DBENG_INTERNAL_ERROR);
          }

       /* attempt to create the new table file using the physical file name */

       if (!zcreate(pname))
          {
          dbeng_io_code_log(mname, "exit:error creating empty table",
                            DBENG_CANNOT_CREATE_TABLE);
          free(lname);
          free(pname);
          return(DBENG_CANNOT_CREATE_TABLE);
          }

       /* attempt to create the catalog entry */

       if ((ret = dbeng_catalog_new(lname, pname)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_catalog_new", ret);
          free(lname);
          unlink(pname);
          free(pname);
          return(ret);
          }

       if (catalog_flag)
          /* output logical name (into 'oname') if catalog is on */

          strcpy(oname, lname);
       else
          /* output physical file name (no indicator) if catalog is off */

          strcpy(oname, pname);

       free(lname);
       free(pname);
       logman("%s:normal exit[0],oname=%s,l=%d", mname, oname,
                     strlen(oname));
       return(DBENG_OK);
    }

    int dbeng_catalog_delete(char *tname)
    {
       /* Delete the catalog entry with the table name 'tname'.
          Function returns 'DBENG_OK' upon success, an engine
          i/o code otherwise. */

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

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

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

       /* open catalog */

       if ((ret = dbeng_catalog_open()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_open", ret);
          return(ret);
          }

       /* find table name in catalog */

       if ((ret = dbeng_catalog_tfind(tname)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:catalog entry not found", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_delete_recd(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_delete_recd", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_catalog_close()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_close", ret);
          return(ret);
          }

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

    int dbeng_catalog_init_table_details(char *tname, int systable_flag,
                                         struct dbengcat_details **det)
    {
       // Prepare the catalog details structure for use.

       struct dbengcat_details *ot;
       char mname[] = "dbeng_catalog_init_table_details";
       int size;

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

       if (det == (struct dbengcat_details **)NULL)
          {
          dbeng_io_code_log(mname, "exit:null[**det]",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       size = sizeof(struct dbengcat_details);

       // allocate structure

       if ((ot = (struct dbengcat_details *)malloc(size)) ==
           (struct dbengcat_details *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[ot]", DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

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

       strcpy(ot->logical_name, tname);
       ot->systable_flag = systable_flag;
       ot->physical_name = (char *)NULL;
       ot->apack = 0;
    #ifdef MULTIUSER
       ot->rep_list = (char *)NULL;
    #endif
       *det = ot;
       return(DBENG_OK);
    }

    int dbeng_catalog_term_table_details(struct dbengcat_details **det)
    {
       // De-allocate the entire catalog details structure.

       struct dbengcat_details *ot;
       char mname[] = "dbeng_catalog_term_table_details";
       int ret;

       if (det == (struct dbengcat_details **)NULL)
          {
          dbeng_io_code_log(mname, "null[**det]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (*det == (struct dbengcat_details *)NULL)
          {
          dbeng_io_code_log(mname, "null[*det]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       ot = *det;
       free(ot->logical_name);

       if (ot->physical_name != (char *)NULL)
          free(ot->physical_name);

    #ifdef MULTIUSER
       if (ot->rep_list != (char *)NULL)
          free(ot->rep_list);
    #endif

       free(*det);
       *det = (struct dbengcat_details *)NULL;
       return(DBENG_OK);
    }

    int dbeng_catalog_table_details(struct dbengcat_details *det)
    {
       /* Find a catalog entry by its logical table name and return all
          catalog details. Function returns a 'dbeng' code. */

       char mname[] = "dbeng_catalog_table_details", tmp[25];
       int ret, catalog_flag, len;

       if (det == (struct dbengcat_details *)NULL)
          {
          dbeng_io_code_log(mname, "null or empty[det]", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

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

       logman("%s:enter", mname);
       (void)dbeng_config_get_catalog_flag(&catalog_flag);

       /* first, resolve supplied table name (could be logical or physical)
          to the physical OS path/file name */

       // if its a system table, copy name

       if (det->systable_flag)
          {
          if ((det->physical_name = (char *)malloc(strlen(det->logical_name) + 1))
              == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[det->physical_name][1]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          strcpy(det->physical_name, det->logical_name);
          dbeng_io_code_log(mname, "found system table", DBENG_OK);
          return(DBENG_OK);
          }

       // if the catalog flag is not on, copy name

       if (!catalog_flag)
          {
          if ((det->physical_name = (char *)malloc(strlen(det->logical_name) + 1))
              == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[det->physical_name][2]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          strcpy(det->physical_name, det->logical_name);
          dbeng_io_code_log(mname, "catalog is not on", DBENG_OK);
          return(DBENG_OK);
          }

       /* if the logical name starts with the file name indicator, copy
          the same name minus the indicator */

       if (det->logical_name[0] == DBENG_CATALOG_FNAME_IND)
          {
          if ((det->physical_name = (char *)malloc(strlen(det->logical_name) + 1))
              == (char *)NULL)
             {
             dbeng_io_code_log(mname, "alloc fail[det->physical_name][3]",
                               DBENG_MEMORY_FAIL);
             return(DBENG_MEMORY_FAIL);
             }

          strcpy(det->physical_name, &det->logical_name[1]);
          dbeng_io_code_log(mname, "exit:found physical file name", DBENG_OK);
          return(DBENG_OK);
          }

       /* only other possibility is a catalog table name which we
          look-up and get the physical file name */

       if (cat != DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog already open",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       /* open catalog */

       if ((ret = dbeng_catalog_open()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_open", ret);
          return(ret);
          }

       // find catalog entry by its logical name

       if ((ret = dbeng_catalog_tfind(det->logical_name)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_catalog_tfind", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       // get size of physical name

       if ((ret = dbeng_field_size(cat, DBENG_CATALOG_FIELD_FNAME, &len)) !=
           DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_field_size", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((det->physical_name = (char *)malloc(len + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[det->physical_name][4]",
                            DBENG_MEMORY_FAIL);
          (void)dbeng_catalog_close();
          return(DBENG_MEMORY_FAIL);
          }

       // load physical file name

       if ((ret = dbeng_get_field(cat, det->physical_name,
           DBENG_CATALOG_FIELD_FNAME)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_get_field", ret);
          free(det->physical_name);
          det->physical_name = (char *)NULL;
          (void)dbeng_catalog_close();
          return(ret);
          }

       // load autopack value (ok if not present)

       if ((ret = dbeng_get_field(cat, tmp, DBENG_CATALOG_FIELD_AUTOPACK)) !=
           DBENG_OK)
          if (ret == DBENG_NO_SUCH_FIELD)
             {
             det->apack = 0;
             ret = DBENG_OK;
             }
          else
             {
             dbeng_io_code_log(mname, "exit:bad rc from dbeng_get_field", ret);
             (void)dbeng_catalog_close();
             return(ret);
             }
       else
          {
          if (!qatoi(tmp, &len))
             {
             dbeng_io_code_log(mname, "exit:autopack value not a number",
                               DBENG_INVALID_FUNCTION);
             (void)dbeng_catalog_close();
             return(DBENG_INVALID_FUNCTION);
             }

          det->apack = len;
          }

       // load replication list if present (database server only)

    #ifdef MULTIUSER
       if ((det->rep_list = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[det->rep_list]",
                            DBENG_MEMORY_FAIL);
          (void)dbeng_catalog_close();
          return(DBENG_MEMORY_FAIL);
          }

       if ((ret = dbeng_catalog_rep_list(det->rep_list)) != DBENG_OK)
          {
          // ok if no replication entry

          if (ret == DBENG_NO_REPLICATION_ENTRY)
             {
             dbeng_io_code_log(mname, "normal exit:no rep entry", DBENG_OK);
             (void)dbeng_catalog_close();
             free(det->rep_list);
             det->rep_list = (char *)NULL;
             return(DBENG_OK);
             }

          dbeng_io_code_log(mname, "exit:bad rc from dbeng_catalog_rep_list", ret);
          (void)dbeng_catalog_close();
          free(det->rep_list);
          det->rep_list = (char *)NULL;
          return(ret);
          }
    #endif

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

    int dbeng_catalog_fname(char *tname, char *fname)
    {
       /* Given a table name ('tname'), locate and return the physical
          name ('fname') from the system catalog. Upon success, the
          physical file name will be loaded into 'fname' which must
          already be allocated to sufficient size. Note that this
          function assumes the catalog is currently closed. Function
          returns 'DBENG_OK' upon success, an engine i/o code
          otherwise. */

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

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

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

       if (cat != DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog already open",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if (fname == (char *)NULL)
          {
          dbeng_io_code_log(mname, "exit:'fname' null", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       fname[0] = EOS;

       /* open catalog */

       if ((ret = dbeng_catalog_open()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_open", ret);
          return(ret);
          }

       /* find table name in catalog */

       if ((ret = dbeng_catalog_tfind(tname)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad rc from dbeng_catalog_tfind", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       /* load physical file name into 'fname' */

       if ((ret = dbeng_get_field(cat, fname, DBENG_CATALOG_FIELD_FNAME)) !=
          DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_get_field", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       if ((ret = dbeng_catalog_close()) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_catalog_close", ret);
          return(ret);
          }

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

    static int dbeng_catalog_rep_list(char *rep_list)
    {
       /* Get and return the replication list for the table. Upon success, the
          replication list will be loaded into 'rep_list' which must
          already be allocated to sufficient size. The format of
          the replication list is:

             port,tname

          There may be multiple entries in the replication list.
          Each entry will be delimited by 'DBENG_LIST_DELIM'.
          Note that this function assumes the catalog is
          currently open and the catalog file pointer is pointing
          at the record in question. Function returns 'DBENG_OK' upon
          success, an engine i/o code otherwise. */

       char mname[] = "dbeng_catalog_rep_list";
       char *before, *after, *wdata, *tname, port_char[10];
       int ret, nentries, i, entry_size, nsubsubfields, port;

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

       if (rep_list == (char *)NULL)
          {
          dbeng_io_code_log(mname, "exit:'rep_list' null", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       rep_list[0] = EOS;

       /* get number of entries in replication list */

       if ((ret = dbeng_nsubfields(cat, DBENG_CATALOG_FIELD_REPLICATE, &nentries))
           != DBENG_OK)
          {
          if (ret == DBENG_NO_SUCH_FIELD)
             {
             dbeng_io_code_log(mname, "no replication entry",
                               DBENG_NO_REPLICATION_ENTRY);
             return(DBENG_NO_REPLICATION_ENTRY);
             }

          dbeng_io_code_log(mname, "bad rc from dbeng_nsubfields", ret);
          return(ret);
          }

       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);
          }

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

       /* loop through all replication entries */

       for(i = 1; i <= nentries; i++)
          {
          if ((ret = dbeng_subfield_size(cat, DBENG_CATALOG_FIELD_REPLICATE, i,
                                         &entry_size)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_subfield_size", ret);
             free(before);
             free(after);
             return(ret);
             }

          if (entry_size <= 0)
             {
             logman("%s:entry[%d]:empty catalog entry,ignoring", mname, i);
             continue;
             }

          /* must have exactly two subsubfields (port and table name) */

          if ((ret = dbeng_nsubsubfields(cat, DBENG_CATALOG_FIELD_REPLICATE, i,
                                         &nsubsubfields)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_nsubsubfields", ret);
             free(before);
             free(after);
             return(ret);
             }

          if (nsubsubfields != 2)
             {
             logman("%s:replication entry invalid", mname);
             continue;
             }

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

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

          if ((ret = dbeng_get_subsubfield(cat, port_char,
                                           DBENG_CATALOG_FIELD_REPLICATE, i,
                                           DBENG_CATALOG_SSF_PORT)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_get_subsubfield[port]",
                               ret);
             free(tname);
             free(wdata);
             free(before);
             free(after);
             return(ret);
             }

          /* port number must be valid and in range */

          if (!qatoi(port_char, &port))
             {
             logman("%s:entry[%d]:non-numeric port,ignoring", mname, i);
             free(tname);
             free(wdata);
             continue;
             }

          if (port <= 0)
             {
             logman("%s:entry[%d]:out of range[port],ignoring", mname, i);
             free(tname);
             free(wdata);
             continue;
             }

          if ((ret = dbeng_get_subsubfield(cat, tname,
                                           DBENG_CATALOG_FIELD_REPLICATE, i,
                                           DBENG_CATALOG_SSF_TNAME)) != DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_get_subsubfield[tname]",
                               ret);
             free(tname);
             free(wdata);
             free(before);
             free(after);
             return(ret);
             }

          sprintf(wdata, "%d,%s", port, tname);
          free(tname);

          if (!ll_wordput(before, after, wdata, i, 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);
             }

          free(wdata);
          strcpy(before, after);
          }

       strcpy(rep_list, after);
       free(before);
       free(after);

       if (!strlen(rep_list))
          {
          dbeng_io_code_log(mname, "exit:list is empty",
                            DBENG_NO_REPLICATION_ENTRY);
          return(DBENG_NO_REPLICATION_ENTRY);
          }

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

    int dbeng_catalog_list(char *pat, char *list_out)
    {
       /* Return a list of matching table names (logical) from
          the system catalog. 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 logical name
          followed by the file path/name (physical name). The two
          fields in the list are delimited by a comma.
          The parameter 'pat' may contain a pattern
          acceptable to the 'pmatch' function. If 'pat' is
          'NULL' or empty, all entries from the catalog
          will be returned. Function returns a 'dbeng' code. */

       char *before, *after, *wdata, *tname, *fname;
       char mname[] = "dbeng_catalog_list";
       long rec_cnt, dummy;
       int cnt, is_pat, ret, process;

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

       if (list_out == 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;
       list_out[0] = EOS;

       /* open catalog */

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

       /* get the number of records */

       if ((ret = dbeng_rec_count(cat, &rec_cnt, &dummy)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "bad rc from dbeng_count_rec(cat)", ret);
          (void)dbeng_catalog_close();
          return(ret);
          }

       /* it is not an error if there are no records */

       if (rec_cnt == 0L)
          {
          dbeng_io_code_log(mname, "list is empty", DBENG_OK);
          (void)dbeng_catalog_close();
          return(DBENG_OK);
          }

       if ((before = (char *)malloc(DBENG_MAXRECFIELD)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "alloc fail[before]", DBENG_MEMORY_FAIL);
          (void)dbeng_catalog_close();
          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);
          (void)dbeng_catalog_close();
          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);
          (void)dbeng_catalog_close();
          return(DBENG_MEMORY_FAIL);
          }

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

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

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

       if ((ret = dbeng_goto_top(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error going top of catalog", ret);
          free(before);
          free(after);
          free(wdata);
          free(tname);
          free(fname);
          (void)dbeng_catalog_close();
          return(ret);
          }

       /* get first record */

       if ((ret = dbeng_get_recd(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "error getting first record", ret);
          free(before);
          free(after);
          free(wdata);
          free(tname);
          free(fname);
          (void)dbeng_catalog_close();
          return(ret);
          }

       while(ret == DBENG_OK)
          {
          if ((ret = dbeng_get_field(cat, tname, DBENG_CATALOG_FIELD_TNAME)) !=
              DBENG_OK)
             {
             dbeng_io_code_log(mname, "bad rc from dbeng_get_field(tname)", ret);
             free(before);
             free(after);
             free(wdata);
             free(tname);
             free(fname);
             (void)dbeng_catalog_close();
             return(ret);
             }

          process = TRUE;

          /* match to pattern (if supplied) */

          if (is_pat)
             if (!pmatch(pat, tname))
                process = FALSE;

          if (process)
             {
             if ((ret = dbeng_get_field(cat, fname, DBENG_CATALOG_FIELD_FNAME)) !=
                 DBENG_OK)
                {
                dbeng_io_code_log(mname, "bad rc from dbeng_get_field(fname)", ret);
                free(before);
                free(after);
                free(wdata);
                free(tname);
                free(fname);
                (void)dbeng_catalog_close();
                return(ret);
                }

             sprintf(wdata, "%s,%s", tname, fname);

             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);
                free(tname);
                free(fname);
                (void)dbeng_catalog_close();
                return(DBENG_INTERNAL_ERROR);
                }

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

          /* get next record */

          ret = dbeng_get_recd(cat);
          }

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

    /* private functions */

    static int dbeng_catalog_tfind(char *tname)
    {
       /* Locate the table by table name ('tname') within the
          system catalog. The system catalog table is assumed
          to be already open. It is an error if the catalog
          is found to be closed. Function returns 'DBENG_OK' if
          the table name was found, an engine i/o code
          otherwise. */

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

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

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

       if (cat == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog not open", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if ((ret = dbeng_goto_top(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_goto_top", ret);
          return(ret);
          }

       if ((ret = dbeng_find_field(cat, 0, tname, DBENG_CATALOG_FIELD_TNAME))
            == DBENG_EOF)
          ret = DBENG_NO_CATALOG_ENTRY;

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

    static int dbeng_catalog_ffind(char *fname)
    {
       /* Locate the table by file name ('fname') within the
          system catalog. The system catalog table is assumed
          to be already open. It is an error if the catalog
          is found to be closed. Function returns 'DBENG_OK' if
          the file name was found, an engine i/o code
          otherwise. */

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

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

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

       if (cat == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog not open", DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if ((ret = dbeng_goto_top(cat)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:bad ret from dbeng_goto_top", ret);
          return(ret);
          }

       if ((ret = dbeng_find_field(cat, 0, fname, DBENG_CATALOG_FIELD_FNAME))
            == DBENG_EOF)
          ret = DBENG_NO_CATALOG_ENTRY;

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

    static int dbeng_catalog_open(void)
    {
       /* Open the catalog table and load the 'cat' pointer. Function
          returns 'DBENG_OK' upon success, an engine i/o code
          otherwise. */

       char mname[] = "dbeng_catalog_open";
       char *cat_table;
       int ret;
       int tid;

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

       if (cat != DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog already open",
                            DBENG_INVALID_FUNCTION);
          return(DBENG_INVALID_FUNCTION);
          }

       if ((cat_table = (char *)malloc(DBENG_PATH_LIMIT + 1)) == (char *)NULL)
          {
          dbeng_io_code_log(mname, "exit:alloc fail[cat_table]",
                            DBENG_MEMORY_FAIL);
          return(DBENG_MEMORY_FAIL);
          }

       /* get catalog table name */

       if ((ret = dbeng_config_get_catalog(cat_table)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:error getting catalog name", ret);
          free(cat_table);
          return(ret);
          }

       if ((ret = dbeng_open_systable(cat_table, &tid)) != DBENG_OK)
          {
          dbeng_io_code_log(mname, "exit:error opening catalog", ret);
          free(cat_table);
          return(ret);
          }

       free(cat_table);

       /* load table pointer from tid */

       if ((cat = dbeng_atid_lookup(tid)) == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:dbeng_atid_lookup error on catalog table",
                            DBENG_UNABLE_TO_OPEN);
          return(DBENG_UNABLE_TO_OPEN);
          }

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

    static int dbeng_catalog_close(void)
    {
       /* Close the system catalog table. Function returns 'DBENG_OK'
          upon success, an engine i/o code otherwise. */

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

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

       if (cat == DBENG_OT_NULL)
          {
          dbeng_io_code_log(mname, "exit:catalog table not open", DBENG_NO_SUCH_FILE);
          return(DBENG_NO_SUCH_FILE);
          }

       ret = dbeng_close_table(cat);
       cat = DBENG_OT_NULL;
       dbeng_io_code_log(mname, "normal exit", ret);
       return(ret);
    }

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