# DB - A command line for the database server/engine 'Bbuuzzb',
# Rick Smereka, Copyright (C) 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

# Initial Tcl version, Apr/2006, Rick Smereka

# required path to library modules
set TCLDIR /home/rick/gpl/tcl

source $TCLDIR/lib/stdhead.tcl
source $TCLDIR/lib/comcode.tcl
source $TCLDIR/lib/appinit.tcl
const MULTIUSER 0
source $TCLDIR/dbeng/dbmess.tcl
source $TCLDIR/dbeng/dbiocode.tcl
source $TCLDIR/dbeng/dbeng.tcl
source $TCLDIR/dbeng/dbengcfg.tcl
source $TCLDIR/dbeng/dbengcat.tcl
source $TCLDIR/dbeng/dbfxp.tcl
source $TCLDIR/dbeng/dbengsrt.tcl
source $TCLDIR/dbeng/dbcomm.tcl

const APNAME "db"
const VERSION "1.43.01-2006.04.15"

# command list
set coms [list " " OPEN CLOSE NEXT TOP GET.REC GET.FIELD GOTO COUNT PUT.FIELD \
                   WRITE DELETE GET.DELETE.FLAG SET.DELETE.FLAG \
                   GET.NFIELDS GET.REC.SIZE GET.FIELD.SIZE \
                   GET.REC.NUM GET.POS SET.POS GET.CHANGE.REC.FLAG \
                   SET.CHANGE.REC.FLAG PACK NEW FIND FIND.FIELD FIND.PART \
                   FIND.FIELD.PART GET.REC.COUNT GET.ENF.CHANGE.REC.FLAG \
                   SET.ENF.CHANGE.REC.FLAG GET.IS.TABLE.LOCKED \
                   SET.IS.TABLE.LOCKED NEW.TABLE CONFIG.GET.TMP.PATH \
                   CONFIG.GET.LOG CONFIG.GET.SESSION CONFIG.GET.CATALOG \
                   CONFIG.GET.LOG.FLAG CONFIG.GET.SESSION.FLAG \
                   CONFIG.GET.CATALOG.FLAG CONFIG.GET.VERSION \
                   CONFIG.SET.TMP.PATH CONFIG.SET.LOG CONFIG.SET.SESSION \
                   CONFIG.SET.CATALOG CONFIG.SET.LOG.FLAG \
                   CONFIG.SET.SESSION.FLAG CONFIG.SET.CATALOG.FLAG \
                   VERSION GET.OPEN.TABLE.LIST GET.NSUBFIELDS \
                   GET.SUBFIELD.SIZE GET.SUBFIELD PUT.SUBFIELD \
                   GET.NSUBSUBFIELDS GET.SUBSUBFIELD.SIZE GET.SUBSUBFIELD \
                   PUT.SUBSUBFIELD CONFIG.GET.REPLICATE.FLAG \
                   CONFIG.SET.REPLICATE.FLAG REPLICATE.UPDATE DELETE.TABLE \
                   EXIST CLEAR.TABLE COPY.TABLE GET.CATALOG.LIST SORT \
                   GET.SORT.MEM SET.SORT.MEM GET.SORT.OPEN.BIN \
                   SET.SORT.OPEN.BIN DELETE.FIELD DELETE.SUBFIELD \
                   DELETE.SUBSUBFIELD TRANS.NUM CONNECT.NUM GET.AUTOPACK \
                   SET.AUTOPACK CONFIG.CLIENT.GET.LOG \
                   CONFIG.CLIENT.GET.LOG.FLAG CONFIG.CLIENT.SET.LOG \
                   CONFIG.CLIENT.SET.LOG.FLAG]

proc c_initialize {} {
   # Start the 'dbeng' local engine, read the config file and
   # display the result settings. Returns 1 upon success, 0
   # otherwise.
   global DBENG_OK DBENG_CONFIG_UNABLE_TO_OPEN
   set mname "c_initialize"

   set ret [db_initialize]

   # interpret code from init which may be a
   # fatal config error
   switch $ret \
      $DBENG_OK {puts "$mname:read config file successfully"} \
      $DBENG_CONFIG_UNABLE_TO_OPEN \
                {puts "$mname:config file not found,loaded defaults"} \
      default {puts "$mname:fatal config error([db_io_code_string $ret])"; \
               puts "$mname:terminating";return 0}

   db_config_get_tmp_path mes
   puts "$mname:tmp path is '$mes'"
   db_config_get_log mes
   puts "$mname:log is '$mes'"
   set ret [db_config_get_catalog mes]

   if {$ret == $DBENG_OK} {
      puts "$mname:catalog is '$mes'"
   } else {
      puts "$mname:catalog name not loaded"
   }

   db_config_get_catalog_flag flag

   if {[flag_2_logic_string $flag mes]} {
      puts "$mname:catalog is $mes"
   }

   return 1
}

proc do_comm {comm} {
   # Command dispatcher.
   global DBCOM_NOT_FOUND DBCOM_OFF DBCOM_OPEN DBCOM_CLOSE DBCOM_NEXT DBCOM_TOP
   global DBCOM_GET_REC DBCOM_GET_FIELD DBCOM_GOTO DBCOM_COUNT DBCOM_PUT_FIELD
   global DBCOM_WRITE DBCOM_DELETE DBCOM_GET_DELETE_FLAG DBCOM_SET_DELETE_FLAG
   global DBCOM_GET_NFIELDS DBCOM_GET_REC_SIZE DBCOM_GET_FIELD_SIZE DBCOM_NEW
   global DBCOM_GET_REC_NUM DBCOM_GET_POS DBCOM_SET_POS DBCOM_PACK DBCOM_FIND
   global DBCOM_GET_CHANGE_REC_FLAG DBCOM_SET_CHANGE_REC_FLAG DBCOM_FIND_FIELD
   global DBCOM_FIND_PART DBCOM_FIND_FIELD_PART DBCOM_GET_REC_COUNT DBCOM_EXIST
   global DBCOM_GET_ENF_CHANGE_REC_FLAG DBCOM_SET_ENF_CHANGE_REC_FLAG
   global DBCOM_GET_IS_TABLE_LOCKED DBCOM_SET_IS_TABLE_LOCKED DBCOM_NEW_TABLE
   global DBCOM_CONFIG_GET_TMP_PATH DBCOM_CONFIG_GET_LOG DBCOM_CONFIG_SET_LOG
   global DBCOM_CONFIG_GET_SESSION DBCOM_CONFIG_GET_CATALOG DBCOM_GET_NSUBFIELDS
   global DBCOM_CONFIG_GET_LOG_FLAG DBCOM_CONFIG_GET_SESSION_FLAG DBCOM_VERSION
   global DBCOM_CONFIG_GET_CATALOG_FLAG DBCOM_CONFIG_GET_REPLICATE_FLAG
   global DBCOM_CONFIG_GET_VERSION DBCOM_CONFIG_SET_TMP_PATH DBCOM_PUT_SUBFIELD
   global DBCOM_CONFIG_SET_SESSION DBCOM_CONFIG_SET_CATALOG DBCOM_GET_SUBFIELD
   global DBCOM_CONFIG_SET_LOG_FLAG DBCOM_CONFIG_SET_SESSION_FLAG DBCOM_SORT
   global DBCOM_CONFIG_SET_CATALOG_FLAG DBCOM_CONFIG_SET_REPLICATE_FLAG
   global DBCOM_GET_OPEN_TABLE_LIST DBCOM_GET_SUBFIELD_SIZE DBCOM_DELETE_TABLE
   global DBCOM_GET_NSUBSUBFIELDS DBCOM_GET_SUBSUBFIELD_SIZE DBCOM_CLEAR_TABLE
   global DBCOM_GET_SUBSUBFIELD DBCOM_PUT_SUBSUBFIELD DBCOM_REPLICATE_UPDATE
   global DBCOM_COPY_TABLE DBCOM_GET_CATALOG_LIST DBCOM_GET_SORT_MEM
   global DBCOM_SET_SORT_MEM DBCOM_GET_SORT_OPEN_BIN DBCOM_SET_SORT_OPEN_BIN
   global DBCOM_DELETE_FIELD DBCOM_DELETE_SUBFIELD DBCOM_DELETE_SUBSUBFIELD
   global DBCOM_TRANS_NUM DBCOM_CONNECT_NUM DBCOM_GET_AUTOPACK
   global DBCOM_SET_AUTOPACK DBCOM_CONFIG_CLIENT_GET_LOG
   global DBCOM_CONFIG_CLIENT_SET_LOG DBCOM_CONFIG_CLIENT_SET_LOG_FLAG
   set mname "do_comm"

   set len [string length $comm]

   if {!$len} {
      puts "$mname:empty command"
      return 1
   }

   set keywrd [lindex $comm 0]

   if {$keywrd == ""} {
      puts "$mname:error while retreiving keyword,line skipped"
      return 1
   }

   set token [get_token $keywrd]

   if {$token == $DBCOM_NOT_FOUND} {
      puts "$mname:unknown keyword '$keywrd'"
      return 1
   }

   # dispatch command
   switch -- $token \
      $DBCOM_OPEN {set status [c_open $comm]} \
      $DBCOM_CLOSE {set status [c_close $comm]} \
      $DBCOM_NEXT {set status [c_next $comm]} \
      $DBCOM_TOP {set status [c_top $comm]} \
      $DBCOM_GET_REC {set status [c_get_rec $comm]} \
      $DBCOM_GET_FIELD {set status [c_get_field $comm]} \
      $DBCOM_GOTO {set status [c_goto $comm]} \
      $DBCOM_COUNT {set status [c_count $comm]} \
      $DBCOM_PUT_FIELD {set status [c_put_field $comm]} \
      $DBCOM_WRITE {set status [c_write $comm]} \
      $DBCOM_DELETE {set status [c_delete $comm]} \
      $DBCOM_GET_DELETE_FLAG {set status [c_get_delete_flag $comm]} \
      $DBCOM_SET_DELETE_FLAG {set status [c_set_delete_flag $comm]} \
      $DBCOM_GET_NFIELDS {set status [c_get_nfields $comm]} \
      $DBCOM_GET_REC_SIZE {set status [c_get_rec_size $comm]} \
      $DBCOM_GET_FIELD_SIZE {set status [c_get_field_size $comm]} \
      $DBCOM_GET_REC_NUM {set status [c_get_rec_num $comm]} \
      $DBCOM_GET_POS {set status [c_get_pos $comm]} \
      $DBCOM_SET_POS {set status [c_set_pos $comm]} \
      $DBCOM_GET_CHANGE_REC_FLAG {set status [c_get_change_rec_flag $comm]} \
      $DBCOM_SET_CHANGE_REC_FLAG {set status [c_set_change_rec_flag $comm]} \
      $DBCOM_PACK {set status [c_pack $comm]} \
      $DBCOM_NEW {set status [c_new $comm]} \
      $DBCOM_FIND {set status [c_find $comm]} \
      $DBCOM_FIND_FIELD {set status [c_find_field $comm]} \
      $DBCOM_FIND_PART {set status [c_find_part $comm]} \
      $DBCOM_FIND_FIELD_PART {set status [c_find_field_part $comm]} \
      $DBCOM_GET_REC_COUNT {set status [c_get_rec_count $comm]} \
      $DBCOM_GET_ENF_CHANGE_REC_FLAG \
                           {set status [c_get_enf_change_rec_flag $comm]} \
      $DBCOM_SET_ENF_CHANGE_REC_FLAG \
                           {set status [c_set_enf_change_rec_flag $comm]} \
      $DBCOM_GET_IS_TABLE_LOCKED {set status [c_get_is_table_locked $comm]} \
      $DBCOM_SET_IS_TABLE_LOCKED {set status [c_set_is_table_locked $comm]} \
      $DBCOM_NEW_TABLE {set status [c_new_table $comm]} \
      $DBCOM_CONFIG_GET_TMP_PATH {set status [c_config_get_tmp_path]} \
      $DBCOM_CONFIG_GET_LOG {set status [c_config_get_log]} \
      $DBCOM_CONFIG_GET_SESSION {set status [c_config_get_session]} \
      $DBCOM_CONFIG_GET_CATALOG {set status [c_config_get_catalog]} \
      $DBCOM_CONFIG_GET_LOG_FLAG {set status [c_config_get_log_flag]} \
      $DBCOM_CONFIG_GET_SESSION_FLAG {set status [c_config_get_session_flag]} \
      $DBCOM_CONFIG_GET_CATALOG_FLAG {set status [c_config_get_catalog_flag]} \
      $DBCOM_CONFIG_GET_REPLICATE_FLAG \
                           {set status [c_config_get_replicate_flag]} \
      $DBCOM_CONFIG_GET_VERSION {set status [c_config_get_version]} \
      $DBCOM_CONFIG_SET_TMP_PATH {set status [c_config_set_tmp_path $comm]} \
      $DBCOM_CONFIG_SET_LOG {set status [c_config_set_log $comm]} \
      $DBCOM_CONFIG_SET_SESSION {set status [c_config_set_session $comm]} \
      $DBCOM_CONFIG_SET_CATALOG {set status [c_config_set_catalog $comm]} \
      $DBCOM_CONFIG_SET_LOG_FLAG {set status [c_config_set_log_flag $comm]} \
      $DBCOM_CONFIG_SET_SESSION_FLAG \
                           {set status [c_config_set_session_flag $comm]} \
      $DBCOM_CONFIG_SET_CATALOG_FLAG \
                           {set status [c_config_set_catalog_flag $comm]} \
      $DBCOM_CONFIG_SET_REPLICATE_FLAG \
                           {set status [c_config_set_replicate_flag $comm]} \
      $DBCOM_GET_OPEN_TABLE_LIST {set status [c_get_open_table_list $comm]} \
      $DBCOM_GET_NSUBFIELDS {set status [c_get_nsubfields $comm]} \
      $DBCOM_GET_SUBFIELD_SIZE {set status [c_get_subfield_size $comm]} \
      $DBCOM_GET_SUBFIELD {set status [c_get_subfield $comm]} \
      $DBCOM_PUT_SUBFIELD {set status [c_put_subfield $comm]} \
      $DBCOM_GET_NSUBSUBFIELDS {set status [c_get_nsubsubfields $comm]} \
      $DBCOM_GET_SUBSUBFIELD_SIZE {set status [c_get_subsubfield_size $comm]} \
      $DBCOM_GET_SUBSUBFIELD {set status [c_get_subsubfield $comm]} \
      $DBCOM_PUT_SUBSUBFIELD {set status [c_put_subsubfield $comm]} \
      $DBCOM_REPLICATE_UPDATE {set status [c_replicate_update]} \
      $DBCOM_DELETE_TABLE {set status [c_delete_table $comm]} \
      $DBCOM_EXIST {set status [c_exist $comm]} \
      $DBCOM_CLEAR_TABLE {set status [c_clear_table $comm]} \
      $DBCOM_COPY_TABLE {set status [c_copy_table $comm]} \
      $DBCOM_GET_CATALOG_LIST {set status [c_get_catalog_list $comm]} \
      $DBCOM_SORT {set status [c_sort $comm]} \
      $DBCOM_GET_SORT_MEM {set status [c_get_sort_mem $comm]} \
      $DBCOM_SET_SORT_MEM {set status [c_set_sort_mem $comm]} \
      $DBCOM_GET_SORT_OPEN_BIN {set status [c_get_sort_open_bin $comm]} \
      $DBCOM_SET_SORT_OPEN_BIN {set status [c_set_sort_open_bin $comm]} \
      $DBCOM_DELETE_FIELD {set status [c_delete_field $comm]} \
      $DBCOM_DELETE_SUBFIELD {set status [c_delete_subfield $comm]} \
      $DBCOM_DELETE_SUBSUBFIELD {set status [c_delete_subsubfield $comm]} \
      $DBCOM_TRANS_NUM {set status [c_trans_num]} \
      $DBCOM_CONNECT_NUM {set status [c_connect_num]} \
      $DBCOM_GET_AUTOPACK {set status [c_get_autopack $comm]} \
      $DBCOM_SET_AUTOPACK {set status [c_set_autopack $comm]} \
      $DBCOM_CONFIG_CLIENT_GET_LOG {set status [c_config_client_get_log]} \
      $DBCOM_CONFIG_CLIENT_SET_LOG \
                           {set status [c_config_client_set_log $comm]} \
      $DBCOM_CONFIG_CLIENT_SET_LOG_FLAG \
                           {set status [c_config_client_set_log_flag $comm]} \
      $DBCOM_VERSION {set status [c_version]} \
      $DBCOM_OFF {return 0} \
      default {puts "procedure not yet implemented";return 1}

   puts "[db_io_code_string $status]"

   # if a server failover has occured, display results of
   # new connected server
   #if {$MULTIUSER} {
   #   if {$status == $DBENG_FAILOVER} {
   #      db_get_active serv port
   #      puts "now connected to Bbuuzzb server $serv on TCP port $port\
   #            (decimal)"
   #   }
   #}

   return 1
}

proc c_open {comm} {
   # Open a table. The name (logical or physical depending on the catalog
   # flag) is expected on the command line. Returns a 'dbeng' code. Syntax:
   #
   #    open file_name
   #
   # Note that the returned tid is displayed on the screen.
   global DBENG_OK DBENG_INVALID_FILE_NAME DBENG_FILE_DATA_ERROR
   set mname "c_open"

   # must be at least two command words on command line
   if {[command_words $comm] < 2} {
      puts "$mname:missing file name"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm fname 2
   set ret [db_open $fname tid]

   if {$ret == $DBENG_OK || $ret == $DBENG_FILE_DATA_ERROR} {
      puts "tid is $tid"
   }

   if {$ret == $DBENG_FILE_DATA_ERROR} {
      puts "table is still open"
   }

   return $ret
}

proc c_close {comm} {
   # Close a table. Returns a 'dbeng' code. Syntax:
   #
   #    close tid
   global DBENG_NO_SUCH_TID
   set mname "c_close"

   # must be at least two words on command line
   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_close $tid]
}

proc c_next {comm} {
   # Get next record in a table. Returns a 'dbeng' code. Syntax:
   #
   #    next tid
   global DBENG_NO_SUCH_TID
   set mname "c_next"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_next $tid]
}

proc c_top {comm} {
   # Goto the top of the table. Note that the channel is at the
   # very top and no record is loaded. Returns a 'dbeng' code. Syntax:
   #
   #    top tid
   global DBENG_NO_SUCH_TID
   set mname "c_top"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_top $tid]
}

proc c_get_rec {comm} {
   # Get and display the current record. Returns a 'dbeng' code. Syntax:
   #
   #    get.rec tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_rec"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_rec $tid rec]

   if {$ret == $DBENG_OK} {
      set ret [db_get_nfields $tid nfields]

      if {$ret != $DBENG_OK} {
         return $ret
      }

      puts "rec=$rec,l=[string length $rec],nfields=$nfields"
   }

   return $ret
}

proc c_get_field {comm} {
   # Get and display a field in the current record. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    get.field tid field_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_field"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_field $tid $field_num field]

   if {$ret == $DBENG_OK} {
      if {$field != ""} {
         puts "field=$field,l=[string length $field]"
      } else {
         puts "field empty"
      }
   }

   return $ret
}

proc c_goto {comm} {
   # Goto a specific record. Returns a 'dbeng' code. Syntax:
   #
   #    goto tid rec_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_goto"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing record number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm rec_num 3

   if {![num $rec_num]} {
      puts "$mname:non-numeric record number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$rec_num <= 0} {
      puts "$mname:invalid record number"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_goto $tid $rec_num]
}

proc c_count {comm} {
   # Count record in a table. Returns a 'dbeng' code. Syntax:
   #
   #    count tid
   #
   # The number of active and deleted records will be displayed.
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_count"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_count $tid active_count deleted_count]

   if {$ret == $DBENG_OK} {
      puts "$mname:counted $active_count active and $deleted_count deleted\
            record(s)"
   }

   return $ret
}

proc c_put_field {comm} {
   # Put a field into the record. Returns a 'dbeng' code. Syntax:
   #
   #    put.field tid field_num field_data
   #
   # Note that if the field data contains spaces, you must quote
   # the data.
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_put_field"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing field data"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   # field number may be zero to indicate an append to the record
   if {$field_num < 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm field_data 4
   return [db_put_field $tid $field_num $field_data]
}

proc c_write {comm} {
   # Write the current record to the table. Returns a 'dbeng' code. Syntax:
   #
   #    write tid
   global DBENG_NO_SUCH_TID
   set mname "c_write"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_write $tid]
}

proc c_delete {comm} {
   # Delete the current record. Returns a 'dbeng' code. Syntax:
   #
   #    delete tid
   global DBENG_NO_SUCH_TID
   set mname "c_delete"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_delete $tid]
}

proc c_get_delete_flag {comm} {
   # Get the current tables' 'process_deleted' flag. Returns a 'dbeng' code. 
   # Flag value is displayed on the screen upon success. Syntax:
   #
   #    get.delete.flag tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_delete_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_delete_flag $tid flag]

   if {$ret == $DBENG_OK} {
      puts "$mname:'process_deleted' is $flag"
   }

   return $ret
}

proc c_set_delete_flag {comm} {
   # Set the current tables' 'process_deleted' flag. Returns a 'dbeng' code. 
   # Note that the 'flag_value' must be '0' or '1'. Syntax:
   #
   #    set.delete.flag tid flag_value
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_delete_flag"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing flag value"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm delete_flag 3

   if {![num $delete_flag]} {
      puts "$mname:non-numeric flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   if {$delete_flag < 0 || $delete_flag > 1} {
      puts "$mname:invalid flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_delete_flag $tid $delete_flag]
}

proc c_get_nfields {comm} {
   # Get the number of fields in the current record. Returns a 'dbeng' code. 
   # The number of fields is displayed on the screen upon success. Syntax:
   #
   #    get.nfields tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_nfields"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_nfields $tid nfields]

   if {$ret == $DBENG_OK} {
      puts "$mname:nfields is $nfields"
   }

   return $ret
}

proc c_get_rec_size {comm} {
   # Get the size of the current record. Returns a 'dbeng' code. 
   # THe record size is displayed on the screen upon success. Syntax:
   #
   #    get.rec.size tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_rec_size"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_rec_size $tid rec_size]

   if {$ret == $DBENG_OK} {
      puts "$mname:record size is $rec_size bytes"
   }

   return $ret
}

proc c_get_field_size {comm} {
   # Get and display a field size in the current record. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    get.field.size tid field_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_field_size"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_field_size $tid $field_num field_size]

   if {$ret == $DBENG_OK} {
      puts "$mname:field($field_num) size=$field_size"
   }

   return $ret
}

proc c_get_rec_num {comm} {
   # Get the the current record number. Returns a 'dbeng' code. 
   # The record number is displayed on the screen upon success. Syntax:
   #
   #    get.rec.num tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_rec_num"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_rec_num $tid rec_num]

   if {$ret == $DBENG_OK} {
      puts "$mname:record number is $rec_num"
   }

   return $ret
}

proc c_get_pos {comm} {
   # Get the the current channel position from the beginning. Returns a 'dbeng' 
   # code. The channel position is displayed on the screen upon success. Syntax:
   #
   #    get.pos tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_rec_num"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_pos $tid pos]

   if {$ret == $DBENG_OK} {
      puts "$mname:channel position/offset is $pos"
   }

   return $ret
}

proc c_set_pos {comm} {
   # Set the table channel position. Returns a 'dbeng' code. Syntax:
   #
   #    set.pos tid chpos
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_pos"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing channel position"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm chpos 3

   if {![num $chpos]} {
      puts "$mname:non-numeric channel position"
      return $DBENG_INVALID_FUNCTION
   }

   if {$chpos < 0} {
      puts "$mname:invalid channel position"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_pos $tid $chpos]
}

proc c_get_change_rec_flag {comm} {
   # Get the current tables' 'change_rec_flag'. Returns a 'dbeng' code. 
   # Flag value is displayed on the screen upon success. Syntax:
   #
   #    get.change.rec.flag tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_change_rec_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_change_rec_flag $tid flag]

   if {$ret == $DBENG_OK} {
      puts "$mname:flag is $flag"
   }

   return $ret
}

proc c_set_change_rec_flag {comm} {
   # Set the current tables' 'change_rec_flag'. Returns a 'dbeng' code. 
   # Note that the 'flag_value' must be '0' or '1'. Syntax:
   #
   #    set.change.rec.flag tid flag_value
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_change_rec_flag"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing flag value"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm flag 3

   if {![num $flag]} {
      puts "$mname:non-numeric flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   if {$flag < 0 || $flag > 1} {
      puts "$mname:invalid flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_change_rec_flag $tid $flag]
}

proc c_pack {comm} {
   # Pack a table. Returns a 'dbeng' code. Syntax:
   #
   #    pack tid
   global DBENG_NO_SUCH_TID
   set mname "c_pack"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_pack $tid]
}

proc c_new {comm} {
   # Declare/start a new record. Returns a 'dbeng' code. Syntax:
   #
   #    new tid
   global DBENG_NO_SUCH_TID
   set mname "c_new"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_new $tid]
}

proc c_find {comm} {
   # Find field. All whole fields will be searched from (and including)
   # the current record. String comparison is case insensitive unless
   # a 'case_flag' is supplied ('1' for insensitive, '0' for sensitive).
   # Returns a 'dbeng' code. Syntax:
   #
   #    find tid 'string' [case_flag]
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_find"

   set nwords [command_words $comm]
   set case_flag 1

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing search string"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords > 3} {
      # get the case flag
      command_word $comm case_flag 4

      if {![num $case_flag]} {
         puts "$mname:non-numeric flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }

      if {$case_flag < 0 || $case_flag > 1} {
         puts "$mname:invalid flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }
   }

   command_word $comm findstr 3
   set ret [db_find $tid $case_flag $findstr field_num]

   if {$ret == $DBENG_OK} {
      puts "$mname:match found in field $field_num"
   }

   return $ret
}

proc c_find_field {comm} {
   # Find field. A single whole field will be searched from (and including)
   # the current record. String comparison is case insensitive unless
   # a 'case_flag' is supplied ('1' for insensitive, '0' for sensitive).
   # Returns a 'dbeng' code. Syntax:
   #
   #    find.field tid field_num 'string' [case_flag]
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION DBENG_NO_SUCH_FIELD
   set mname "c_find_field"

   set nwords [command_words $comm]
   set case_flag 1

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing search string"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_NO_SUCH_FIELD
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_NO_SUCH_FIELD
   }

   if {$nwords > 4} {
      # get the case flag
      command_word $comm case_flag 5

      if {![num $case_flag]} {
         puts "$mname:non-numeric flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }

      if {$case_flag < 0 || $case_flag > 1} {
         puts "$mname:invalid flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }
   }

   command_word $comm findstr 4
   set ret [db_find_field $tid $case_flag $findstr $field_num]

   if {$ret == $DBENG_OK} {
      puts "$mname:match found"
   }

   return $ret
}

proc c_find_part {comm} {
   # Find field. Any part of all fields will be searched from (and including)
   # the current record. String comparison is case insensitive unless
   # a 'case_flag' is supplied ('1' for insensitive, '0' for sensitive).
   # Returns a 'dbeng' code. Syntax:
   #
   #    find.part tid 'string' [case_flag]
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_find_part"

   set nwords [command_words $comm]
   set case_flag 1

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing search string"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords > 3} {
      # get the case flag
      command_word $comm case_flag 4

      if {![num $case_flag]} {
         puts "$mname:non-numeric flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }

      if {$case_flag < 0 || $case_flag > 1} {
         puts "$mname:invalid flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }
   }

   command_word $comm findstr 3
   set ret [db_find_part $tid $case_flag $findstr field_num]

   if {$ret == $DBENG_OK} {
      puts "$mname:match found in field $field_num"
   }

   return $ret
}

proc c_find_field_part {comm} {
   # Find field. Any part of a single field will be searched from (and 
   # including) the current record. String comparison is case insensitive unless
   # a 'case_flag' is supplied ('1' for insensitive, '0' for sensitive).
   # Returns a 'dbeng' code. Syntax:
   #
   #    find.field.part tid field_num 'string' [case_flag]
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION DBENG_NO_SUCH_FIELD
   set mname "c_find_field_part"

   set nwords [command_words $comm]
   set case_flag 1

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing search string"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_NO_SUCH_FIELD
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_NO_SUCH_FIELD
   }

   if {$nwords > 4} {
      # get the case flag
      command_word $comm case_flag 5

      if {![num $case_flag]} {
         puts "$mname:non-numeric flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }

      if {$case_flag < 0 || $case_flag > 1} {
         puts "$mname:invalid flag value,must be '0' or '1'"
         return $DBENG_INVALID_FUNCTION
      }
   }

   command_word $comm findstr 4
   set ret [db_find_field_part $tid $case_flag $findstr $field_num]

   if {$ret == $DBENG_OK} {
      puts "$mname:match found"
   }

   return $ret
}

proc c_get_rec_count {comm} {
   # Get the current record count. Returns a 'dbeng' code. 
   # The number of records is displayed on the screen upon success. Syntax:
   #
   #    get.rec.count tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_rec_count"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_rec_count $tid active_count deleted_count]

   if {$ret == $DBENG_OK} {
      puts "$mname:table contains $active_count active and $deleted_count\
            deleted record(s)"
   }

   return $ret
}

proc c_get_enf_change_rec_flag {comm} {
   # Get the current tables' 'enf_change_rec_flag'. Returns a 'dbeng' code. 
   # Flag value is displayed on the screen upon success. Syntax:
   #
   #    get.enf.change.rec.flag tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_enf_change_rec_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_enf_change_rec_flag $tid flag]

   if {$ret == $DBENG_OK} {
      puts "$mname:flag is $flag"
   }

   return $ret
}

proc c_set_enf_change_rec_flag {comm} {
   # Set the current tables' 'enf_change_rec_flag'. Returns a 'dbeng' code. 
   # Note that the 'flag_value' must be '0' or '1'. Syntax:
   #
   #    set.enf.change.rec.flag tid flag_value
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_enf_change_rec_flag"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing flag value"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm flag 3

   if {![num $flag]} {
      puts "$mname:non-numeric flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   if {$flag < 0 || $flag > 1} {
      puts "$mname:invalid flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_enf_change_rec_flag $tid $flag]
}

proc c_get_is_table_locked {comm} {
   # Get the table 'is_table_locked' flag. Returns a 'dbeng' code. 
   # Flag value is displayed on the screen upon success. Syntax:
   #
   #    get.is.table.locked tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_is_table_locked"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_is_table_locked $tid flag]

   if {$ret == $DBENG_OK} {
      puts "$mname:flag is $flag"
   }

   return $ret
}

proc c_set_is_table_locked {comm} {
   # Set the table 'is_table_locked' flag. Returns a 'dbeng' code. 
   # Note that the 'flag_value' must be '0' or '1'. Syntax:
   #
   #    set.is_table_locked tid flag_value
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_is_table_locked"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing flag value"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm flag 3

   if {![num $flag]} {
      puts "$mname:non-numeric flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   if {$flag < 0 || $flag > 1} {
      puts "$mname:invalid flag value,must be '0' or '1'"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_is_table_locked $tid $flag]
}

proc c_new_table {comm} {
   # Create a new table. The name may be in one of two forms. If 'tid_flag'
   # is a positive number, the table will be opened. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    new.table physical_file_name tid_flag
   #    or
   #    new.table 'locgical_file_name,physical_file_name' tid_flag
   #
   # Note that the returned tid is displayed on the screen.
   global DBENG_OK DBENG_INVALID_FILE_NAME DBENG_INVALID_FUNCTION
   set mname "c_new_table"

   set nwords [command_words $comm]

   # must be at least three command words on command line
   if {$nwords < 2} {
      puts "$mname:missing file name"
      return $DBENG_INVALID_FILE_NAME
   }

   if {$nwords < 3} {
      puts "$mname:missing tid flag"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm fname 2
   command_word $comm tid 3

   if {![num $tid]} {
      puts "$mname:non-numeric tid flag"
      return $DBENG_INVALID_FUNCTION
   }

   if {$tid > 0} {
      set should_open 1
   } else {
      set should_open 0
   }

   set ret [db_new_table $fname tid]

   if {$ret == $DBENG_OK && $should_open} {
      puts "tid is $tid"
   }

   return $ret
}

proc c_config_get_tmp_path {} {
   # Display the current temporary file path. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.tmp.path
   global DBENG_OK
   set mname "c_config_get_tmp_path"

   set ret [db_config_get_tmp_path tmp_path]

   if {$ret == $DBENG_OK} {
      puts "path for tmp files is $tmp_path"
   }

   return $ret
}

proc c_config_get_log {} {
   # Display the current log name/destination. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.log
   global DBENG_OK
   set mname "c_config_get_log"

   set ret [db_config_get_log slog]

   if {$ret == $DBENG_OK} {
      puts "log is $slog"
   }

   return $ret
}

proc c_config_get_session {} {
   # Get the current path/file for the system session table.
   # Note that the session table is only used in
   # multiuser mode. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.session
   global MULTIUSER DBENG_OK
   set mname "c_config_get_session"

   if {$MULTIUSER} {
      set ret [db_config_get_session ses_table]

      if {$ret == $DBENG_OK} {
         puts "session table is $ses_table"
      }
   } else {
      puts "session table is only active in multiuser mode"
      set ret $DBENG_OK
   }

   return $ret
}

proc c_config_get_catalog {} {
   # Get the current path/file for the system catalog. Returns a 'dbeng' code.
   #
   #    config.get.catalog
   global DBENG_OK
   set mname "c_config_get_catalog"

   set ret [db_config_get_catalog cat]

   if {$ret == $DBENG_OK} {
      puts "catalog is $cat"
   }

   return $ret
}

proc c_config_get_log_flag {} {
   # Get the log flag. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.log.flag
   global DBENG_OK
   set mname "c_config_get_log_flag"

   set ret [db_config_get_log_flag flag]

   if {$ret == $DBENG_OK} {
      if {[flag_2_logic_string $flag str]} {
         puts "logging is $str"
      }
   } 

   return $ret
}

proc c_config_get_session_flag {} {
   # Get the engine session table flag. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.session.flag
   global DBENG_OK
   set mname "c_config_get_session_flag"

   set ret [db_config_get_session_flag flag]

   if {$ret == $DBENG_OK} {
      if {[flag_2_logic_string $flag str]} {
         puts "session table is $str"
      }
   }

   return $ret
}

proc c_config_get_catalog_flag {} {
   # Get the engine catalog flag. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.catalog.flag
   global DBENG_OK
   set mname "c_config_get_catalog_flag"

   set ret [db_config_get_catalog_flag flag]

   if {$ret == $DBENG_OK} {
      if {[flag_2_logic_string $flag str]} {
         puts "catalog is $str"
      }
   }

   return $ret
}

proc c_config_get_replicate_flag {} {
   # Get the engine replication flag. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.replicate.flag
   global DBENG_OK
   set mname "c_config_get_replicate_flag"

   set ret [db_config_get_replicate_flag flag]

   if {$ret == $DBENG_OK} {
      if {[flag_2_logic_string $flag str]} {
         puts "replication is $str"
      }
   }

   return $ret
}

proc c_config_get_version {} {
   # Get the current 'dbeng' version ID string. Returns a 'dbeng' code. Syntax:
   #
   #    config.get.version
   global DBENG_OK
   set mname "c_config_get_version"

   set ret [db_config_get_version ver]

   if {$ret == $DBENG_OK} {
      puts "current 'Bbuuzzb' database engine version is $ver"
   }

   return $ret
}

proc c_config_set_tmp_path {comm} {
   # Set the current path for temporary files. Returns a 'dbeng' code. Syntax:
   #
   #    config.set.tmp.path tmp_path
   global DBENG_INVALID_FILE_NAME
   set mname "c_config_set_tmp_path"

   if {[command_words $comm] < 2} {
      puts "$mname:missing path"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm path 2
   return [db_config_set_tmp_path $path]
}

proc c_config_set_log {comm} {
   # Set the current log destination. Returns a 'dbeng' code. Syntax:
   #
   #    config.set.log log_dest
   global DBENG_INVALID_FILE_NAME
   set mname "c_config_set_log"

   if {[command_words $comm] < 2} {
      puts "$mname:missing log destination"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm slog 2
   return [db_config_set_log $slog]
}

proc c_config_set_session {comm} {
   # Set the current system session table path/file. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    config.set.session session_table
   global DBENG_INVALID_FILE_NAME
   set mname "c_config_set_session"

   if {[command_words $comm] < 2} {
      puts "$mname:missing path/file"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm ses_table 2
   return [db_config_set_session $ses_table]
}

proc c_config_set_catalog {comm} {
   # Set the current catalog path/file. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    config.set.catalog catalog
   global DBENG_INVALID_FILE_NAME
   set mname "c_config_set_catalog"

   if {[command_words $comm] < 2} {
      puts "$mname:missing path/file"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm cat 2
   return [db_config_set_catalog $cat]
}

proc c_config_set_log_flag {comm} {
   # Set the current log flag. Syntax:
   #
   #    config.set.log.flag flag
   #
   # The procedure 'logic_string_2_flag' is used to validate
   # 'flag' so the value of 'flag' must be acceptable to this
   # procedure. Retuns a 'dbeng' code.
   global DBENG_INVALID_FUNCTION
   set mname "c_config_set_log_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing flag"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm flag_char 2

   # validate input flag
   if {![logic_string_2_flag $flag_char flag]} {
      puts "$mname:flag is invalid"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_config_set_log_flag $flag]
}

proc c_config_set_session_flag {comm} {
   # Set the current session flag. Syntax:
   #
   #    config.set.session.flag flag
   #
   # The procedure 'logic_string_2_flag' is used to validate
   # 'flag' so the value of 'flag' must be acceptable to this
   # procedure. Retuns a 'dbeng' code.
   global DBENG_INVALID_FUNCTION
   set mname "c_config_set_session_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing flag"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm flag_char 2

   # validate input flag
   if {![logic_string_2_flag $flag_char flag]} {
      puts "$mname:flag is invalid"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_config_set_session_flag $flag]
}

proc c_config_set_catalog_flag {comm} {
   # Set the current catalog flag. Syntax:
   #
   #    config.set.catalog.flag flag
   #
   # The procedure 'logic_string_2_flag' is used to validate
   # 'flag' so the value of 'flag' must be acceptable to this
   # procedure. Retuns a 'dbeng' code.
   global DBENG_INVALID_FUNCTION
   set mname "c_config_set_catalog_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing flag"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm flag_char 2

   # validate input flag
   if {![logic_string_2_flag $flag_char flag]} {
      puts "$mname:flag is invalid"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_config_set_catalog_flag $flag]
}

proc c_config_set_replicate_flag {comm} {
   # Set the current replication flag. Syntax:
   #
   #    config.set.replicate.flag flag
   #
   # The procedure 'logic_string_2_flag' is used to validate
   # 'flag' so the value of 'flag' must be acceptable to this
   # procedure. Retuns a 'dbeng' code.
   global DBENG_INVALID_FUNCTION
   set mname "c_config_set_replicate_flag"

   if {[words $comm] < 2} {
      puts "$mname:missing flag"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm flag_char 2

   # validate input flag
   if {![logic_string_2_flag $flag_char flag]} {
      puts "$mname:flag is invalid"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_config_set_replicate_flag $flag]
}

proc c_get_open_table_list {comm} {
   # Get and display a list of open tables. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    get.open.table.list [pat]
   #
   # Where 'pat' is an optional pattern acceptable to
   # the 'string match' builtin proc. The pattern
   # will be matched with the physical file name.
   global DBENG_OK DBENG_LIST_DELIM DBENG_INTERNAL_ERROR
   set mname "c_get_open_table_list"

   set nwords [command_words $comm]
   set pat ""

   if {$nwords > 1} {
      set is_pat 1
      command_word $comm pat 2
   } else {
      set is_pat 0
   }

   set ret [db_get_open_table_list $pat tlist]

   if {$ret != $DBENG_OK} {
      return $ret
   }

   set nwords [ll_words $tlist $DBENG_LIST_DELIM]
   
   if {!$nwords} {
      puts "$mname:list empty"
      return $ret
   }

   puts "$mname:list of open tables"
   set anentry ""

   for {set i 1} {$i <= $nwords} {incr i} {
      if {![ll_word $tlist anentry $i $DBENG_LIST_DELIM]} {
         puts "$mname:error getting list entry($i)"
         return $DBENG_INTERNAL_ERROR
      }

      puts "table($i)=$anentry"
   }

   return $DBENG_OK
}

proc c_get_nsubfields {comm} {
   # Get the number of subfields in a field. Returns a 'dbeng' code. 
   # The number of subfields is displayed on the screen upon success. Syntax:
   #
   #    get.nsubfields tid field_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_nsubfields"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_nsubfields $tid $field_num nsubfields]

   if {$ret == $DBENG_OK} {
      puts "$mname:nsubfields is $nsubfields"
   }

   return $ret
}

proc c_get_subfield_size {comm} {
   # Get the size of a subfield in a field. Returns a 'dbeng' code. 
   # The subfield size is displayed on the screen upon success. Syntax:
   #
   #    get.subfield.size tid field_num subfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_subfield_size"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_subfield_size $tid $field_num $subfield_num subfield_size]

   if {$ret == $DBENG_OK} {
      puts "$mname:subfield size is $subfield_size"
   }

   return $ret
}

proc c_get_subfield {comm} {
   # Get and display a subfield in the current record. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    get.subfield tid field_num subfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_subfield"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_subfield $tid $field_num $subfield_num subfield]

   if {$ret == $DBENG_OK} {
      if {$subfield != ""} {
         puts "subfield=$subfield,l=[string length $subfield]"
      } else {
         puts "subfield empty"
      }
   }

   return $ret
}

proc c_put_subfield {comm} {
   # Put a subfield into the field. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    put.subfield tid field_num subfield_num 'data1 data2...'
   #
   # Note that if the subfield data contains spaces, you must
   # quote the data.
   global DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_put_subfield"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 5} {
      puts "$mname:missing subfield data"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num < 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num < 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subfield_data 5
   return [db_put_subfield $tid $field_num $subfield_num $subfield_data]
}

proc c_get_nsubsubfields {comm} {
   # Get the number of subsubfields in a subfield. Returns a 'dbeng' code. 
   # The number of subsubfields is displayed on the screen upon success. Syntax:
   #
   #    get.nsubsubfields tid field_num subfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_nsubsubfields"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_nsubsubfields $tid $field_num $subfield_num nsubsubfields]

   if {$ret == $DBENG_OK} {
      puts "$mname:nsubsubfields is $nsubsubfields"
   }

   return $ret
}

proc c_get_subsubfield_size {comm} {
   # Get the size of a subsubfield in a subfield. Returns a 'dbeng' code. 
   # The subsubfield size is displayed on the screen upon success. Syntax:
   #
   #    get.subsubfield.size tid field_num subfield_num subsubfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_subsubfield_size"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 5} {
      puts "$mname:missing subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subsubfield_num 5

   if {![num $subsubfield_num]} {
      puts "$mname:non-numeric subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subsubfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_subsubfield_size $tid $field_num $subfield_num \
            $subsubfield_num subsubfield_size]

   if {$ret == $DBENG_OK} {
      puts "$mname:subsubfield size is $subsubfield_size"
   }

   return $ret
}

proc c_get_subsubfield {comm} {
   # Get and display a subsubfield in the current record. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    get.subsubfield tid field_num subfield_num subsubfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_get_subsubfield"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 5} {
      puts "$mname:missing subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm subsubfield_num 5

   if {![num $subsubfield_num]} {
      puts "$mname:non-numeric subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subsubfield_num <= 0} {
      puts "$mname:invalid subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   set ret [db_get_subsubfield $tid $field_num $subfield_num $subsubfield_num \
            subsubfield]

   if {$ret == $DBENG_OK} {
      if {$subsubfield != ""} {
         puts "subsubfield=$subsubfield,l=[string length $subsubfield]"
      } else {
         puts "empty subsubfield"
      }
   }

   return $ret
}

proc c_put_subsubfield {comm} {
   # Put a subsubfield into the field. Returns a 'dbeng' code. 
   # Syntax:
   #
   # put.subsubfield tid field_num subfield_num subsubfield_num 'data1 data2...'
   #
   # Note that if the subsubfield data contains spaces, you must
   # quote the data.
   global DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_put_subsubfield"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 5} {
      puts "$mname:missing subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 6} {
      puts "$mname:missing subsubfield data"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num < 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num < 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subsubfield_num 5

   if {![num $subsubfield_num]} {
      puts "$mname:non-numeric subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subsubfield_num < 0} {
      puts "$mname:invalid subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subsubfield_data 6
   return [db_put_subsubfield $tid $field_num $subfield_num $subsubfield_num \
                              $subsubfield_data]
}

proc c_replicate_update {} {
   # Updae all replication details. Applies to multiuser only. Returns a
   # 'dbeng' code. Syntax:
   #
   #    replicate.update
   global MULTIUSER DBENG_OK
   set mname "c_replicate_update"

   if {$MULTIUSER} {
      # set ret [db_replicate_update]
      puts "not as yet implemented"
      return $DBENG_OK
   }

   puts "replication is only active in multiuser mode"
   return $DBENG_OK
}

proc c_delete_table {comm} {
   # Delete a table. The name (logical or physical depending on the catalog
   # flag) is expected on the command line. Returns a 'dbeng' code. Syntax:
   #
   #    delete.table file_name
   global DBENG_INVALID_FILE_NAME
   set mname "c_delete_table"

   # must be at least two command words on command line
   if {[command_words $comm] < 2} {
      puts "$mname:missing file name"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm fname 2
   return [db_delete_table $fname]
}

proc c_exist {comm} {
   # Test for the existance of a table. Returns a 'dbeng' code. Syntax:
   #
   #    exist tname
   global DBENG_OK DBENG_INVALID_FILE_NAME
   set mname "c_exist"

   # must be at least two command words on command line
   if {[command_words $comm] < 2} {
      puts "$mname:missing file name"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm fname 2
   set ret [db_exist $fname exist_flag]

   if {$ret == $DBENG_OK} {
      if {$exist_flag} {
         puts "$mname:table does exist"
      } else {
         puts "$mname:table does not exist"
      }
   }

   return $ret
}

proc c_clear_table {comm} {
   # Clear a table of all records. Returns a 'dbeng' code. Syntax:
   #
   #    clear.table tname
   global DBENG_INVALID_FILE_NAME
   set mname "c_clear_table"

   # must be at least two command words on command line
   if {[command_words $comm] < 2} {
      puts "$mname:missing file name"
      return $DBENG_INVALID_FILE_NAME
   }

   command_word $comm fname 2
   return [db_clear_table $fname]
}

proc c_copy_table {comm} {
   # Copy a table from source to destination. Both tables are expected to be
   # open. Returns a 'dbeng' code. Syntax:
   #
   #    copy.table source_tid destination_tid
   global DBENG_NO_SUCH_TID
   set mname "c_copy_table"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing source table tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing destination table tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm src_tid 2

   if {![num $src_tid]} {
      puts "$mname:non-numeric source table tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$src_tid <= 0} {
      puts "$mname:invalid source table tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm dest_tid 3

   if {![num $dest_tid]} {
      puts "$mname:non-numeric destination table tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$dest_tid <= 0} {
      puts "$mname:invalid destination table tid"
      return $DBENG_NO_SUCH_TID
   }

   return [db_copy_table $src_tid $dest_tid]
}

proc c_get_catalog_list {comm} {
   # Get and display a list of catalog entries. Returns a 'dbeng' 
   # code. Syntax:
   #
   #    get.catalog.list [pat]
   #
   # Where 'pat' is an optional pattern acceptable to
   # the 'string match' builtin proc. The pattern
   # will be matched with the logical table name.
   global DBENG_OK DBENG_LIST_DELIM DBENG_INTERNAL_ERROR
   set mname "c_get_catalog_list"

   set nwords [command_words $comm]
   set pat ""

   if {$nwords > 1} {
      set is_pat 1
      command_word $comm pat 2
   } else {
      set is_pat 0
   }

   set ret [db_get_catalog_list $pat tlist]

   if {$ret != $DBENG_OK} {
      return $ret
   }

   set nwords [ll_words $tlist $DBENG_LIST_DELIM]
   
   if {!$nwords} {
      puts "$mname:list empty"
      return $ret
   }

   puts "$mname:list of catalog entries"
   set anentry ""

   for {set i 1} {$i <= $nwords} {incr i} {
      if {![ll_word $tlist anentry $i $DBENG_LIST_DELIM]} {
         puts "$mname:error getting list entry($i)"
         return $DBENG_INTERNAL_ERROR
      }

      puts "table($i)=$anentry"
   }

   return $DBENG_OK
}

proc c_sort {comm} {
   # Sort a table. Returns a 'dbeng' code. Syntax:
   #
   #    sort tid specs
   global DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_sort"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing sort specification(s)"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm specs 3
   return [db_sort $tid $specs]
}

proc c_get_sort_mem {comm} {
   # Get the the current allowable max sort memory. Returns a 'dbeng' 
   # code. The amount is displayed on the screen upon success. Syntax:
   #
   #    get.sort.mem tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_sort_mem"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_sort_max_mem $tid mem]

   if {$ret == $DBENG_OK} {
      puts "$mname:allowable sort memory is $mem bytes"
   }

   return $ret
}

proc c_set_sort_mem {comm} {
   # Set the allowable sort memory. Returns a 'dbeng' code. Syntax:
   #
   #    set.sort.mem tid mem
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_sort_mem"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing sort memory"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm mem 3

   if {![num $mem]} {
      puts "$mname:non-numeric memory amount"
      return $DBENG_INVALID_FUNCTION
   }

   if {$mem < 0} {
      puts "$mname:invalid amount of memory"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_sort_max_mem $tid $mem]
}

proc c_get_sort_open_bin {comm} {
   # Get the the current allowable sort open bin tables. Returns a 'dbeng' 
   # code. The amount is displayed on the screen upon success. Syntax:
   #
   #    get.sort.open.bin tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_sort_open_bin"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_sort_max_open_bin $tid open_bin]

   if {$ret == $DBENG_OK} {
      puts "$mname:allowable sort open bin tables is $open_bin"
   }

   return $ret
}

proc c_set_sort_open_bin {comm} {
   # Set the allowable number of sort open bin tables. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    set.sort.open.bin tid open_bin
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_sort_open_bin"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing sort open bin"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm open_bin 3

   if {![num $open_bin]} {
      puts "$mname:non-numeric number of bin tables"
      return $DBENG_INVALID_FUNCTION
   }

   if {$open_bin <= 0} {
      puts "$mname:invalid number of open bin tables"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_sort_max_open_bin $tid $open_bin]
}

proc c_delete_field {comm} {
   # Delete a field from the current record. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    delete.field tid field_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_delete_field"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_delete_field $tid $field_num]
}

proc c_delete_subfield {comm} {
   # Delete a subfield from the current record. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    delete.subfield tid field_num subfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_delete_subfield"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_delete_subfield $tid $field_num $subfield_num]
}

proc c_delete_subsubfield {comm} {
   # Delete a subsubfield from the current record. Returns a 'dbeng' code. 
   # Syntax:
   #
   #    delete.subsubfield tid field_num subfield_num subsubfield_num
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_delete_subsubfield"

   set nwords [command_words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 4} {
      puts "$mname:missing subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$nwords < 5} {
      puts "$mname:missing subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   command_word $comm field_num 3

   if {![num $field_num]} {
      puts "$mname:non-numeric field number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$field_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subfield_num 4

   if {![num $subfield_num]} {
      puts "$mname:non-numeric subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subfield_num <= 0} {
      puts "$mname:invalid subfield number"
      return $DBENG_INVALID_FUNCTION
   }

   command_word $comm subsubfield_num 5

   if {![num $subsubfield_num]} {
      puts "$mname:non-numeric subsubfield number"
      return $DBENG_INVALID_FUNCTION
   }

   if {$subsubfield_num <= 0} {
      puts "$mname:invalid field number"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_delete_subsubfield $tid $field_num $subfield_num $subsubfield_num]
}

proc c_trans_num {} {
   # Get the server current transaction count. Returns a 'dbeng' code. Syntax:
   #
   #    trans.num
   global MULTIUSER DBENG_OK DBENG_ACCESS_DENIED
   set mname "c_trans_num"

   if {$MULTIUSER} {
      set ret [db_trans_num trans_num]

      if {$ret == $DBENG_OK} {
         puts "server transaction count is $trans_num"
      }
   } else {
      puts "transaction count is active only in multiuser mode"
      set ret $DBENG_ACCESS_DENIED
   }

   return $ret
}

proc c_connect_num {} {
   # Get the server current connection count. Returns a 'dbeng' code. Syntax:
   #
   #    connect.num
   global MULTIUSER DBENG_OK DBENG_ACCESS_DENIED
   set mname "c_connect_num"

   if {$MULTIUSER} {
      set ret [db_connect_num connect_num]

      if {$ret == $DBENG_OK} {
         puts "server connection count is $connect_num"
      }
   } else {
      puts "connection count is active only in multiuser mode"
      set ret $DBENG_ACCESS_DENIED
   }

   return $ret
}

proc c_get_autopack {comm} {
   # Get the current value of 'autopack'. Returns a 'dbeng' code. 
   # The value is displayed on the screen upon success. Syntax:
   #
   #    get.autopack tid
   global DBENG_OK DBENG_NO_SUCH_TID
   set mname "c_get_autopack"

   if {[words $comm] < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   set ret [db_get_autopack $tid pval]

   if {$ret == $DBENG_OK} {
      puts "$mname:autopack threshold is $pval"
   }

   return $ret
}

proc c_set_autopack {comm} {
   # Set the current tables' autopack value. Returns a 'dbeng' code. Syntax:
   #
   #    set.autopack tid autopack_value
   global DBENG_OK DBENG_NO_SUCH_TID DBENG_INVALID_FUNCTION
   set mname "c_set_autopack"

   set nwords [words $comm]

   if {$nwords < 2} {
      puts "$mname:missing tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$nwords < 3} {
      puts "$mname:missing autopack value"
      return $DBENG_INVALID_FUNCTION
   }

   word $comm tid 2

   if {![num $tid]} {
      puts "$mname:non-numeric tid"
      return $DBENG_NO_SUCH_TID
   }

   if {$tid <= 0} {
      puts "$mname:invalid tid"
      return $DBENG_NO_SUCH_TID
   }

   word $comm pval 3

   if {![num $pval]} {
      puts "$mname:non-numeric autopack theshold"
      return $DBENG_INVALID_FUNCTION
   }

   if {$pval < 0} {
      puts "$mname:out of range autopack threshold"
      return $DBENG_INVALID_FUNCTION
   }

   return [db_set_autopack $tid $pval]
}

proc c_config_client_get_log {} {
   # Display the current client log name/destination. Multiuser only.
   # Returns a 'dbeng' code. Syntax:
   #
   #    config.client.get.log
   global MULTIUSER DBENG_OK DBENG_ACCESS_DENIED
   set mname "c_config_client_get_log"

   if {$MULTIUSER} {
      set ret [db_config_client_get_log slog]

      if {$ret == $DBENG_OK} {
         if {$slog != ""} {
            puts "client log is $slog"
         } else {
            puts "no client log currently defined"
         }
      }

      return $ret
   }

   puts "only valid in multiuser mode"
   puts "use 'config.get.log' instead"
   return $DBENG_ACCESS_DENIED
}

proc c_config_client_set_log {comm} {
   # Set the current client log destination. Multiuser only. Returns a 
   # 'dbeng' code. Syntax:
   #
   #    config.client.set.log log_dest
   global MULTIUSER DBENG_INVALID_FILE_NAME DBENG_ACCESS_DENIED
   set mname "c_config_client_set_log"

   if {$MULTIUSER} {
      if {[command_words $comm] < 2} {
         puts "$mname:missing log destination"
         return $DBENG_INVALID_FILE_NAME
      }

      command_word $comm slog 2
      return [db_config_client_set_log $slog]
   }

   puts "valid only in multiuser mode"
   puts "use 'db.config.set.log' instead"
   return $DBENG_ACCESS_DENIED
}

proc c_config_client_set_log_flag {comm} {
   # Set the current client log flag. Multiuser only. Syntax:
   #
   #    config.client.set.log.flag flag
   #
   # The procedure 'logic_string_2_flag' is used to validate
   # 'flag' so the value of 'flag' must be acceptable to this
   # procedure. Retuns a 'dbeng' code.
   global MULTIUSER DBENG_INVALID_FUNCTION DBENG_ACCESS_DENIED
   set mname "c_config_client_set_log_flag"

   if {$MULTIUSER} {
      if {[words $comm] < 2} {
         puts "$mname:missing flag"
         return $DBENG_INVALID_FUNCTION
      }

      word $comm flag_char 2

      # validate input flag
      if {![logic_string_2_flag $flag_char flag]} {
         puts "$mname:flag is invalid"
         return $DBENG_INVALID_FUNCTION
      }

      return [db_config_set_log_flag $flag]
   }

   puts "only valid in multiuser mode"
   puts "use 'config.set.log.flag' instead"
   return $DBENG_ACCESS_DENIED
}

proc c_version {} {
   # Get the current client and server (if in multiuser mode) version string. 
   # Returns a 'dbeng' code. Syntax:
   #
   #    version
   global MULTIUSER VERSION DBENG_OK
   set mname "c_version"

   puts "current client version is $VERSION"

   if {$MULTIUSER} {
      set ret [db_version ver]

      if {$ret == $DBENG_OK} {
         puts "current 'Bbuuzzb' server version is $ver"
      }
   } else {
      set ret $DBENG_OK
   }

   return $ret
}

proc get_token {keywrd} {
   # Get keyword token. Returns token number.
   global DBCOM_NOT_FOUND DBCOM_OFF coms

   set kwrd [string toupper $keywrd]

   if {$kwrd == "OFF"} {
      return $DBCOM_OFF
   }

   set token [lsearch $coms $kwrd]

   if {$token == -1} {
      set token $DBCOM_NOT_FOUND
   }

   return $token
}

proc term_app {} {
   # Prepare to terminate the application. Shutdown all IPC API's.

   db_end

   if {[logman_is_active]} {
      logman_end
   }

   appinit_remove_name
}

# Program main line.
# unbuffered stdout
fconfigure stdout -buffering none

# display logo
puts "$APNAME for $tcl_platform(platform) Version $VERSION"
puts "By Rick Smereka, Copyright (c) 2006"

# register application name with 'appinit'
if {![appinit_register_name $APNAME]} {
   puts "$APNAME:fatal error registering app name with appinit"
   exit
}

# initialize local engine if we are running locally or
# startup the 'socloc' interface and connect to a
# Bbuuzzb database server if we are running in
# client/server mode
if {![c_initialize]} {
   exit
}

set comm ""
set done 0
set ret [db_config_get_version comm]

if {$ret != $DBENG_OK} {
   puts "$APNAME:error getting 'Bbuuzzb' version,rc=[db_io_code_string $ret]"
   term_app
   exit
}

puts "based on version $comm of 'Bbuuzzb'"
puts "$APNAME comes with ABSOLUTELY NO WARRANTY"
puts "This is free software, and you are welcome to redistribute it"
puts "under certain conditions; see 'gpl.txt' for information."

# interpret loop
while {!$done} {
   puts -nonewline "$APNAME> "
   set comm [get_rec stdin]

   if {![do_comm $comm]} {
      set done 1
   }
}

term_app
puts "exit $APNAME"
exit
