text: /*-
   * Copyright (c) 1999, 2018 Oracle and/or its affiliates.  All rights reserved.
   *
   * See the file LICENSE for license information.
   *
   * $Id$
   */
  
  #ifndef _DB_TCL_DB_H_
  #define	_DB_TCL_DB_H_
  
  #if defined(__cplusplus)
  extern "C" {
  #endif
  
  #define	MSG_SIZE 100		/* Message size */
  
  enum INFOTYPE {
  	I_AUX, I_DB, I_DBC, I_DBSTREAM, I_ENV,
  	I_LOCK, I_LOGC, I_MP, I_NDBM, I_PG, I_SEQ, I_TXN};
  
  #define	MAX_ID		8	/* Maximum number of sub-id's we need */
  #define	DBTCL_PREP	64	/* Size of txn_recover preplist */
  
  #define	DBTCL_DBM	1
  #define	DBTCL_NDBM	2
  
  #define	DBTCL_GETCLOCK			0
  #define	DBTCL_GETINQUEUE_MAX		1
  #define	DBTCL_GETINQUEUE_REDZONE	2
  #define	DBTCL_GETLIMIT			3
  #define	DBTCL_GETREQ			4
  
  #define	DBTCL_MUT_ALIGN	0
  #define	DBTCL_MUT_INCR	1
  #define	DBTCL_MUT_INIT	2
  #define	DBTCL_MUT_MAX	3
  #define	DBTCL_MUT_TAS	4
  
  /*
   * Data structure to record information about events that have occurred.  Tcl
   * command "env event_info" can retrieve all the information except the number
   * of times, and "env event_count" can retrieve the number of times a specific
   * event is fired.  We added "env event_count" instead of merging the times
   * information into "env event_info" to avoid breaking the existing tests.
   * Tcl command "env event_info -clear" can be used to reset the info.
   *
   * Besides the bit flag that records the fact that an event type occurred, some
   * event types have associated "info" and we record that here too.  When new
   * event types are invented that have associated info, we should add a field
   * here to record that info as well, so that it can be returned to the script
   * with the "env event_info" results.
   */
  typedef struct dbtcl_event_info {
  	u_int32_t	  events;	/* Bit flag on for each event fired. */
  	int		  panic_error;
  	int		  newmaster_eid;
  	int		  added_eid;
  	int		  removed_eid;
  	pid_t		  attached_process;
  	int		  connected_eid;
  	DB_REPMGR_CONN_ERR conn_broken_info;
  	DB_REPMGR_CONN_ERR conn_failed_try_info;
  	DB_LSN		  sync_point;
  	size_t		  count[32]; /* The number of times for each event. */
  } DBTCL_EVENT_INFO;
  
  /*
   * Why use a home grown package over the Tcl_Hash functions?
   *
   * We could have implemented the stuff below without maintaining our
   * own list manipulation, efficiently hashing it with the available
   * Tcl functions (Tcl_CreateHashEntry, Tcl_GetHashValue, etc).  I chose
   * not to do so for these reasons:
   *
   * We still need the information below.  Using the hashing only removes
   * us from needing the next/prev pointers.  We still need the structure
   * itself because we need more than one value associated with a widget.
   * We need to keep track of parent pointers for sub-widgets (like cursors)
   * so we can correctly close.  We need to keep track of individual widget's
   * id counters for any sub-widgets they may have.  We need to be able to
   * associate the name/client data outside the scope of the widget.
   *
   * So, is it better to use the hashing rather than
   * the linear list we have now?  I decided against it for the simple reason
   * that to access the structure would require two calls.  The first is
   * Tcl_FindHashEntry(table, key) and then, once we have the entry, we'd
   * have to do Tcl_GetHashValue(entry) to get the pointer of the structure.
   *
   * I believe the number of simultaneous DB widgets in existence at one time
   * is not going to be that large (more than several dozen) such that
   * linearly searching the list is not going to impact performance in a
   * noticeable way.  Should performance be impacted due to the size of the
   * info list, then perhaps it is time to revisit this decision.
   */
  typedef struct dbtcl_info {
  	LIST_ENTRY(dbtcl_info) entries;
  	Tcl_Interp *i_interp;
  	char *i_name;
  	enum INFOTYPE i_type;
  	union infop {
  		DB *dbp;
  		DBC *dbcp;
  		DB_ENV *envp;
  		DB_LOCK *lock;
  		DB_LOGC *logc;
  		DB_MPOOLFILE *mp;
  		DB_STREAM *dbsp;
  		DB_TXN *txnp;
  		void *anyp;
  	} un;
  	union data {
  		int anydata;
  		db_pgno_t pgno;		      /* For I_MP. */
  		u_int32_t lockid;	      /* For I_LOCK. */
  		DBTCL_EVENT_INFO *event_info; /* For I_ENV. */
  		DB_TXN_TOKEN *commit_token;   /* For I_TXN. */
  	} und;
  	union data2 {
  		int anydata;
  		int pagesz;	    /* For I_MP. */
  		DB_COMPACT *c_data; /* For I_DB. */
  		db_mutex_t mutex;   /* Protects event_info (I_ENV). */
  	} und2;
  	DBT i_lockobj;
  	FILE *i_err;
  	char *i_errpfx;
  	FILE *i_msg;
  	char *i_msgpfx;
  
  	/* Callbacks--Tcl_Objs containing proc names */
  	Tcl_Obj *i_compare;
  	Tcl_Obj *i_dupcompare;
  	Tcl_Obj *i_foreign_call;
  	Tcl_Obj *i_hashproc;
  	Tcl_Obj *i_isalive;
  	Tcl_Obj *i_part_callback;
  	Tcl_Obj *i_rep_send;
  	Tcl_Obj *i_rep_view;
  	Tcl_Obj *i_second_call;
  	Tcl_Obj *i_slice_callback;
  
  	/* Environment ID for the i_rep_send callback. */
  	Tcl_Obj *i_rep_eid;
  
  	struct dbtcl_info *i_parent;
  	int	i_otherid[MAX_ID];
  
  	/* Heap dbs have an associated recno db, and secondary db. */
  	DB *hrdbp;
  	DB *hsdbp;
  } DBTCL_INFO;
  
  #define	i_anyp un.anyp
  #define	i_dbp un.dbp
  #define	i_dbcp un.dbcp
  #define	i_dbsp un.dbsp
  #define	i_envp un.envp
  #define	i_lock un.lock
  #define	i_logc un.logc
  #define	i_mp un.mp
  #define	i_pagep un.anyp
  #define	i_txnp un.txnp
  
  #define	i_data und.anydata
  #define	i_pgno und.pgno
  #define	i_locker und.lockid
  #define	i_event_info und.event_info
  #define	i_commit_token und.commit_token
  #define	i_data2 und2.anydata
  #define	i_pgsz und2.pagesz
  #define	i_cdata und2.c_data
  #define	i_mutex und2.mutex
  
  #define	i_envtxnid i_otherid[0]
  #define	i_envmpid i_otherid[1]
  #define	i_envlockid i_otherid[2]
  #define	i_envlogcid i_otherid[3]
  #define	i_envsliceid i_otherid[4]
  
  #define	i_mppgid  i_otherid[0]
  
  #define	i_dbdbcid i_otherid[0]
  #define	i_dbsliceid i_otherid[1]
  
  #define	i_dbcdbsid i_otherid[0]
  
  extern int __debug_on, __debug_print, __debug_stop, __debug_test;
  
  typedef struct dbtcl_global {
  	LIST_HEAD(infohead, dbtcl_info) g_infohead;
  } DBTCL_GLOBAL;
  #define	__db_infohead __dbtcl_global.g_infohead
  
  extern DBTCL_GLOBAL __dbtcl_global;
  
  /*
   * Tcl_NewStringObj takes an "int" length argument, when the typical use is to
   * call it with a size_t length (for example, returned by strlen).  Tcl is in
   * the wrong, but that doesn't help us much -- cast the argument.
   */
  #define	NewStringObj(a, b)						\
  	Tcl_NewStringObj((a), (int)(b))
  
  #define	NAME_TO_DB(name)	(DB *)_NameToPtr((name))
  #define	NAME_TO_DBC(name)	(DBC *)_NameToPtr((name))
  #define	NAME_TO_ENV(name)	(DB_ENV *)_NameToPtr((name))
  #define	NAME_TO_LOCK(name)	(DB_LOCK *)_NameToPtr((name))
  #define	NAME_TO_MP(name)	(DB_MPOOLFILE *)_NameToPtr((name))
  #define	NAME_TO_TXN(name)	(DB_TXN *)_NameToPtr((name))
  #define	NAME_TO_SEQUENCE(name)	(DB_SEQUENCE *)_NameToPtr((name))
  
  /*
   * MAKE_STAT_LIST appends a {name value} pair to a result list that MUST be
   * called 'res' that is a Tcl_Obj * in the local function.  This macro also
   * assumes a label "error" to go to in the event of a Tcl error.  For stat
   * functions this will typically go before the "free" function to free the
   * stat structure returned by DB.
   */
  #ifdef HAVE_STATISTICS
  #define	MAKE_STAT_LIST(s, v) do {					\
  	result = _SetListElemInt(interp, res, (s), (long)(v));		\
  	if (result != TCL_OK)						\
  		goto error;						\
  } while (0)
  
  #define	MAKE_WSTAT_LIST(s, v) do {					\
  	result = _SetListElemWideInt(interp, res, (s), (int64_t)(v));	\
  	if (result != TCL_OK)						\
  		goto error;						\
  } while (0)
  #else
  /* These do-nothing versions streamline the code & reduce warning messages. */
  #define	MAKE_STAT_LIST(s, v)	if (0) goto error
  #define	MAKE_WSTAT_LIST(s, v)	if (0) goto error
  #endif
  
  /*
   * MAKE_STAT_LSN appends a {name {LSNfile LSNoffset}} pair to a result list
   * that MUST be called 'res' that is a Tcl_Obj * in the local
   * function.  This macro also assumes a label "error" to go to
   * in the event of a Tcl error.  For stat functions this will
   * typically go before the "free" function to free the stat structure
   * returned by DB.
   */
  #define	MAKE_STAT_LSN(s, lsn) do {					\
  	myobjc = 2;							\
  	myobjv[0] = Tcl_NewLongObj((long)(lsn)->file);			\
  	myobjv[1] = Tcl_NewLongObj((long)(lsn)->offset);		\
  	lsnlist = Tcl_NewListObj(myobjc, myobjv);			\
  	myobjc = 2;							\
  	myobjv[0] = Tcl_NewStringObj((s), (int)strlen(s));		\
  	myobjv[1] = lsnlist;						\
  	thislist = Tcl_NewListObj(myobjc, myobjv);			\
  	result = Tcl_ListObjAppendElement(interp, res, thislist);	\
  	if (result != TCL_OK)						\
  		goto error;						\
  } while (0)
  
  /*
   * MAKE_STAT_STRLIST appends a {name string} pair to a result list
   * that MUST be called 'res' that is a Tcl_Obj * in the local
   * function.  This macro also assumes a label "error" to go to
   * in the event of a Tcl error.  For stat functions this will
   * typically go before the "free" function to free the stat structure
   * returned by DB.
   */
  #define	MAKE_STAT_STRLIST(s,s1) do {					\
  	result = _SetListElem(interp, res, (s), (u_int32_t)strlen(s),	\
  	    (s1), (u_int32_t)strlen(s1));				\
  	if (result != TCL_OK)						\
  		goto error;						\
  } while (0)
  
  /*
   * MAKE_SITE_LIST appends a
   * {eid host port status peer view unelect maxackfile maxackoffset}
   * tuple to a result list that MUST be called 'res' that is a Tcl_Obj * in
   * the local function.  This macro also assumes a label "error" to go to in
   * the event of a Tcl error.
   */
  #define	MAKE_SITE_LIST(e, h, p, s, pr, vw, el, maf, mao) do {		\
  	myobjc = 9;							\
  	myobjv[0] = Tcl_NewIntObj(e);					\
  	myobjv[1] = Tcl_NewStringObj((h), (int)strlen(h));		\
  	myobjv[2] = Tcl_NewIntObj((int)p);				\
  	myobjv[3] = Tcl_NewStringObj((s), (int)strlen(s));		\
  	myobjv[4] = Tcl_NewStringObj((pr), (int)strlen(pr));		\
  	myobjv[5] = Tcl_NewStringObj((vw), (int)strlen(vw));		\
  	myobjv[6] = Tcl_NewStringObj((el), (int)strlen(el));		\
  	myobjv[7] = Tcl_NewLongObj((long)(maf));			\
  	myobjv[8] = Tcl_NewLongObj((long)(mao));			\
  	thislist = Tcl_NewListObj(myobjc, myobjv);			\
  	result = Tcl_ListObjAppendElement(interp, res, thislist);	\
  	if (result != TCL_OK)						\
  		goto error;						\
  } while (0)
  
  /*
   * FLAG_CHECK checks that the given flag is not set yet.
   * If it is, it sets up an error message.
   */
  #define	FLAG_CHECK(flag) do {						\
  	if ((flag) != 0) {						\
  		Tcl_SetResult(interp,					\
  		    " Only 1 policy can be specified.\n",		\
  		    TCL_STATIC);					\
  		result = TCL_ERROR;					\
  		break;							\
  	}								\
  } while (0)
  
  /*
   * FLAG_CHECK2 checks that the given flag is not set yet or is
   * only set to the given allowed value.
   * If it is, it sets up an error message.
   */
  #define	FLAG_CHECK2(flag, val) do {					\
  	if (((flag) & ~(val)) != 0) {					\
  		Tcl_SetResult(interp,					\
  		    " Only 1 policy can be specified.\n",		\
  		    TCL_STATIC);					\
  		result = TCL_ERROR;					\
  		break;							\
  	}								\
  } while (0)
  
  /*
   * IS_HELP checks whether the arg we bombed on is -?, which is a help option.
   * If it is, we return TCL_OK (but leave the result set to whatever
   * Tcl_GetIndexFromObj says, which lists all the valid options.  Otherwise
   * return TCL_ERROR.
   */
  #define	IS_HELP(s)						\
      (strcmp(Tcl_GetStringFromObj(s,NULL), "-?") == 0) ? TCL_OK : TCL_ERROR
  
  #if defined(__cplusplus)
  }
  #endif
  
  #include "dbinc_auto/tcl_ext.h"
  #endif /* !_DB_TCL_DB_H_ */
  