/* DBENG (Bbuuzzb) configuration module. This module reads the
   settings in the DBENG config file (DBENG_CONFIG_FILE).
   This module also provides the 'get' and 'set' low level
   access functions. Used in single user database applications
   and the database server.
   Rick Smereka, Copyright (C) 1998-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

   The DBENG config file is a normal ASCII file that can
   contain the following settings:

      tmp_path path
      log file
      session_table file
      catalog file

   'tmp_path' specifies the complete path to use for
   DBENG temporary files. If not specified, the
   path used will be 'DBENG_DEFAULT_TMP_PATH'.
   'log' is the path and file name of the
   DBENG error log. If not specified, the file
   used will be 'DBENG_DEFAULT_LOG'.
   'session_table' is the path and file name of
   the system session table. If not specified,
   the file used will be 'DBENG_DEFAULT_SESSION_TABLE'.
   'catalog' is the path and file name of the
   system catalog. If not specified, the file
   used will be 'DBENG_DEFAULT_CATALOG'.
   The session table is only used with
   the c/s version. It is currently used by 'dbsrv'.
   All data on the configuration lines after the
   required parameters and all lines not containing
   a valid keyword will be ignored.

   Original QNX version, Dec/98, Rick Smereka

   Ported to 32bit Windows under CodeWarrior V4.
   Jan/99, Rick Smereka

   Moved global configuration data from 'dbeng.c' into
   this module. Also moved 'get' functions and wrote
   'set' functions. Wrote 'valid' functions.
   Ported to HP-UX under GNU C 2.8.1.
   Feb/99, Rick Smereka

   Added session table and catalog functions and put
   them on conditional compile only with the multiuser
   define. Changed 'error_log' to 'log'. Added on/off
   switches for the log, session table and catalog
   along with 'get' and 'set' functions.
   Mar/99, Rick Smereka

   Added support for obtaining the Bbuuzzb version ID.
   Apr/99, Rick Smereka

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

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

   Added include of 'dbeng.h'. Feb/2001, Rick Smereka

   Changed 'session_flag' and 'catalog_flag' default values
   to 'FALSE'. Jun/2001, Rick Smereka

   Changed function 'dbeng_config_is_systable' to detect temporary
   tables. Oct/2001, Rick Smereka

   Added member 'replicate_flag' to configuration structure. Added
   functions 'dbeng_config_get_replicate_flag' and
   'dbeng_config_set_replicate_flag'. Jan/2002, Rick Smereka

   Added function 'dbeng_config_is_session' and deleted the
   function 'dbeng_is_systable'. System tables are now
   recognized by the 'is_systable' flag in the 'dbeng_table'
   structure. Feb/2002, Rick Smereka

   Added function 'dbeng_config_is_catalog'. Jun/2002,
   Rick Smereka

   Changed function 'dbeng_config_set_session_flag' to flush
   the session table by calling 'dbeng_session_flush_all' when
   the session flag is set high. Jul/2002, Rick Smereka

   Ported to Debian Linux. Nov/2002, Rick Smereka

   Changed 'dbeng_config' structure to always contain catalog
   information and enabled config catalog functions
   in both single user apps and the database server. Nov/2003,
   Rick Smereka

   Modified 'dbeng_config_read' to automatically enable the catalog
   in single user mode if a catalog table was supplied in the config
   file. Feb/2004, Rick Smereka

   Changed all logging calls from 'sys_log' to 'logman'.
   Changed function 'dbeng_config_set_log' so that it no longer
   attempts to validate the path/file name of the log.
   Changed function 'dbeng_config_set_log_flag' so that it
   activates or de-activates logging.
   Added include of 'appinit.h'.
   Removed private function 'dbeng_config_valid_log'.
   Mar/2004, Rick Smereka */

#include "stdhead.h"
#include "dbeng.h"
#include "dbmess.h"
#include "dblocal.h"
#include "dbengcfg.h"
#include "dbengver.h"
#include "dbiocode.h"

#ifndef OS_DOS
#include "appinit.h"
#endif

/* global configuration data */

struct
   {
   char *tmp_path;
   char *log;
   int log_flag;
   char *catalog;
   int catalog_flag;
#ifdef MULTIUSER
   char *session_table;
   int session_flag;
   int replicate_flag;
   } dbeng_config = {(char *)NULL, (char *)NULL, FALSE, (char *)NULL, FALSE,
                     (char *)NULL, FALSE, FALSE};
#else
   } dbeng_config = {(char *)NULL, (char *)NULL, FALSE, (char *)NULL, FALSE};
#endif

/* private functions */

static int dbeng_config_valid_tmp_path(char *);
static int dbeng_config_valid_catalog(char *);

#ifdef MULTIUSER
static int dbeng_config_valid_session(char *);
#endif

int dbeng_config_read(char *fname)
{
   /* Read and parse the DBENG config file 'fname'.
      Upon successful completion, the global configuration
      data will be loaded from the file. Note that missing
      data will be filled in from default data as defined
      in 'dbengcfg.h'. Function returns a 'dbeng' code. */

   FILE *in;
   char *line, *word, *ucase_word;
   char mname[25];
   int nwords;
   int ret;

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

   /* attempt to open config file, if not found, attempt
      to load compiled defaults and exit */

   if ((in = fopen(fname, "r")) == (FILE *)NULL)
      {
      if ((ret = dbeng_config_set_tmp_path(DBENG_DEFAULT_TMP_PATH)) !=
           DBENG_OK)
         {
         dbeng_io_code_log(mname,
                           "exit:bad ret from dbeng_config_set_tmp_path",
                           ret);
         return(ret);
         }

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

#ifdef MULTIUSER
      if ((ret = dbeng_config_set_session(DBENG_DEFAULT_SESSION))
           != DBENG_OK)
         {
         dbeng_io_code_log(mname,
                           "exit:bad ret from dbeng_config_set_session_table",
                           ret);
         return(ret);
         }
#endif

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

      dbeng_io_code_log(mname, "exit:no config file, loaded defaults",
                        DBENG_CONFIG_UNABLE_TO_OPEN);
      return(DBENG_CONFIG_UNABLE_TO_OPEN);
      }

   /* alloc line buffer */

   if ((line = (char *)malloc(BUFSIZE)) == (char *)NULL)
      {
      fclose(in);
      dbeng_io_code_log(mname, "alloc fail[line]", DBENG_MEMORY_FAIL);
      return(DBENG_MEMORY_FAIL);
      }

   /* get first record/line */

   get_rec(in, line);

   while(!feof(in))
      {
      /* number of command words on line */

      nwords = command_words(line);

      /* must have at least two command words per record */

      if (nwords < 2)
         {
         free(line);
         fclose(in);
         dbeng_io_code_log(mname, "exit:missing parameter",
                           DBENG_CONFIG_PARAMETER_MISSING);
         return(DBENG_CONFIG_PARAMETER_MISSING);
         }

      /* create word string by dynamically copying and
         set length to zero */

      word = initstring(line);
      word[0] = EOS;

      /* get first command word on line */

      if (!command_word(line, word, 1))
         {
         free(word);
         free(line);
         fclose(in);
         dbeng_io_code_log(mname, "exit:command_word[1] fail",
                           DBENG_INTERNAL_ERROR);
         return(DBENG_INTERNAL_ERROR);
         }

      /* make another copy and convert it to upper case */

      ucase_word = initstring(word);
      ucase_word[0] = EOS;
      ucase(word, ucase_word);

      /* is it the 'tmp_path' keyword? */

      if (!strcmp(ucase_word, "TMP_PATH"))
         {
         word[0] = EOS;

         /* get second command word */

         if (!command_word(line, word, 2))
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:command_word[2,tmp_path] fail",
                           DBENG_INTERNAL_ERROR);
            return(DBENG_INTERNAL_ERROR);
            }

         /* set path */

         if ((ret = dbeng_config_set_tmp_path(word)) != DBENG_OK)
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_tmp_path",
                              ret);
            return(ret);
            }
         }

      /* is it the 'log' keyword? */

      if (!strcmp(ucase_word, "LOG"))
         {
         word[0] = EOS;

         if (!command_word(line, word, 2))
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:command_word[2,log] fail",
                              DBENG_INTERNAL_ERROR);
            return(DBENG_INTERNAL_ERROR);
            }

         /* set log name */

         if ((ret = dbeng_config_set_log(word)) != DBENG_OK)
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_log", ret);
            return(ret);
            }
         }

#ifdef MULTIUSER
      /* is it the 'session_table' keyword?
         which is valid only for the database
         server */

      if (!strcmp(ucase_word, "SESSION_TABLE"))
         {
         word[0] = EOS;

         if (!command_word(line, word, 2))
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:command_word[2,session_table] fail",
                              DBENG_INTERNAL_ERROR);
            return(DBENG_INTERNAL_ERROR);
            }

         if ((ret = dbeng_config_set_session(word)) != DBENG_OK)
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname,
                              "exit:bad ret dbeng_config_set_session",
                              ret);
            return(ret);
            }
         }
#endif

      // is it the 'catalog' keyword?

      if (!strcmp(ucase_word, "CATALOG"))
         {
         word[0] = EOS;

         if (!command_word(line, word, 2))
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:command_word[2,catalog] fail",
                              DBENG_INTERNAL_ERROR);
            return(DBENG_INTERNAL_ERROR);
            }

         /* set error log path/name */

         if ((ret = dbeng_config_set_catalog(word)) != DBENG_OK)
            {
            free(word);
            free(line);
            free(ucase_word);
            fclose(in);
            dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_catalog",
                              ret);
            return(ret);
            }
         }

      // dealloc temp vars

      free(word);
      free(ucase_word);

      // get next record

      get_rec(in, line);
      }

   fclose(in);
   free(line);

   /* handle defaults in the case of missing data */

   if (dbeng_config.tmp_path == (char *)NULL)
      if ((ret = dbeng_config_set_tmp_path(DBENG_DEFAULT_TMP_PATH)) !=
         DBENG_OK)
         {
         dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_tmp_path",
                           ret);
         return(ret);
         }

   if (dbeng_config.log == (char *)NULL)
      if ((ret = dbeng_config_set_log(DBENG_DEFAULT_LOG)) !=
         DBENG_OK)
         {
         dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_log",
                           ret);
         return(ret);
         }

#ifdef MULTIUSER
   if (dbeng_config.session_table == (char *)NULL)
      if ((ret = dbeng_config_set_session(DBENG_DEFAULT_SESSION))
         != DBENG_OK)
         {
         dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_session",
                           ret);
         return(ret);
         }

   /* we set the default if no catalog given for the database
      server, single user apps will have no default if no
      catalog is given */

   if (dbeng_config.catalog == (char *)NULL)
      if ((ret = dbeng_config_set_catalog(DBENG_DEFAULT_CATALOG))
         != DBENG_OK)
         {
         dbeng_io_code_log(mname, "exit:bad ret dbeng_config_set_catalog",
                           ret);
         return(ret);
         }
#else
   /* single user apps have the catalog enabled if it was
      supplied in the config file */

   if (dbeng_config.catalog != (char *)NULL)
      if ((ret = dbeng_config_set_catalog_flag(TRUE)) != DBENG_OK)
         {
         dbeng_io_code_log(mname, "exit:bad rc from "
                           "dbeng_config_set_catalog_flag", ret);
         return(ret);
         }
#endif

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

int dbeng_config_get_tmp_path(char *path)
{
   /* Load the global 'dbeng_config.tmp_path' into 'path' which
      must already be allocated to sufficient size.
      Function returns 'DBENG_OK' upon success,
      an engine i/o code otherwise. */

   char mname[30];

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

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

   /* if global tmp path has not been loaded, it is an error */

   if (dbeng_config.tmp_path == (char *)NULL)
      {
      dbeng_io_code_log(mname, "exit:tmp path not loaded",
                        DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   strcpy(path, dbeng_config.tmp_path);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

int dbeng_config_get_log(char *slog)
{
   /* Load the global 'dbeng_config.log' into 'slog'
      which must already be allocated to sufficient size.
      Function returns 'DBENG_OK' upon success,
      an engine i/o code otherwise. */

   char mname[25];

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

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

   /* if log name has not been loaded, it is an error */

   if (dbeng_config.log == (char *)NULL)
      {
      dbeng_io_code_log(mname, "exit:log not loaded", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   strcpy(slog, dbeng_config.log);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_is_session(char *fname)
{
   /* Determine whether 'fname' is the session table. Function
      returns 'TRUE' if the session table was found, 'FALSE'
      otherwise. */

   char mname[] = "dbeng_config_is_session", ses_table[128];
   int session_flag;

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

   logman("%s:enter:fname=%s,l=%d", mname, fname, strlen(fname));
   (void)dbeng_config_get_session_flag(&session_flag);

   /* if session table is not active, cannot be the session table
      even if the name matches */

   if (!session_flag)
      {
      logman("%s:exit[FALSE]:session table not active", mname);
      return(FALSE);
      }

   (void)dbeng_config_get_session(ses_table);

   if (!strcmp(fname, ses_table))
      {
      logman("%s:exit[TRUE]:matches session table", mname);
      return(TRUE);
      }

   logman("%s:exit[FALSE]:no match", mname);
   return(FALSE);
}
#endif

int dbeng_config_is_catalog(char *fname)
{
   /* Determine whether 'fname' is the catalog. Function
      returns 'TRUE' if the catalog was found, 'FALSE'
      otherwise. */

   char mname[] = "dbeng_config_is_catalog", cat[128];
   int catalog_flag;

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

   logman("%s:enter:fname=%s,l=%d", mname, fname, strlen(fname));
   (void)dbeng_config_get_catalog_flag(&catalog_flag);
   cat[0] = EOS;

   /* if catalog is not active, cannot be the catalog 
      even if the name matches */

   if (!catalog_flag)
      {
      logman("%s:exit[FALSE]:catalog not active", mname);
      return(FALSE);
      }

   (void)dbeng_config_get_catalog(cat);

   if (!strcmp(fname, cat))
      {
      logman("%s:exit[TRUE]:matches catalog", mname);
      return(TRUE);
      }

   logman("%s:exit[FALSE]:no match", mname);
   return(FALSE);
}

#ifdef MULTIUSER
int dbeng_config_get_session(char *ses_table)
{
   /* Load the global 'dbeng_config.session_table' into 'ses_table'
      which must already be allocated to sufficient size.
      Function returns 'DBENG_OK' upon success,
      an engine i/o code otherwise. */

   char mname[35];

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

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

   /* if session table path/name has not been loaded, it is an error */

   if (dbeng_config.session_table == (char *)NULL)
      {
      dbeng_io_code_log(mname, "exit:session table not loaded",
                        DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   strcpy(ses_table, dbeng_config.session_table);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}
#endif

int dbeng_config_get_catalog(char *cat)
{
   /* Load the global 'dbeng_config.catalog' into 'cat'
      which must already be allocated to sufficient size.
      Function returns 'DBENG_OK' upon success,
      an engine i/o code otherwise. */

   char mname[30];

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

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

   // if catalog path/name has not been loaded, catalog is not in use

   if (dbeng_config.catalog == (char *)NULL)
      {
      dbeng_io_code_log(mname, "exit:catalog not loaded",
                        DBENG_NO_CATALOG);
      return(DBENG_NO_CATALOG);
      }

   strcpy(cat, dbeng_config.catalog);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

int dbeng_config_get_log_flag(int *flag)
{
   /* Get the value of  'dbeng_config.log_flag' and load
      the value into 'flag'. Function returns 'DBENG_OK'
      upon success, an engine i/o code otherwise. */

   char mname[30];

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

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

   *flag = dbeng_config.log_flag;
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_get_session_flag(int *flag)
{
   /* Get the value of  'dbeng_config.session_flag' and load
      the value into 'flag'. Function returns 'DBENG_OK'
      upon success, an engine i/o code otherwise. */

   char mname[] = "dbeng_config_get_session_flag";

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

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

   *flag = dbeng_config.session_flag;
   logman("%s:normal exit[0],flag=%d", mname, *flag);
   return(DBENG_OK);
}
#endif

int dbeng_config_get_catalog_flag(int *flag)
{
   /* Get the value of  'dbeng_config.catalog_flag' and load
      the value into 'flag'. Function returns 'DBENG_OK'
      upon success, an engine i/o code otherwise. */

   char mname[35];

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

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

   *flag = dbeng_config.catalog_flag;
   logman("%s:normal exit[0],flag=%d", mname, *flag);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_get_replicate_flag(int *flag)
{
   /* Get the value of  'dbeng_config.replicate_flag' and load
      the value into 'flag'. Function returns 'DBENG_OK'
      upon success, an engine i/o code otherwise. */

   char mname[] = "dbeng_config_get_replicate_flag";

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

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

   *flag = dbeng_config.replicate_flag;
   logman("%s:normal exit[0],flag=%d", mname, *flag);
   return(DBENG_OK);
}
#endif

int dbeng_config_get_version(char *ver)
{
   /* Get the 'dbeng' version ID string. Function returns
      'DBENG_OK' upon success with the version ID
      loaded into 'ver' which must already be allocated
      to sufficient size. Function returns a 'dbeng'
      i/o code otherwise. */

   char mname[] = "dbeng_config_get_version";

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

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


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

int dbeng_config_set_tmp_path(char *path)
{
   /* Load 'dbeng_config.tmp_path' with data from 'path'.
      'dbeng_config.tmp_path' is dynamically allocated.
      Function returns a 'dbeng' i/o code. */

   char mname[] = "dbeng_config_set_tmp_path";
   int len;
   int ret;

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

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

   len = strlen(path);

   if (!len)
      {
      dbeng_io_code_log(mname, "exit:[path]empty", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (len >= DBENG_PATH_LIMIT)
      {
      dbeng_io_code_log(mname, "exit:[path]too large",
                        DBENG_CONFIG_TMP_PATH_SIZE);
      return(DBENG_CONFIG_TMP_PATH_SIZE);
      }

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

   if (dbeng_config.tmp_path != (char *)NULL)
      free(dbeng_config.tmp_path);

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

   strcpy(dbeng_config.tmp_path, path);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

int dbeng_config_set_log(char *slog)
{
   /* Load 'dbeng_config.log' with data from 'slog'.
      'dbeng_config.log' is dynamically allocated.
      Function returns a 'dbeng' i/o code. */

   char mname[] = "dbeng_config_set_log";
   int len;

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

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

   len = strlen(slog);

   if (!len)
      {
      dbeng_io_code_log(mname, "exit:[slog]empty", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (len >= DBENG_PATH_LIMIT)
      {
      dbeng_io_code_log(mname, "exit:[slog]too large",
                        DBENG_CONFIG_LOG_SIZE);
      return(DBENG_CONFIG_LOG_SIZE);
      }

   if (dbeng_config.log != (char *)NULL)
      free(dbeng_config.log);

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

   strcpy(dbeng_config.log, slog);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_set_session(char *ses_table)
{
   /* Load 'dbeng_config.session_table' with data from 'ses_table'.
      'dbeng_config.session_table' is dynamically allocated.
      If the file does not already exist, an attempt will be
      made to create it. Function returns a 'dbeng' i/o code. */

   char mname[35];
   int len;
   int ret;

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

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

   len = strlen(ses_table);

   if (!len)
      {
      dbeng_io_code_log(mname, "exit:[ses_table]empty",
                        DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (len >= DBENG_PATH_LIMIT)
      {
      dbeng_io_code_log(mname, "exit:[slog]too large",
                        DBENG_CONFIG_SESSION_TABLE_SIZE);
      return(DBENG_CONFIG_SESSION_TABLE_SIZE);
      }

   /* attempt to create the file if it does not exist */

   if (!exist(ses_table))
      if (!zcreate(ses_table))
         {
         dbeng_io_code_log(mname, "exit:cannot create session table",
                           DBENG_CANNOT_CREATE_TABLE);
         return(DBENG_CANNOT_CREATE_TABLE);
         }

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

   if (dbeng_config.session_table != (char *)NULL)
      free(dbeng_config.session_table);

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

   strcpy(dbeng_config.session_table, ses_table);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}
#endif

int dbeng_config_set_catalog(char *cat)
{
   /* Load 'dbeng_config.catalog' with data from 'cat'.
      'dbeng_config.catalog' is dynamically allocated.
      The catalog file must exist. Function returns a
      'dbeng' i/o code. */

   char mname[30];
   int len;
   int ret;

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

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

   len = strlen(cat);

   if (!len)
      {
      dbeng_io_code_log(mname, "exit:[cat]empty", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (len >= DBENG_PATH_LIMIT)
      {
      dbeng_io_code_log(mname, "exit:[cat]too large", 
                        DBENG_CONFIG_INVALID_CATALOG);
      return(DBENG_CONFIG_INVALID_CATALOG);
      }

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

   if (dbeng_config.catalog != (char *)NULL)
      free(dbeng_config.catalog);

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

   strcpy(dbeng_config.catalog, cat);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

int dbeng_config_set_log_flag(int flag)
{
   /* Set 'dbeng_config.log_flag' to the value of
      'flag' which must be zero or one and activate
      or de-activate logging. Function
      returns 'DBENG_OK' upon success, an
      engine i/o code otherwise. */

   char mname[] = "dbeng_config_set_log_flag";
   char *ap;

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

   if (flag != TRUE && flag != FALSE)
      {
      dbeng_io_code_log(mname, "exit:[flag]not 0 or 1", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

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

#ifndef OS_DOS
      if (!appinit_get_name(ap))
         {
         free(ap);
         dbeng_io_code_log(mname, "exit:bad rc[FALSE] from appinit_get_name",
                           DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }
#else
      ap[0] = EOS;
#endif

      if (logman_start(dbeng_config.log, ap))
         {
         free(ap);
         dbeng_io_code_log(mname, "exit:bad rc[TRUE] from logman_start",
                           DBENG_CONFIG_INVALID_LOG);
         return(DBENG_CONFIG_INVALID_LOG);
         }

      free(ap);
      }
   else
      logman_end();

   dbeng_config.log_flag = flag;
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_set_session_flag(int flag)
{
   /* Set 'dbeng_config.session_flag' to the value of
      'flag' which must be zero or one. The session
      table will be flushed when the session flag
      is set to on. Function returns 'DBENG_OK' upon 
      success, an engine i/o code otherwise. */

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

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

   if (flag != TRUE && flag != FALSE)
      {
      dbeng_io_code_log(mname, "exit:[flag]not 0 or 1", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   dbeng_config.session_flag = flag;

   if (flag)
      if ((ret = dbeng_session_flush_all()) != DBENG_OK)
         dbeng_io_code_log(mname, "bad rc from dbeng_session_flush_all", ret);

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

int dbeng_config_set_catalog_flag(int flag)
{
   /* Set 'dbeng_config.catalog_flag' to the value of
      'flag' which must be zero or one. Function
      returns 'DBENG_OK' upon success, an
      engine i/o code otherwise. */

   char mname[30];

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

   if (flag != TRUE && flag != FALSE)
      {
      dbeng_io_code_log(mname, "exit:[flag]not 0 or 1", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   // if activating catalog, make sure catalog name is loaded

   if (flag == TRUE)
      if (dbeng_config.catalog == (char *)NULL || !strlen(dbeng_config.catalog))
         {
         dbeng_io_code_log(mname, "exit:no catalog name", DBENG_NO_CATALOG);
         return(DBENG_NO_CATALOG);
         }

   dbeng_config.catalog_flag = flag;
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
int dbeng_config_set_replicate_flag(int flag)
{
   /* Set 'dbeng_config.replicate_flag' to the value of
      'flag' which must be zero or one. Catalog must also
      be active to activate replication. Function
      returns 'DBENG_OK' upon success, an
      engine i/o code otherwise. */

   char mname[] = "dbeng_config_set_replicate_flag";

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

   if (flag != TRUE && flag != FALSE)
      {
      dbeng_io_code_log(mname, "exit:[flag]not 0 or 1", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (flag && !dbeng_config.catalog_flag)
      {
      dbeng_io_code_log(mname, "exit:catalog not active", DBENG_ACCESS_DENIED);
      return(DBENG_ACCESS_DENIED);
      }

   dbeng_config.replicate_flag = flag;
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}
#endif

static int dbeng_config_valid_tmp_path(char *path)
{
   /* Validate the temporary 'path' by attempting
      to open a file for writing. Function
      returns 'DBENG_OK' upon success, an engine
      code otherwise. */

   FILE *tst;
   char mname[30];
   char rname[15], *tname;

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

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

   if (!unique(path, rname))
      {
      dbeng_io_code_log(mname, "bad ret unique",
                        DBENG_CONFIG_INVALID_TMP_PATH);
      return(DBENG_CONFIG_INVALID_TMP_PATH);
      }

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

   sprintf(tname, "%s%c%s", path, PATH_SEP, rname);

   if ((tst = fopen(tname, "w")) == (FILE *)NULL)
      {
      free(tname);
      dbeng_io_code_log(mname, "exit:cannot create tmp file",
                        DBENG_CONFIG_INVALID_TMP_PATH);
      return(DBENG_CONFIG_INVALID_TMP_PATH);
      }

   fclose(tst);
   unlink(tname);
   free(tname);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}

#ifdef MULTIUSER
static int dbeng_config_valid_session(char *ses_table)
{
   /* Validate the session table path/name by attempting
      to open it. Function returns 'DBENG_OK' upon
      success, an engine code otherwise. */

   char mname[35];
   int tid;
   int ret;

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

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

   if ((ret = db_open(ses_table, &tid)) != DBENG_OK)
      {
      dbeng_io_code_log(mname, "exit:bad ret db_open", ret);
      return(ret);
      }

   (void)db_close(tid);
   dbeng_io_code_log(mname, "normal exit", DBENG_OK);
   return(DBENG_OK);
}
#endif

static int dbeng_config_valid_catalog(char *cat)
{
   /* Validate the system catalog path/name by attempting
      to open it. Function returns 'DBENG_OK' upon
      success, an engine code otherwise. */

   char mname[30];
   int tid;
   int ret;

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

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

   if ((ret = db_open(cat, &tid)) != DBENG_OK)
      {
      dbeng_io_code_log(mname, "exit:bad ret db_open", ret);
      return(ret);
      }

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