/****************************************************************
 *								*
 *	Copyright 2010, 2012 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#ifdef GTM_TRIGGER
#include <errno.h>
#include "gtm_ctype.h"
#include "gtm_string.h"
#include "gtm_fcntl.h"
#include "gtm_unistd.h"
#include "gdsroot.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include <rtnhdr.h>
#include "gv_trigger.h"
#include "targ_alloc.h"
#include "filestruct.h"
#include "mv_stent.h"		/* for COPY_SUBS_TO_GVCURRKEY macro */
#include "gvsub2str.h"		/* for COPY_SUBS_TO_GVCURRKEY macro */
#include "format_targ_key.h"	/* for COPY_SUBS_TO_GVCURRKEY macro */
#include "trigger.h"
#include "trigger_gbl_fill_xecute_buffer.h"
#include "trigger_scan_string.h"
#include "trigger_select_protos.h"
#include "trigger_user_name.h"
#include "change_reg.h"
#include "gvcst_protos.h"
#include "op.h"
#include "mupip_exit.h"
#include "zshow.h"
#include "util.h"
#include "compiler.h"
#include "mvalconv.h"
#include "op_tcommit.h"
#include "gdscc.h"			/* needed for tp.h */
#include "gdskill.h"			/* needed for tp.h */
#include "buddy_list.h"			/* needed for tp.h */
#include "hashtab_int4.h"		/* needed for tp.h */
#include "jnl.h"			/* needed for tp.h */
#include "tp.h"
#include "t_retry.h"
#include "io_params.h"
#include "min_max.h"			/* Needed for MIN */
#include "gtmimagename.h"
#include "gtmio.h"
#include "have_crit.h"

GBLREF	sgmnt_addrs		*cs_addrs;
GBLREF	sgmnt_data_ptr_t	cs_data;
GBLREF	gd_region		*gv_cur_region;
GBLREF	gv_namehead		*gv_target;
GBLREF	gv_key			*gv_currkey;
GBLREF	gd_addr			*gd_header;
GBLREF	gv_key			*gv_altkey;
GBLREF	io_pair			io_curr_device;
GBLREF	io_pair			io_std_device;			/* standard device */
GBLREF	sgm_info		*sgm_info_ptr;
GBLREF	int			(*op_open_ptr)(mval *v, mval *p, int t, mval *mspace);

LITREF	mval		    	literal_hasht;
LITREF	mval			literal_zero;
LITREF	mval			literal_ten;
LITREF	char 			*trigger_subs[];

#define TRIGGER_NAME_COMMENT	"trigger name: "
#define TRIGGER_CYCLE_COMMENT	"  cycle: "

#define NAM_LEN(PTR, LEN)	MIN(STRLEN((PTR)), (LEN))

#define COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, VAL, VAL_LEN)		\
{											\
	mval		op_val;								\
											\
	assert(MAX_BUFF_SIZE >= VAL_LEN);						\
	if ((INTCAST(OUT_PTR - OUT_BUFF) + VAL_LEN) > MAX_BUFF_SIZE)			\
	{										\
		STR2MVAL(op_val, OUT_BUFF, (unsigned int)(OUT_PTR - OUT_BUFF));		\
		op_write(&op_val);							\
		io_curr_device.out->dollar.x = 0;					\
		OUT_PTR = OUT_BUFF;							\
	}										\
	memcpy(OUT_PTR, (const void *)VAL, VAL_LEN);					\
	OUT_PTR += VAL_LEN;								\
}

#define COPY_SUBSCRIPT(OUT_BUFF, OUT_PTR, CHPTR, LEN_LEFT, IS_TYPE)			\
{											\
	COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, CHPTR, 1);		\
	CHPTR++;									\
	LEN_LEFT--;									\
	while ((0 < LEN_LEFT) && IS_TYPE(*CHPTR))					\
	{										\
		COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_BUFF, OUT_PTR, CHPTR, 1);	\
		CHPTR++;								\
		LEN_LEFT--;								\
	}										\
}

#define MAKE_ZWR_STR(STR, STR_LEN, OUT_START, OUT_STR)					\
{											\
	int		lcl_len;				       			\
	unsigned char	tmp_buff[MAX_ZWR_EXP_RATIO * MAX_BUFF_SIZE];			\
											\
	format2zwr((sm_uc_ptr_t)STR, STR_LEN, tmp_buff, &lcl_len);			\
	COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(OUT_START, OUT_STR, tmp_buff, lcl_len);	\
}

#define CHECK_FOR_M_NAME(START, PTR, LEN, STR, ERR, MAX_LEN)				\
{											\
	int		lcl_len;							\
	int		cur_len;							\
											\
	assert(0 < MAX_LEN);								\
	if (!ISALPHA_ASCII(*PTR) && ('%' != *PTR))					\
	{										\
		lcl_len = STRLEN(START);						\
		CONV_STR_AND_PRINT(STR, lcl_len, START);				\
		ERR = TRIG_FAILURE;							\
		continue;								\
	}										\
	PTR++;										\
	LEN--;										\
	cur_len = 1;									\
	while ((0 < LEN) && ISALNUM_ASCII(*PTR))					\
	{										\
		if (MAX_LEN < ++cur_len)						\
			break;								\
		PTR++;									\
		LEN--;									\
	}										\
}

#define INVALID_NAME_ERROR(ERR_STR, PTR, ERR_VAR, POS)					\
{											\
	int		lcl_len;							\
											\
	lcl_len = STRLEN(PTR);								\
	CONV_STR_AND_PRINT(ERR_STR, lcl_len, PTR);					\
	select_status = TRIG_FAILURE;							\
}

error_def(ERR_MUNOACTION);
error_def(ERR_MUPCLIERR);
error_def(ERR_TRIGDEFBAD);
error_def(ERR_MUPCLIERR);
error_def(ERR_MUNOACTION);


STATICDEF char *triggerfile_quals[] = {"-name=", "", "-commands=", "-options=", "-delim=", "-zdelim=", "-pieces=", "-xecute="};

STATICFNDEF void write_subscripts(char *out_rec, char **out_ptr, char **sub_ptr, int *sub_len)
{
	char			*out_p, *ptr, *dst_ptr;
	int			str_len;
	uint4			len_left, dst_len, len;
	char			dst[MAX_GVSUBS_LEN];

	len_left = *sub_len;
	ptr = *sub_ptr;
	out_p = *out_ptr;
	while (0 < len_left)
	{
		if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr))
		{
			COPY_SUBSCRIPT(out_rec, out_p, ptr, len_left, ISDIGIT_ASCII);
		} else if (ISALPHA_ASCII(*ptr) || ('%' == *ptr))
		{
			COPY_SUBSCRIPT(out_rec, out_p, ptr, len_left, ISALNUM_ASCII);
		} else if ('"' == *ptr)
		{
			len = len_left;
			trigger_scan_string(ptr, &len_left, dst, &dst_len);
			MAKE_ZWR_STR(dst, dst_len, out_rec, out_p);
			len_left--;		/* Need to skip the trailing " */
			ptr += (len - len_left);
		} else
		{
			COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_p, ptr, 1);
			ptr++;
			len_left--;
		}
	}
	*sub_len = len_left;
	*sub_ptr = ptr;
	*out_ptr = out_p;
}

STATICFNDEF void write_out_trigger(char *gbl_name, uint4 gbl_name_len, uint4 file_name_len, mval *op_val, int nam_indx)
{
	mval			data_val;
	char			out_rec[MAX_BUFF_SIZE];
	char			*out_rec_ptr;
	char			*ptr1, *ptr2;
	mname_entry		gvent;
	mval			mi, trigger_count, trigger_value;
	mval			*mv_trig_cnt_ptr;
	boolean_t		multi_line;
	boolean_t		have_value, multi_record;
	int			count;
	int			indx, sub_indx;
	int4			skip_chars;
	int			sub_len;
	char			*sub_ptr;
	char			*tmp_str_ptr, tmp_string[MAX_SRCLINE];
	uint4			tmp_str_len;
	char			cycle[MAX_DIGITS_IN_INT + 1];
	unsigned char		util_buff[MAX_TRIG_UTIL_LEN];
	int4			util_len;
	char			*xecute_buff;
	int4			xecute_len;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHCOUNT, LITERAL_HASHCOUNT_LEN);
	if (gvcst_get(&trigger_count))
	{
		BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL));
		if (!gvcst_get(&trigger_value))
		{	/* There has to be a #LABEL */
			if (CDB_STAGNATE > t_tries)
				t_retry(cdb_sc_triggermod);
			else
			{
				assert(FALSE);
				rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, gbl_name_len, gbl_name, gbl_name_len, gbl_name,
					LEN_AND_LIT("\"#LABEL\""));
			}
		}
		skip_chars = 1;
		if ((trigger_value.str.len != STRLEN(HASHT_GBL_CURLABEL))
				|| (0 != memcmp(trigger_value.str.addr, HASHT_GBL_CURLABEL, trigger_value.str.len)))
		{
			if ((1 == trigger_value.str.len) && ('1' == *trigger_value.str.addr))
				/* 1 == #LABEL - No leading blank in xecute string */
				skip_chars = 0;
		}
		mv_trig_cnt_ptr = &trigger_count;
		count = MV_FORCE_INT(mv_trig_cnt_ptr);
		BUILD_HASHT_SUB_SUB_CURRKEY(gbl_name, gbl_name_len, LITERAL_HASHCYCLE, LITERAL_HASHCYCLE_LEN);
		if (!gvcst_get(&trigger_value))
		{	/* There has to be a #CYCLE */
			if (CDB_STAGNATE > t_tries)
				t_retry(cdb_sc_triggermod);
			else
			{
				assert(FALSE);
				rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, gbl_name_len, gbl_name, gbl_name_len, gbl_name,
					LEN_AND_LIT("\"#CYCLE\""));
			}
		}
		assert(MAX_DIGITS_IN_INT >= trigger_value.str.len);
		memcpy(cycle, trigger_value.str.addr, trigger_value.str.len);
		cycle[trigger_value.str.len] = '\0';
		xecute_buff = NULL;
		for (indx = 1; indx <= count; indx++)
		{
			if ((0 != nam_indx) && (indx != nam_indx))
				continue;
			MV_FORCE_MVAL(&mi, indx);
			BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[TRIGNAME_SUB],
				STRLEN(trigger_subs[TRIGNAME_SUB]));
			out_rec_ptr = out_rec;
			if (gvcst_get(&trigger_value))
			{
				*out_rec_ptr++ = COMMENT_LITERAL;

				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, TRIGGER_NAME_COMMENT,
					STR_LIT_LEN(TRIGGER_NAME_COMMENT));
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, trigger_value.str.addr,
					trigger_value.str.len);
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, TRIGGER_CYCLE_COMMENT,
					STR_LIT_LEN(TRIGGER_CYCLE_COMMENT));
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, cycle, STRLEN(cycle));
				STR2MVAL((*op_val), out_rec, (unsigned int)(out_rec_ptr - out_rec));
				op_write(op_val);
				op_wteol(1);
			}
			out_rec_ptr = out_rec;
			*out_rec_ptr++ = '+';
			*out_rec_ptr++ = '^';
			COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, gbl_name, gbl_name_len);
			BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[GVSUBS_SUB],
				STRLEN(trigger_subs[GVSUBS_SUB]));
			if (gvcst_get(&trigger_value))
			{
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, "(", 1);
				sub_ptr = trigger_value.str.addr;
				sub_len = trigger_value.str.len;
				write_subscripts(out_rec, &out_rec_ptr, &sub_ptr, &sub_len);
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, ")", 1);
			}
			for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++)
			{
				if ((GVSUBS_SUB == sub_indx) || (CHSET_SUB == sub_indx))
					continue;
				BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(gbl_name, gbl_name_len, mi, trigger_subs[sub_indx],
					STRLEN(trigger_subs[sub_indx]));
				have_value = gvcst_get(&trigger_value);
				multi_record = FALSE;
				if (!have_value && (XECUTE_SUB == sub_indx))
				{
					op_gvdata(&data_val);
					multi_record = (literal_ten.m[0] == data_val.m[0]) && (literal_ten.m[1] == data_val.m[1]);
				}
				if (have_value || multi_record)
				{
					if (TRIGNAME_SUB == sub_indx)
					{	/* Output name only if it is user defined */
						BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(gbl_name, gbl_name_len, mi,
							trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), mi);
						if (!trigger_user_name(trigger_value.str.addr, trigger_value.str.len))
							continue;
						trigger_value.str.len--;	/* Don't include trailing # */
					}
					COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, " ", 1);
					COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, triggerfile_quals[sub_indx],
						STRLEN(triggerfile_quals[sub_indx]));
					switch (sub_indx)
					{
						case DELIM_SUB:
						case ZDELIM_SUB:
							MAKE_ZWR_STR(trigger_value.str.addr, trigger_value.str.len, out_rec,
								     out_rec_ptr);
							break;
						case XECUTE_SUB:
							/* After the buffer is malloc-ed by trigger_gbl_fill_xecute_buffer(), the
							 * only exits from the inner loop have to go to the free() below.  Exits
							 * from the outer loop happen before the buffer is malloc-ed.  So no leaks
							 * by error returns.  But just to make sure, let's assert that the buffer
							 * pointer is NULL.
							 */
							assert(NULL == xecute_buff);
							xecute_buff = trigger_gbl_fill_xecute_buffer(gbl_name, gbl_name_len, &mi,
								multi_record ? NULL : &trigger_value, &xecute_len);
							multi_line = (NULL != memchr(xecute_buff, '\n', xecute_len));
							assert(NULL != xecute_buff);
							if (multi_line)
							{
								COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr,
									XTENDED_START, XTENDED_START_LEN);
							} else
							{
								tmp_str_ptr = xecute_buff + skip_chars;
								tmp_str_len = xecute_len - skip_chars;
								MAKE_ZWR_STR(tmp_str_ptr, tmp_str_len, out_rec, out_rec_ptr);
							}
							break;
						default:
							COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr,
								trigger_value.str.addr,trigger_value.str.len);
					}
				}
			}
			/* we had better have an XECUTE STRING, probably should check some buddies */
			assert(NULL != xecute_buff);
			STR2MVAL((*op_val), out_rec, (unsigned int)(out_rec_ptr - out_rec));
			op_write(op_val);
			op_wteol(1);
			if (multi_line)
			{
				ptr1 = xecute_buff;
				do
				{
					ptr2 = memchr(ptr1, '\n', xecute_len);
					out_rec_ptr = out_rec;
					COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, ptr1, UINTCAST(ptr2 - ptr1));
					STR2MVAL((*op_val), out_rec, (unsigned int)(out_rec_ptr - out_rec));
					op_write(op_val);
					op_wteol(1);
					ptr1 = ptr2 + 1;
				} while (ptr1 < (xecute_buff + xecute_len));
				out_rec_ptr = out_rec;
				COPY_TO_OUTPUT_AND_WRITE_IF_NEEDED(out_rec, out_rec_ptr, XTENDED_STOP, XTENDED_STOP_LEN);
				STR2MVAL((*op_val), out_rec, (unsigned int)(out_rec_ptr - out_rec));
				op_write(op_val);
				op_wteol(1);
			}
			if (NULL != xecute_buff)
			{
				free(xecute_buff);
				xecute_buff = NULL;
			}
		}
	}
}

STATICFNDEF void write_gbls_or_names(char *gbl_name, uint4 gbl_name_len, uint4 file_name_len, mval *op_val, boolean_t trig_name)
{
	char			save_name[MAX_MIDENT_LEN], curr_name[MAX_MIDENT_LEN];
	boolean_t		wildcard;
	char			save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
	gd_region		*save_gv_cur_region;
	gv_key			*save_gv_currkey;
	gv_namehead		*hasht_tree, *save_gv_target;
	sgm_info		*save_sgm_info_ptr;
	sgmnt_addrs		*csa;
	mname_entry		gvent;
	mstr			ms_gbl_nam;
	mval			mv_curr_nam;
        mval                    mi, trigger_count, trig_gbl;
        mval                    *mv_trig_cnt_ptr, mv_trigger_val;
	int			indx, count;
	char			*ptr;
	uint4			curr_name_len;
	int			trigvn_len;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	memcpy(save_name, gbl_name, gbl_name_len);
	wildcard = (NULL != (ptr = memchr(gbl_name, '*', gbl_name_len)));
	if (wildcard)
	{
		*ptr = '\0';
		gbl_name_len--;
		assert(INTCAST(ptr - gbl_name) == gbl_name_len);
	}
	mv_trig_cnt_ptr = &trigger_count;
	memcpy(curr_name, gbl_name, gbl_name_len);
	curr_name_len = gbl_name_len;
	STR2MVAL(mv_curr_nam, gbl_name, gbl_name_len);
	while (TRUE)
	{
		if (0 != memcmp(curr_name, save_name, gbl_name_len))
			break;
		if (trig_name)
		{
			BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), curr_name, curr_name_len);
			if (!gvcst_get(&mv_trigger_val))
			{
				if (wildcard)
				{
					op_gvorder(&mv_curr_nam);
					if (0 == mv_curr_nam.str.len)
						break;
					memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len);
					curr_name_len = mv_curr_nam.str.len;
					continue;
				}
				break;
			}
			ptr = mv_trigger_val.str.addr;
			trigvn_len = STRLEN(mv_trigger_val.str.addr);
			ptr += trigvn_len + 1;
			A2I(ptr, mv_trigger_val.str.addr + mv_trigger_val.str.len, indx);
			STR2MVAL(trig_gbl, mv_trigger_val.str.addr, trigvn_len);
			SAVE_TRIGGER_REGION_INFO;
			ms_gbl_nam.addr = mv_trigger_val.str.addr;
			ms_gbl_nam.len = trigvn_len;
			GV_BIND_NAME_ONLY(gd_header, &ms_gbl_nam);
			csa = gv_target->gd_csa;
			SETUP_TRIGGER_GLOBAL;
			INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
		} else
		{
			STR2MVAL(trig_gbl, curr_name, curr_name_len);
			indx = 0;
		}
		write_out_trigger(trig_gbl.str.addr, trig_gbl.str.len, file_name_len, op_val, indx);
		if (trig_name)
		{
			RESTORE_TRIGGER_REGION_INFO;
		}
		if (wildcard)
		{
			if (trig_name)
			{
				BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), curr_name,
					curr_name_len);
			} else
			{
				BUILD_HASHT_SUB_CURRKEY(curr_name, curr_name_len);
			}
			op_gvorder(&mv_curr_nam);
			if (0 == mv_curr_nam.str.len)
				break;
			memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len);
			curr_name_len = mv_curr_nam.str.len;
		} else
			break;
	}
}

STATICFNDEF void dump_all_triggers(uint4 file_name_len, mval *op_val)
{
	mval			curr_gbl_name, val;
	gd_region		*reg;
	sgmnt_addrs		*csa;
	mname_entry		gvent;
	gv_namehead		*hasht_tree, *save_gvtarget;
	char			save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
	char			save_altkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
	gv_key			*save_gv_currkey;
	gv_key			*save_gv_altkey;
	unsigned char		*key;
	mval			trigger_value;
	int			reg_index;
	mstr			gbl_name;
	char			global[MAX_MIDENT_LEN];
	int			gbl_len;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(NULL != gd_header);
	save_gvtarget = gv_target;
	for (reg_index = 0, reg = gd_header->regions; reg_index < gd_header->n_regions; reg_index++, reg++)
	{
		if (!reg->open)
			gv_init_reg(reg);
		gv_cur_region = reg;
		change_reg();
		csa = cs_addrs;
		SETUP_TRIGGER_GLOBAL;
		INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
		if (0 != gv_target->root)
		{
			op_gvdata(&val);
			if ((literal_ten.m[0] == val.m[0]) && (literal_ten.m[1] == val.m[1]))
			{ /* $DATA(^#t) is 10 - get first subscript (trigger's global) */
				BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STR_LIT_LEN(LITERAL_MAXHASHVAL));
				while (TRUE)
				{
					op_gvorder(&curr_gbl_name);
					if (0 == curr_gbl_name.str.len)
						break;
					gbl_len = curr_gbl_name.str.len;
					memcpy(global, curr_gbl_name.str.addr, gbl_len);
					write_out_trigger(global, gbl_len, file_name_len, op_val, 0);
					BUILD_HASHT_SUB_CURRKEY(global, gbl_len);
				}
			} else
				assert((literal_zero.m[0] == val.m[0]) && (literal_zero.m[1] == val.m[1]));
		}
	}
}

boolean_t trigger_select(char *select_list, uint4 select_list_len, char *file_name, uint4 file_name_len)
{
	char			*sel_ptr, *prev_ptr, *ptr1, *ptr2;
	int			gbl_len, prev_len;
	mstr			gbl_name;
	sgmnt_addrs		*csa;
	gv_namehead		*hasht_tree, *save_gvtarget;
	char			save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
	char			save_altkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
	gv_key			*save_gv_currkey;
	gv_key			*save_gv_altkey;
	gd_region		*save_gv_cur_region;
	char			save_select_list[MAX_BUFF_SIZE];
	mname_entry		gvent;
	mval			trigger_value;
	int			len, len1, badpos;
	int			local_errno;
	boolean_t		trig_name;
	gv_namehead		*save_gv_target;
	sgm_info		*save_sgm_info_ptr;
	boolean_t		dump_all;
	mval			op_val, op_pars;
	boolean_t		select_status;
	io_pair			save_io_curr_device;

	static readonly unsigned char	open_params_list[] =
	{
		(unsigned char)iop_m,
		(unsigned char)iop_noreadonly,
		(unsigned char)iop_nowrap,
		(unsigned char)iop_stream,
		(unsigned char)iop_eol
	};
	static readonly unsigned char	use_params[] =
	{
		(unsigned char)iop_nowrap,
		(unsigned char)iop_eol
	};
	static readonly unsigned char	no_param = (unsigned char)iop_eol;

	/* make a local copy of the select list and use it to avoid string-pool problems */
	if (MAX_BUFF_SIZE <= select_list_len)
		return TRIG_FAILURE;
	memcpy(save_select_list, select_list, select_list_len);
	save_select_list[select_list_len] = '\0';
	gvinit();
	dump_all = FALSE;
	prev_len = 0;
	op_pars.mvtype = MV_STR;
	op_val.mvtype = MV_STR;
	if (0 == file_name_len)
	{
		op_pars.str.len = SIZEOF(use_params);
		op_pars.str.addr = (char *)use_params;
		if (IS_MUPIP_IMAGE)
		{
			PRINTF("\n");
			FFLUSH(NULL);
			op_val.str.len = io_std_device.out->trans_name->len;
			op_val.str.addr = io_std_device.out->trans_name->dollar_io;
		} else
		{
			op_val.str.len = io_curr_device.out->trans_name->len;
			op_val.str.addr = io_curr_device.out->trans_name->dollar_io;
		}
	} else
	{
		op_pars.str.len = SIZEOF(open_params_list);
		op_pars.str.addr = (char *)open_params_list;
		op_val.str.len = file_name_len;
		op_val.str.addr = (char *)file_name;
		(*op_open_ptr)(&op_val, &op_pars, 0, 0);
	}
	save_io_curr_device = io_curr_device;
	op_use(&op_val, &op_pars);
	if (0 == select_list_len)
		dump_all = TRUE;
	else
	{
		for (ptr1 = save_select_list, len = select_list_len;
		     (NULL != (ptr2 = strchr(ptr1, '*'))) && (len > (len1 = INTCAST(ptr2 - ptr1)));
		     ptr1 = ptr2 + 1)
		{	/* look for either a real "dump-it-all" *, an error *, or a wildcard * */
			/* A "*" anywhere in the select list (at a place where a global name would be) is the same as only a "*" */
			len -= (len1 + 1);		/* Length after the "*" -- len1 is length before the "*" */
			assert((0 <= len1) && (0 <= len));
			if (dump_all = ((0 == len1) && (0 == len) && (ptr1 == save_select_list))
					|| ((0 == len1) && (0 == len) && (',' == *(ptr2 - 1)))
					|| ((0 == len1) && (0 < len) && (',' == *(ptr2 + 1)))
					|| ((0 < len1) && (0 == len) && (',' == *(ptr2 - 1)))
					|| ((0 < len1) && (0 < len) && (',' == *(ptr2 - 1)) && (',' == *(ptr2 + 1))))
				break;
		}
	}
	select_status = TRIG_SUCCESS;
	if (dump_all)
		dump_all_triggers(file_name_len, &op_val);
	else
	{
		len = select_list_len;
		sel_ptr = strtok(save_select_list, ",");
		do
		{
			trig_name = ('^' != *sel_ptr);
			ptr1 = sel_ptr;
			len1 = STRLEN(ptr1);
			if (!trig_name)
			{
				ptr1++;
				len1--;
				CHECK_FOR_M_NAME(sel_ptr, ptr1, len1, "Invalid global variable name in SELECT list: ",
						 select_status, MAX_MIDENT_LEN);
				if ((0 != len1) &&  ((1 != len1) || ('*' != *ptr1)))
				{
					badpos = len1;
					INVALID_NAME_ERROR("Invalid global variable name in SELECT list: ", sel_ptr,
						select_status, badpos);
					continue;
				}
				SAVE_TRIGGER_REGION_INFO;
				gbl_len = NAM_LEN(sel_ptr + 1, (int)(ptr1 - sel_ptr) - 1);
				ptr1 = sel_ptr + 1;
				if ((prev_len != gbl_len) || (0 != memcmp(prev_ptr, ptr1, gbl_len)))
				{
					gbl_name.addr = ptr1;
					gbl_name.len = gbl_len;
					prev_ptr = ptr1;
					prev_len = gbl_len;
					GV_BIND_NAME_ONLY(gd_header, &gbl_name);
					if ('*' == *(ptr1 + gbl_len))
						gbl_name.len++;
				}
				csa = gv_target->gd_csa;
				SETUP_TRIGGER_GLOBAL;
			} else
			{
				if (len1 != (badpos = validate_input_trigger_name(ptr1, len1, NULL))) /* assignment is intended */
				{ /* is the input name valid */
					INVALID_NAME_ERROR("Invalid name entry in SELECT list: ",
							   sel_ptr, select_status, badpos);
					continue;
				}
				if (TRIGNAME_SEQ_DELIM == *(sel_ptr + (len1 - 1)))
					/* drop the trailing # sign */
					len1--;
				gbl_name.addr = sel_ptr;
				gbl_name.len = len1;
				SAVE_TRIGGER_REGION_INFO;
				SWITCH_TO_DEFAULT_REGION;
			}
			INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
			if (0 != gv_target->root)
				write_gbls_or_names(gbl_name.addr, gbl_name.len, file_name_len, &op_val, trig_name);
			RESTORE_TRIGGER_REGION_INFO;
		} while (NULL != (sel_ptr = strtok(NULL, ",")));	/* Embedded assignment is intended */
	}
	if (0 != file_name_len)
	{
		op_val.mvtype = op_pars.mvtype = MV_STR;
		op_val.str.addr = (char *)file_name;;
		op_val.str.len = file_name_len;
		op_pars.str.len = SIZEOF(no_param);
		op_pars.str.addr = (char *)&no_param;
		op_close(&op_val, &op_pars);
		/* Return back to the current device */
		io_curr_device = save_io_curr_device;
	}
	return select_status;
}

#endif
