Пример #1
0
void lvzwr_fini(zshow_out *out, int t)
{
	int4		size;
	mval 		local;
	mname_entry	temp_key;
	ht_ent_mname	*tabent;
	mident_fixed	m;

	zwr_output = out;
	assert(lvzwrite_block);
	if (zwr_patrn_mident == lvzwrite_block->zwr_intype)
	{	/* Mident specified for "pattern" (fixed name, no pattern) */
		size = (lvzwrite_block->pat->str.len <= MAX_MIDENT_LEN) ? lvzwrite_block->pat->str.len : MAX_MIDENT_LEN;
		temp_key.var_name = lvzwrite_block->pat->str;
		COMPUTE_HASH_MNAME(&temp_key);
		tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key);
		if (!tabent || !LV_IS_VAL_DEFINED(tabent->value) && !LV_HAS_CHILD(tabent->value))
		{
			lvzwrite_block->subsc_count = 0;
			rts_error(VARLSTCNT(4) ERR_UNDEF, 2, size, lvzwrite_block->pat->str.addr);
		} else
		{
			lvzwrite_block->curr_name = &tabent->key.var_name;
			lvzwr_var(((lv_val *)tabent->value), 0);
		}
	} else
	{	/* mval specified for character "pattern" (pattern matching) */
		assert(zwr_patrn_mval == lvzwrite_block->zwr_intype);
		memset(m.c, 0, SIZEOF(m.c));
		local.mvtype = MV_STR;
		local.str.addr = &m.c[0];
		local.str.len = 1;
		m.c[0] = '%';	/* Starting variable name for search (first possible varname) */

		lvzwrite_block->fixed = FALSE;
		while (local.str.len)
		{
			if (do_pattern(&local, lvzwrite_block->pat))
			{
				memset(&m.c[local.str.len], 0, SIZEOF(m.c) - local.str.len);
				temp_key.var_name = local.str;
				COMPUTE_HASH_MNAME(&temp_key);
				if (NULL != (tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &temp_key)))
				{
					lvzwrite_block->curr_name = &tabent->key.var_name;
					lvzwr_var(((lv_val *)tabent->value), 0);
				}
			}
			op_fnlvname(&local, TRUE, &local);
			assert(local.str.len <= MAX_MIDENT_LEN);
			memcpy(&m.c[0], local.str.addr, local.str.len);
			local.str.addr = &m.c[0];
		}
	}
	lvzwrite_block->curr_subsc = lvzwrite_block->subsc_count = 0;
	return;
}
Пример #2
0
/* Operation - Kill an alias (unsubscripted variable)
 *
 * Look it up in the hash table and remove the pointer to the lv_val to destroy the
 * alias association. Will need to do this in any previous symtabs this var is in
 * as well.
 */
void op_killalias(int srcindx)
{
	ht_ent_mname	*tabent;
	mname_entry	*varname;
	lv_val		*lv;
	int4		symvlvl;

	active_lv = (lv_val *)NULL;	/* if we get here, subscript set was successful.  clear active_lv to avoid later
					 * cleanup problems.
					 */
	varname = &(((mname_entry *)frame_pointer->vartab_ptr)[srcindx]);
	tabent = lookup_hashtab_mname(&curr_symval->h_symtab, varname);		/* Retrieve hash tab entry this var */
	if (tabent)
	{
		lv = (lv_val *)tabent->value;
		assert(lv);
		assert(LV_IS_BASE_VAR(lv));
		symvlvl = (LV_GET_SYMVAL(lv))->symvlvl;
		/* Clone var if necessary */
		if (dollar_tlevel && (NULL != lv->tp_var) && !lv->tp_var->var_cloned)
			TP_VAR_CLONE(lv);
		/* Decrement reference count and cleanup if necessary */
		DECR_BASE_REF(tabent, lv, TRUE);
		MARK_ALIAS_ACTIVE(symvlvl);	/* Mark this entry as aliasly active now */
	} /* Else var has no hastable entry so this is a NOOP */
}
Пример #3
0
void	op_indglvn(mval *v,mval *dst)
{
	bool		rval;
	mstr		*obj, object;
	oprtype		x;
	lv_val 		*a;
	icode_str	indir_src;
	lv_val		*lv;
	var_tabent	targ_key;
	ht_ent_mname	*tabent;

	error_def(ERR_INDMAXNEST);
	error_def(ERR_UNDEF);

	MV_FORCE_STR(v);
	indir_src.str = v->str;
	indir_src.code = indir_glvn;
	if (NULL == (obj = cache_get(&indir_src)))
	{
		if (valid_mname(&v->str))
		{
			targ_key.var_name = v->str;
			COMPUTE_HASH_MNAME(&targ_key);
			tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &targ_key);
			assert(NULL == tabent ||  NULL != tabent->value);
			if (!tabent || !MV_DEFINED(&((lv_val *)tabent->value)->v))
			{
				if (undef_inhibit)
				{
					*dst = literal_null;
					return;
				}
				else
					rts_error(VARLSTCNT(4) ERR_UNDEF, 2, v->str.len, v->str.addr);
			}
			a = (lv_val *)tabent->value;
			*dst = a->v;
			return;
		}
		comp_init(&v->str);
		rval = glvn(&x);
		if (comp_fini(rval, &object, OC_IRETMVAL, &x, v->str.len))
		{
			indir_src.str.addr = v->str.addr;
			cache_put(&indir_src, &object);
			*ind_result_sp++ = dst;
			if (ind_result_sp >= ind_result_top)
				rts_error(VARLSTCNT(1) ERR_INDMAXNEST);
			comp_indr(&object);
		}
	}
	else
	{
		*ind_result_sp++ = dst;
		if (ind_result_sp >= ind_result_top)
			rts_error(VARLSTCNT(1) ERR_INDMAXNEST);
		comp_indr(obj);
	}
}
Пример #4
0
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL
 * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the
 * very last level.
 *
 * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled
 * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error.
 */
int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc)
{
	ht_ent_mname	*tabent;
	lv_val		*inuse_lv;
	int		elemindx;
	mv_stent	*mvc;
	lvscan_blk	*lvscan, *newlvscan;
	lvTree		*lvt_child;
	boolean_t	var_cloned;

	assert(curr_lv);
	assert(LV_IS_BASE_VAR(curr_lv));
	assert(curr_lv->tp_var);
	DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr"  save_lv: 0x"lvaddr"\n",
		 restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv));
	DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr
		 " symval: 0x"lvaddr"/0x"lvaddr"\n",
		 tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval));

	/* First get the stack in the position where we can actually process this entry. Need to make sure we are processing
	 * the symbol table we need to be processing so unwind enough stuff to get there.
	 */
	if (curr_symval != tp_pointer->sym)
	{	/* Unwind as many stackframes as are necessary up to the max */
		while((curr_symval != tp_pointer->sym) && (frame_pointer < tp_pointer->fp))
		{
#			ifdef GTM_TRIGGER
			if (SFT_TRIGR & frame_pointer->type)
			{	/* We have encountered a trigger base frame. We cannot unroll it because there are C frames
				 * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so
				 * it can unroll the base frame and rethrow the error to properly unroll the C stack.
				 */
				*tprestart_rc = ERR_TPRETRY;
				tprestart_state = TPRESTART_STATE_TPUNW;
				DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - "
					  "rethrowing\n"));
				return -1;
			}
#			endif
			op_unwind();
		}
		if (curr_symval != tp_pointer->sym)
		{	/* Unwind as many mv_stents as are necessary up to the max */
			mvc = mv_chain;
			while((curr_symval != tp_pointer->sym) && (mvc < tp_pointer->mvc))
			{
				unw_mv_ent(mvc);
				mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc);
			}
			mv_chain = mvc;
			/* Final check */
			assertpro(curr_symval == tp_pointer->sym);
		}
	}
	var_cloned = curr_lv->tp_var->var_cloned;
	if (var_cloned)
	{	/* Var/tree has been copied (and modified) -- see about restoring it */
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n"));
		if (NULL != restore_ent->key.var_name.addr)
		{	/* Restore data into a named variable (hash table entry)
			 * Step 1 -- find its hash table address to see what lv_val is there now.
			 */
			tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key);
			assert(tabent);
			/* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val
			 * currently in use. Decrement its use count (which will delete it and the tree if
			 * it is no longer used) and replace with desired previous lv_val whose use count
			 * was incremented when it was saved.
			 */
			if (curr_lv != (inuse_lv = (lv_val *)tabent->value))	/* Note assignment */
			{
				if (inuse_lv)
					DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE);
				DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n",
					 tabent, tabent->value, curr_lv));
				tabent->value = (void *)curr_lv;
				INCR_TREFCNT(curr_lv);			/* Back in the hash table, bump its reference */
			}
		} /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */
		/* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value.
		 * Get rid of its current tree if any.
		 */
		if (lvt_child = LV_GET_CHILD(curr_lv))	/* Note assignment */
		{
			DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv));
			assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child));
			LV_CHILD(curr_lv) = NULL;	/* prevent recursion due to alias containers */
			lv_killarray(lvt_child, FALSE);
		}
		/* Step 4:  Copy in the needed fields from the saved flavor lv_val back to curr_lv.
		 * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date.
		 */
		DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n",
			save_lv, curr_lv));
		/* The following is optimized to do the initialization of just the needed structure members. For that it assumes a
		 * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will
		 * automatically show an issue here and cause the below initialization to be accordingly reworked.
		 */
		assert(0 == OFFSETOF(lv_val, v));
		assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs));
		assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats));
		assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont));
		assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark));
		assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var));
		assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val));
		/* save_lv -> curr_lv Copy begin */
		curr_lv->v = save_lv->v;
		curr_lv->ptrs = save_lv->ptrs;
		assert(0 < curr_lv->stats.trefcnt);	/* No need to copy "stats" as curr_lv is more uptodate */
		assert(0 < curr_lv->stats.crefcnt);
		assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont)));
		curr_lv->has_aliascont = save_lv->has_aliascont;
		DBGALS_ONLY(curr_lv->lvmon_mark = save_lv->has_aliascont);
		assert(save_lv->tp_var == curr_lv->tp_var);	/* no need to copy this field */
		/* save_lv -> curr_lv Copy done */
		/* Some fixup may need to be done if the variable was cloned (and thus moved around) */
		curr_lv->tp_var->var_cloned = FALSE;
		if (lvt_child = LV_GET_CHILD(curr_lv))
		{	/* Some pointer fix up needs to be done since the owner of the restored tree changed */
			assert(LVT_PARENT(lvt_child) == ((lvTreeNode *)curr_lv->tp_var->save_value));
			LV_CHILD(save_lv) = NULL;	/* now that curr_lv->tp_var->var_cloned has been reset */
			LVT_PARENT(lvt_child) = (lvTreeNode *)curr_lv;
			if (curr_lv->has_aliascont && (NULL != lvscan_anchor))
			{	/* Some ref counts need to be restored for arrays this tree points to -- but only if the
				 * array contains pointers (alias containers).
				 */
				DBGRFCT((stderr, "\ntp_unwind_restlv: Putting lv 0x:"lvaddr" on the lvscan list\n", curr_lv));
				/* This array needs to have container pointer target reference counts reestablished. Record
				 * the lv so this can happen after all vars are restored.
				 */
				lvscan = *lvscan_anchor;
				elemindx = lvscan->elemcnt++;	/* Note post increment so elemindx has minus-one value */
				if (ARY_SCNCNTNR_MAX < elemindx)
				{	/* Present block is full so allocate a new one and chain it on */
					lvscan->elemcnt--;		/* New element ended up not being in that block.. */
					newlvscan = (lvscan_blk *)malloc(SIZEOF(lvscan_blk));
					newlvscan->next = lvscan;
					newlvscan->elemcnt = 1;		/* Going to use first one *now* */
					elemindx = 0;
					*lvscan_anchor = newlvscan;
					lvscan = newlvscan;
				}
				assert((ARY_SCNCNTNR_MAX >= elemindx) && (0 <= elemindx));
				lvscan->ary_scncntnr[elemindx] = curr_lv;
			}
		}
	} else
	{
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was NOT modified or cloned\n"));
		assert(NULL == LV_CHILD(save_lv));
		assert(!save_lv->tp_var->var_cloned);
		/* We know that the subscript array underneath curr_lv did not change since saving it into save_lv. But the
		 * unsubscripted lv could have changed (have no way of checking if that is the case) so restore it unconditionally.
		 */
		curr_lv->v = save_lv->v;
		/* No need to copy "save_lv->ptrs" as "ptrs" contains 2 fields both of which are already correct in "curr_lv" */
		assert(save_lv->ptrs.val_ent.parent.sym == curr_lv->ptrs.val_ent.parent.sym);
		assert(NULL == save_lv->ptrs.val_ent.children);
		/* No need to copy "save_lv->stats" as "curr_lv->stats" is more uptodate */
		assert(save_lv->has_aliascont == curr_lv->has_aliascont);	/* no need to copy this field */
		assert(save_lv->lvmon_mark == curr_lv->lvmon_mark);	/* no need to copy this field */
		assert(save_lv->tp_var == curr_lv->tp_var);	/* no need to copy this field */
	}
	if (NULL == lvscan_anchor)
		/* Means this is completely unwinding a nested level so we need to reset the tstartcycle in this
		 * lvval so it gets handled correctly when this lv is encountered again after the restart completes.
		 */
		curr_lv->stats.tstartcycle = 0;
	return 0;
}
Пример #5
0
void view_arg_convert(viewtab_entry *vtp, int vtp_parm, mval *parm, viewparm *parmblk, boolean_t is_dollar_view)
{
	static	int4		first_time = TRUE;
	char			*cptr;
	char			*strtokptr;
	gd_binding		*gd_map;
	gd_region		*gd_reg_start, *r_ptr, *r_top;
	gvnh_reg_t		*gvnh_reg;
	gvnh_spanreg_t		*gvspan;
	gv_namehead		*tmp_gvt;
	ht_ent_mname		*tabent;
	int			n, reg_index;
	mident_fixed		lcl_buff;
	mname_entry		gvent, lvent;
	mstr			namestr, tmpstr;
	unsigned char 		*c, *c_top, *dst, *dst_top, global_names[1024], *nextsrc, *src, *src_top, stashed, y;

	switch (vtp_parm)
	{
		case VTP_NULL:
			if (parm != 0)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			break;
		case (VTP_NULL | VTP_VALUE):
			if (NULL == parm)
			{
				parmblk->value = (mval *)&literal_one;
				break;
			}
			/* caution:  fall through */
		case VTP_VALUE:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			parmblk->value = parm;
			break;
		case (VTP_NULL | VTP_DBREGION):
			if (!is_dollar_view && ((NULL == parm) || ((1 == parm->str.len) && ('*' == *parm->str.addr))))
			{
				parmblk->gv_ptr = NULL;
				break;
			}
			/* caution:  fall through */
		case VTP_DBREGION:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			if (!gd_header)		/* IF GD_HEADER ==0 THEN OPEN GBLDIR */
				gvinit();
			r_ptr = gd_header->regions;
			if (!parm->str.len && vtp->keycode == VTK_GVNEXT)	/* "" => 1st region */
				parmblk->gv_ptr = r_ptr;
			else
			{
				for (cptr = parm->str.addr, n = 0; n < parm->str.len; cptr++, n++)
					lcl_buff.c[n] = TOUPPER(*cptr);		/* Region names are upper-case ASCII */
				namestr.len = n;
				namestr.addr = &lcl_buff.c[0];
				for (r_top = r_ptr + gd_header->n_regions; ; r_ptr++)
				{
					if (r_ptr >= r_top)
					{
						format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n);
						rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOREGION,2, n, global_names);
					}
					tmpstr.len = r_ptr->rname_len;
					tmpstr.addr = (char *)r_ptr->rname;
					MSTR_CMP(tmpstr, namestr, n);
					if (0 == n)
						break;
				}
				parmblk->gv_ptr = r_ptr;
			}
			break;
		case VTP_DBKEY:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			if (!parm->str.len)
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOTGBL, 2, parm->str.len, NULL);
			if (!gd_header)		/* IF GD_HEADER ==0 THEN OPEN GBLDIR */
				gvinit();
			c = (unsigned char *)parm->str.addr;
			if ('^' != *c)
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_NOTGBL, 2, parm->str.len, c);
			c_top = c + parm->str.len;
			c++;				/* skip initial '^' */
			parmblk->str.addr = (char *)c;
			for ( ; (c < c_top) && ('(' != *c); c++)
				;
			parmblk->str.len = (char *)c - parmblk->str.addr;
			if (MAX_MIDENT_LEN < parmblk->str.len)
				parmblk->str.len = MAX_MIDENT_LEN;
			if (!valid_mname(&parmblk->str))
			{
				format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n);
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names);
			}
			break;
		case VTP_RTNAME:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			memset(&parmblk->ident.c[0], 0, SIZEOF(parmblk->ident));
			if (parm->str.len > 0)
				memcpy(&parmblk->ident.c[0], parm->str.addr,
				       (parm->str.len <= MAX_MIDENT_LEN ? parm->str.len : MAX_MIDENT_LEN));
			break;
		case VTP_NULL | VTP_DBKEYLIST:
			if (NULL == parm || 0 == parm->str.len)
			{
				parmblk->ni_list.gvnh_list = NULL;
				parmblk->ni_list.type = NOISOLATION_NULL;
				break;
			}
			/* caution : explicit fall through */
		case VTP_DBKEYLIST:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			if (!gd_header)
				gvinit();
			if (first_time)
			{
				noisolation_buddy_list = (buddy_list *)malloc(SIZEOF(buddy_list));
				initialize_list(noisolation_buddy_list, SIZEOF(noisolation_element), NOISOLATION_INIT_ALLOC);
				gvt_pending_buddy_list = (buddy_list *)malloc(SIZEOF(buddy_list));
				initialize_list(gvt_pending_buddy_list, SIZEOF(gvt_container), NOISOLATION_INIT_ALLOC);
				first_time = FALSE;
			}
			assertpro(SIZEOF(global_names) > parm->str.len);
			tmpstr.len = parm->str.len;	/* we need to change len and should not change parm->str, so take a copy */
			tmpstr.addr = parm->str.addr;
			if (0 != tmpstr.len)
			{
				switch (*tmpstr.addr)
				{
					case '+' :
						parmblk->ni_list.type = NOISOLATION_PLUS;
						tmpstr.addr++;
						tmpstr.len--;
						break;
					case '-' :
						parmblk->ni_list.type = NOISOLATION_MINUS;
						tmpstr.addr++;
						tmpstr.len--;
						break;
					default :
						parmblk->ni_list.type = NOISOLATION_NULL;
						break;
				}
				if (!tmpstr.len)
					rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, tmpstr.len, NULL);
				memcpy(global_names, tmpstr.addr, tmpstr.len);
				global_names[tmpstr.len] = '\0';
				src = (unsigned char *)STRTOK_R((char *)global_names, ",", &strtokptr);
				REINITIALIZE_LIST(noisolation_buddy_list);	/* reinitialize the noisolation buddy_list */
				parmblk->ni_list.gvnh_list = NULL;
				for ( ; src < &global_names[tmpstr.len + 1]; src = nextsrc)
				{
					nextsrc = (unsigned char *)STRTOK_R(NULL, ",", &strtokptr);
					if (NULL == nextsrc)
						nextsrc = &global_names[tmpstr.len + 1];
					if (nextsrc - src >= 2 && '^' == *src)
					{
						namestr.addr = (char *)src + 1;		/* skip initial '^' */
						namestr.len = INTCAST(nextsrc - src - 2); /* don't count initial ^ and trailing 0 */
						if (namestr.len > MAX_MIDENT_LEN)
							namestr.len = MAX_MIDENT_LEN;
						if (valid_mname(&namestr))
						{
							memcpy(&lcl_buff.c[0], namestr.addr, namestr.len);
							gvent.var_name.len = namestr.len;
						} else
						{
							memcpy(&lcl_buff.c[0], src, nextsrc - src - 1);
							format2zwr((sm_uc_ptr_t)&lcl_buff.c, nextsrc - src - 1, global_names, &n);
							rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names);
						}
					} else
					{
						memcpy(&lcl_buff.c[0], src, nextsrc - src - 1);
						format2zwr((sm_uc_ptr_t)&lcl_buff.c, nextsrc - src - 1, global_names, &n);
						rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, n, global_names);
					}
					tmp_gvt = NULL;
					gvent.var_name.addr = &lcl_buff.c[0];
					COMPUTE_HASH_MNAME(&gvent);
					if (NULL != (tabent = lookup_hashtab_mname(gd_header->tab_ptr, &gvent)))
					{
						gvnh_reg = (gvnh_reg_t *)tabent->value;
						assert(NULL != gvnh_reg);
						tmp_gvt = gvnh_reg->gvt;
					} else
					{
						gd_map = gv_srch_map(gd_header, gvent.var_name.addr, gvent.var_name.len,
													SKIP_BASEDB_OPEN_FALSE);
						r_ptr = gd_map->reg.addr;
						tmp_gvt = (gv_namehead *)targ_alloc(r_ptr->max_key_size, &gvent, r_ptr);
						GVNH_REG_INIT(gd_header, gd_header->tab_ptr, gd_map, tmp_gvt,
											r_ptr, gvnh_reg, tabent);
						/* In case of a global spanning multiple regions, the gvt pointer corresponding to
						 * the region where the unsubscripted global reference maps to is stored in TWO
						 * locations (one in gvnh_reg->gvspan->gvt_array[index] and one in gvnh_reg->gvt.
						 * So pass in both these pointer addresses to be stored in the pending list in
						 * case this gvt gets reallocated (due to different keysizes between gld and db).
						 */
						if (NULL == (gvspan = gvnh_reg->gvspan))
						{
							ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(r_ptr, &gvnh_reg->gvt, NULL);
						} else
						{
							gd_reg_start = &gd_header->regions[0];
							GET_REG_INDEX(gd_header, gd_reg_start, r_ptr, reg_index);
								/* the above sets "reg_index" */
							assert(reg_index >= gvspan->min_reg_index);
							assert(reg_index <= gvspan->max_reg_index);
							reg_index -= gvspan->min_reg_index;
							ADD_TO_GVT_PENDING_LIST_IF_REG_NOT_OPEN(r_ptr,
								&gvspan->gvt_array[reg_index], &gvnh_reg->gvt);
						}
					}
					ADD_GVT_TO_VIEW_NOISOLATION_LIST(tmp_gvt, parmblk);
					if (!is_dollar_view && (NULL != gvnh_reg->gvspan))
					{	/* Global spans multiple regions. Make sure gv_targets corresponding to ALL
						 * spanned regions are allocated so NOISOLATION status can be set in all of
						 * them even if the corresponding regions are not open yet. Do this only for
						 * VIEW "NOISOLATION" commands which change the noisolation characteristic.
						 * $VIEW("NOISOLATION") only examines the characteristics and so no need to
						 * allocate all the gv-targets in that case. Just one is enough.
						 */
						gvnh_spanreg_subs_gvt_init(gvnh_reg, gd_header, parmblk);
					}
				}
			} else
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWGVN, 2, tmpstr.len, tmpstr.addr);
			break;
		case VTP_LVN:
			if (NULL == parm)
				rts_error_csa(CSA_ARG(NULL)
					VARLSTCNT(4) ERR_VIEWARGCNT, 2, strlen((const char *)vtp->keyword), vtp->keyword);
			if (0 < parm->str.len)
			{
				lvent.var_name.addr = parm->str.addr;
				lvent.var_name.len = parm->str.len;
				if (lvent.var_name.len > MAX_MIDENT_LEN)
					lvent.var_name.len = MAX_MIDENT_LEN;
				if (!valid_mname(&lvent.var_name))
				{
					format2zwr((sm_uc_ptr_t)parm->str.addr, parm->str.len, global_names, &n);
					rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, n, global_names);
				}
			} else
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, parm->str.len, parm->str.addr);
			/* Now look up the name.. */
			COMPUTE_HASH_MNAME(&lvent);
			if ((tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &lvent)) && (NULL != tabent->value))
				parmblk->value = (mval *)tabent->value;	/* Return lv_val ptr */
			else
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_VIEWLVN, 2, parm->str.len, parm->str.addr);
			break;
		default:
			assertpro(FALSE && vtp_parm);
	}
}
Пример #6
0
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL
 * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the
 * very last level.
 *
 * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled
 * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error.
 */
int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc)
{
	ht_ent_mname	*tabent;
	lv_val		*inuse_lv;
	int		elemindx;
	mv_stent	*mvc;
	lvscan_blk	*lvscan, *newlvscan;
	lvTree		*lvt_child;
	boolean_t	var_cloned;

	assert(curr_lv);
	assert(LV_IS_BASE_VAR(curr_lv));
	assert(curr_lv->tp_var);
	DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr"  save_lv: 0x"lvaddr"\n",
		 restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv));
	DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr
		 " symval: 0x"lvaddr"/0x"lvaddr"\n",
		 tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval));

	/* First get the stack in the position where we can actually process this entry. Need to make sure we are processing
	 * the symbol table we need to be processing so unwind enough stuff to get there.
	 */
	if (curr_symval != tp_pointer->sym)
	{	/* Unwind as many stackframes as are necessary up to the max */
		while(curr_symval != tp_pointer->sym && frame_pointer < tp_pointer->fp)
		{
#			ifdef GTM_TRIGGER
			if (SFT_TRIGR & frame_pointer->type)
			{	/* We have encountered a trigger base frame. We cannot unroll it because there are C frames
				 * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so
				 * it can unroll the base frame and rethrow the error to properly unroll the C stack.
				 */
				*tprestart_rc = ERR_TPRETRY;
				tprestart_state = TPRESTART_STATE_TPUNW;
				DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - "
					  "rethrowing\n"));
				return -1;
			}
#			endif
			op_unwind();
		}
		if (curr_symval != tp_pointer->sym)
		{	/* Unwind as many mv_stents as are necessary up to the max */
			mvc = mv_chain;
			while(curr_symval != tp_pointer->sym && mvc < tp_pointer->mvc)
			{
				unw_mv_ent(mvc);
				mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc);
			}
			mv_chain = mvc;
			/* Final check */
			if (curr_symval != tp_pointer->sym)
				GTMASSERT;
		}
	}
	var_cloned = curr_lv->tp_var->var_cloned;
	if (var_cloned)
	{	/* Var/tree has been copied (and modified) -- see about restoring it */
		DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n"));
		if (NULL != restore_ent->key.var_name.addr)
		{	/* Restore data into a named variable (hash table entry)
			 * Step 1 -- find its hash table address to see what lv_val is there now.
			 */
			tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key);
			assert(tabent);
			/* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val
			 * currently in use. Decrement its use count (which will delete it and the tree if
			 * it is no longer used) and replace with desired previous lv_val whose use count
			 * was incremented when it was saved.
			 */
			if (curr_lv != (inuse_lv = (lv_val *)tabent->value))
			{
				if (inuse_lv)
					DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE);
				DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n",
					 tabent, tabent->value, curr_lv));
				tabent->value = (void *)curr_lv;
				INCR_TREFCNT(curr_lv);			/* Back in the hash table, bump its reference */
			}
		} /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */
		/* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value.
		 * Get rid of its current tree if any.
		 */
		if (lvt_child = LV_GET_CHILD(curr_lv))	/* Note assignment */
		{
			DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv));
			assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child));
			LV_CHILD(curr_lv) = NULL;	/* prevent recursion due to alias containers */
			lv_killarray(lvt_child, FALSE);
		}
		/* Step 4:  Copy in the needed fields from the saved flavor lv_val back to curr_lv.
		 * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date.
		 */
		DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n",
			save_lv, curr_lv));
		/* The following is optimized to do the initialization of just the needed structure members. For that it assumes a
		 * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will
		 * automatically show an issue here and cause the below initialization to be accordingly reworked.
		 */
		assert(0 == OFFSETOF(lv_val, v));
		assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs));
		assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats));
		assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont));
		assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark));
		assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var));
		assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val));
		/* save_lv -> curr_lv Copy begin */
		curr_lv->v = save_lv->v;
		curr_lv->ptrs = save_lv->ptrs;
		assert(0 < curr_lv->stats.trefcnt);	/* No need to copy "stats" as curr_lv is more uptodate */
		assert(0 < curr_lv->stats.crefcnt);
		assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont)));
		/* lv_val->has_aliascont/lvmon_mark all initialized in one shot.
		 * Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too.
		 * On 64-bit platforms, this is 1 8-byte move instead of 2 4-byte moves (the latter means more instructions).
		 * Even though lvmon_mark is maintained only if DEBUG_ALIAS is #defined, we do the copy here unconditionally
		 * because on 64-bit platforms, a 4-byte copy (of just has_aliascont) is the same as an 8-byte copy
		 * (of has_aliascont and lvmon_mark). On 32-bit platforms, it saves us one copy but we dont worry about it much.
		 */
		GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&curr_lv->has_aliascont));)
		NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&curr_lv->has_aliascont));)
Пример #7
0
void	op_indget(mval *dst, mval *target, mval *value)
{
	icode_str	indir_src;
	int		rval;
	ht_ent_mname	*tabent;
	mstr		*obj, object;
	oprtype		v;
	triple		*s, *src, *oldchain, tmpchain, *r, *triptr;
	var_tabent	targ_key;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if ((TREF(ind_source_sp) >= TREF(ind_source_top)) || (TREF(ind_result_sp) >= TREF(ind_result_top)))
		rts_error(VARLSTCNT(1) ERR_INDMAXNEST); /* mdbcondition_handler resets ind_result_sp & ind_source_sp */
	MV_FORCE_DEFINED(value);
	MV_FORCE_STR(target);
	indir_src.str = target->str;
	indir_src.code = indir_get;
	if (NULL == (obj = cache_get(&indir_src)))
	{
		obj = &object;
		if (valid_mname(&target->str))
		{
			targ_key.var_name = target->str;
			COMPUTE_HASH_MNAME(&targ_key);
			tabent = lookup_hashtab_mname(&curr_symval->h_symtab, &targ_key);
			if (!tabent || !LV_IS_VAL_DEFINED(tabent->value))
				*dst = *value;
			else
				*dst = ((lv_val *)tabent->value)->v;
			dst->mvtype &= ~MV_ALIASCONT;	/* Make sure alias container property does not pass */
			return;
		}
		comp_init(&target->str);
		src = newtriple(OC_IGETSRC);
		switch (TREF(window_token))
		{
		case TK_IDENT:
			if (EXPR_FAIL != (rval = lvn(&v, OC_SRCHINDX, 0)))	/* NOTE assignment */
			{
				s = newtriple(OC_FNGET2);
				s->operand[0] = v;
				s->operand[1] = put_tref(src);
			}
			break;
		case TK_CIRCUMFLEX:
			if (EXPR_FAIL != (rval = gvn()))			/* NOTE assignment */
			{
				r = newtriple(OC_FNGVGET1);
				s = newtriple(OC_FNGVGET2);
				s->operand[0] = put_tref(r);
				s->operand[1] = put_tref(src);
			}
			break;
		case TK_ATSIGN:
			TREF(saw_side_effect) = TREF(shift_side_effects);
			if (TREF(shift_side_effects) && (GTM_BOOL == TREF(gtm_fullbool)))
			{
				dqinit(&tmpchain, exorder);
				oldchain = setcurtchain(&tmpchain);
				if (EXPR_FAIL != (rval = indirection(&v)))	/* NOTE assignment */
				{
					s = newtriple(OC_INDGET);
					s->operand[0] = v;
					s->operand[1] = put_tref(src);
					newtriple(OC_GVSAVTARG);
					setcurtchain(oldchain);
					dqadd(TREF(expr_start), &tmpchain, exorder);
					TREF(expr_start) = tmpchain.exorder.bl;
					triptr = newtriple(OC_GVRECTARG);
					triptr->operand[0] = put_tref(TREF(expr_start));
				} else
					setcurtchain(oldchain);
			} else
			{
				if (EXPR_FAIL != (rval = indirection(&v)))	/* NOTE assignment */
				{
					s = newtriple(OC_INDGET);
					s->operand[0] = v;
					s->operand[1] = put_tref(src);
				}
			}
			break;
		default:
			stx_error(ERR_VAREXPECTED);
			rval = EXPR_FAIL;
			break;
		}
		v = put_tref(s);
		if (EXPR_FAIL == comp_fini(rval, obj, OC_IRETMVAL, &v, target->str.len))
			return;
		indir_src.str.addr = target->str.addr;
		cache_put(&indir_src, obj);
		/* Fall into code activation below */
	}
	*(TREF(ind_result_sp))++ = dst;
	*(TREF(ind_source_sp))++ = value;
	comp_indr(obj);
	return;
}
Пример #8
0
void gv_bind_name(gd_addr *addr, mstr *targ)
{
	gd_binding		*map;
	ht_ent_mname		*tabent;
	mname_entry		 gvent;
	int			res;
	boolean_t		added;
	enum db_acc_method	acc_meth;
	gd_region		*reg;
	gvnh_reg_t		*gvnh_reg;
	int			keylen;
	char			format_key[MAX_MIDENT_LEN + 1];	/* max key length + 1 byte for '^' */
	gv_namehead		*tmp_gvt;
	sgmnt_addrs		*csa;

	gd_map = addr->maps;
	gd_map_top = gd_map + addr->n_maps;
	gvent.var_name.addr = targ->addr;
	gvent.var_name.len = MIN(targ->len, MAX_MIDENT_LEN);
	COMPUTE_HASH_MNAME(&gvent);
	if ((NULL != (tabent = lookup_hashtab_mname((hash_table_mname *)addr->tab_ptr, &gvent)))
		&& (NULL != (gvnh_reg = (gvnh_reg_t *)tabent->value)))
	{
		reg = gvnh_reg->gd_reg;
		if (!reg->open)
		{
			gv_init_reg(reg);	/* could modify gvnh_reg->gvt if multiple regions map to same db file */
			assert(0 == gvnh_reg->gvt->clue.end);
		}
		gv_target = gvnh_reg->gvt;
		gv_cur_region = reg;
		acc_meth = gv_cur_region->dyn.addr->acc_meth;
	} else
	{
		map = gd_map + 1;	/* get past local locks */
		for (; (res = memcmp(gvent.var_name.addr, &(map->name[0]), gvent.var_name.len)) >= 0; map++)
		{
			assert(map < gd_map_top);
			if (0 == res && 0 != map->name[gvent.var_name.len])
				break;
		}
		if (!map->reg.addr->open)
			gv_init_reg(map->reg.addr);
		gv_cur_region = map->reg.addr;
		acc_meth = gv_cur_region->dyn.addr->acc_meth;
		if ((dba_cm == acc_meth) || (dba_usr == acc_meth))
		{
			tmp_gvt = malloc(SIZEOF(gv_namehead) + gvent.var_name.len);
			memset(tmp_gvt, 0, SIZEOF(gv_namehead) + gvent.var_name.len);
			tmp_gvt->gvname.var_name.addr = (char *)tmp_gvt + SIZEOF(gv_namehead);
			tmp_gvt->nct = 0;
			tmp_gvt->collseq = NULL;
			tmp_gvt->regcnt = 1;
			memcpy(tmp_gvt->gvname.var_name.addr, gvent.var_name.addr, gvent.var_name.len);
			tmp_gvt->gvname.var_name.len = gvent.var_name.len;
			tmp_gvt->gvname.hash_code = gvent.hash_code;
		} else
		{
			assert(gv_cur_region->max_key_size <= MAX_KEY_SZ);
			tmp_gvt = (gv_namehead *)targ_alloc(gv_cur_region->max_key_size, &gvent, gv_cur_region);
		}
		gvnh_reg = (gvnh_reg_t *)malloc(SIZEOF(gvnh_reg_t));
		gvnh_reg->gvt = tmp_gvt;
		gvnh_reg->gd_reg = gv_cur_region;
		if (NULL != tabent)
		{	/* Since the global name was found but gv_target was null and now we created a new gv_target,
			 * the hash table key must point to the newly created gv_target->gvname. */
			tabent->key = tmp_gvt->gvname;
			tabent->value = (char *)gvnh_reg;
		} else
		{
			added = add_hashtab_mname((hash_table_mname *)addr->tab_ptr, &tmp_gvt->gvname, gvnh_reg, &tabent);
			assert(added);
		}
		gv_target = tmp_gvt;	/* now that any error possibilities (out-of-memory issues in malloc/add_hashtab_mname)
					 * are all done, it is safe to set gv_target. Setting it before could casue gv_target
					 * and gv_currkey to get out of sync in case of an error condition.
					 */
	}
	if ((keylen = gvent.var_name.len + 2) > gv_cur_region->max_key_size)	/* caution: embedded assignment of "keylen" */
	{
		assert(ARRAYSIZE(format_key) >= (1 + gvent.var_name.len));
		format_key[0] = '^';
		memcpy(&format_key[1], gvent.var_name.addr, gvent.var_name.len);
		csa = &FILE_INFO(gv_cur_region)->s_addrs;
		rts_error_csa(CSA_ARG(csa) VARLSTCNT(10) ERR_KEY2BIG, 4, keylen, (int4)gv_cur_region->max_key_size,
			REG_LEN_STR(gv_cur_region), ERR_GVIS, 2, 1 + gvent.var_name.len, format_key);
	}
	memcpy(gv_currkey->base, gvent.var_name.addr, gvent.var_name.len);
	gv_currkey->base[gvent.var_name.len] = 0;
	gvent.var_name.len++;
	gv_currkey->base[gvent.var_name.len] = 0;
	gv_currkey->end = gvent.var_name.len;
	gv_currkey->prev = 0;
	change_reg();
	return;
}
Пример #9
0
/* Routine to eliminate the zlinked trigger code for a given trigger about to be deleted. Operations performed
 * differ depending on platform type (shared binary or not).
 */
void gtm_trigger_cleanup(gv_trigger_t *trigdsc)
{
	rtn_tabent	*rbot, *mid, *rtop;
	mident		*rtnname;
	rhdtyp		*rtnhdr;
	textElem	*telem;
	int		comp, size;
	stack_frame	*fp, *fpprev;
	mname_entry	key;
	ht_ent_mname    *tabent;
	routine_source	*src_tbl;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* First thing to do is release trigger source field if it exists */
	if (0 < trigdsc->xecute_str.str.len)
	{
		free(trigdsc->xecute_str.str.addr);
		trigdsc->xecute_str.str.len = 0;
		trigdsc->xecute_str.str.addr = NULL;
	}
	/* Next thing to do is find the routine header in the rtn_names list so we can remove it. */
	rtnname = &trigdsc->rtn_desc.rt_name;
	rtnhdr = trigdsc->rtn_desc.rt_adr;
	rbot = rtn_names;
	rtop = rtn_names_end;
	for (;;)
	{	/* See if routine exists in list via a binary search which reverts to serial
		   search when # of items drops below the threshold S_CUTOFF.
		*/
		if ((rtop - rbot) < S_CUTOFF)
		{
			comp = -1;
			for (mid = rbot; mid <= rtop ; mid++)
			{
				MIDENT_CMP(&mid->rt_name, rtnname, comp);
				if (0 == comp)
					break;
				if (0 < comp)
					GTMASSERT;	/* Routine should be found */
			}
			break;
		} else
		{	mid = rbot + (rtop - rbot)/2;
			MIDENT_CMP(&mid->rt_name, rtnname, comp);
			if (0 == comp)
				break;
			else if (0 > comp)
			{
				rbot = mid + 1;
				continue;
			} else
			{
				rtop = mid - 1;
				continue;
			}
		}
	}
#	ifdef DEBUG
	assert(rtnhdr == mid->rt_adr);
	/* Verify trigger routine we want to remove is not currently active. If it is, we need to GTMASSERT.
	 * Triggers are not like regular routines since they should only ever be referenced from the stack during a
	 * transaction. Likewise, we should only ever load the triggers as the first action in that transaction.
	 */
	for (fp = frame_pointer; fp ; fp = fpprev)
	{
		fpprev = fp->old_frame_pointer;
#		ifdef GTM_TRIGGER
		if (NULL != fpprev && SFT_TRIGR & fpprev->type)
			fpprev = *(stack_frame **)(fpprev + 1);
#		endif
		/* Only one possible version of a trigger routine */
		assert(USHBIN_ONLY(NULL) NON_USHBIN_ONLY(fp->rvector) == OLD_RHEAD_ADR(CURRENT_RHEAD_ADR(fp->rvector)));
		assert(fp->rvector != rtnhdr);
	}
#	endif
	/* Remove break points in this routine before rmv from rtntbl */
	zr_remove(rtnhdr, BREAKMSG);
	/* Release any $TEXT() info this trigger has loaded */
	if (NULL != (TREF(rt_name_tbl)).base)
	{
		key.var_name = mid->rt_name;
		COMPUTE_HASH_MNAME(&key);
		if (NULL != (tabent = lookup_hashtab_mname(TADR(rt_name_tbl), &key)))	/* note assignment */
		{	/* We have a hash entry. Whether it has a value or not, it has a key name (if this is
			 * the first time this trigger is being deleted) that may be pointing into the routine we
			 * are about to remove. Migrate this key name to the stringpool.
			 */
			s2pool(&tabent->key.var_name);
			if (NULL != tabent->value)
			{	/* Has value, release the source. Entries and source are malloc'd in two blocks on UNIX */
				src_tbl = (routine_source *)tabent->value;
				if (NULL != src_tbl->srcbuff)
					free(src_tbl->srcbuff);
				free(src_tbl);
				tabent->value = NULL;
			}
		}
	}
	/* Remove the routine from the rtn_table */
	size = INTCAST((char *)rtn_names_end - (char *)mid);
	if (0 < size)
		memmove((char *)mid, (char *)(mid + 1), size);	/* Remove this routine name from sorted table */
	rtn_names_end--;
	urx_remove(rtnhdr);					/* Remove any unresolved entries */
#	ifdef USHBIN_SUPPORTED
	stp_move((char *)rtnhdr->literal_text_adr,
		 (char *)(rtnhdr->literal_text_adr + rtnhdr->literal_text_len));
	GTM_TEXT_FREE(rtnhdr->ptext_adr);			/* R/O releasable section */
	free(rtnhdr->literal_adr);				/* R/W releasable section part 1 */
	free(rtnhdr->linkage_adr);				/* R/W releasable section part 2 */
	free(rtnhdr->labtab_adr);				/* Usually non-releasable but triggers don't have labels so
								 * this is just cleaning up a dangling null malloc
								 */
	free(rtnhdr);
#	else
#		if (!defined(__linux__) && !defined(__CYGWIN__)) || !defined(__i386) || !defined(COMP_GTA)
#			error Unsupported NON-USHBIN platform
#		endif
	/* For a non-shared binary platform we need to get an approximate addr range for stp_move. This is not
	 * done when a routine is replaced on these platforms but in this case we are able to due to the isolated
	 * environment if we only take the precautions of migrating potential literal text which may have been
	 * pointed to by any set environment variables.
	 * In this format, the only platform we support currently is Linux-x86 (i386) which uses GTM_TEXT_ALLOC
	 * to allocate special storage for it to put executable code in. We can access the storage header for
	 * this storage and find out how big it is and use that information to give stp_move a good range since
	 * the literal segment occurs right at the end of allocated storage (for which there is no pointer
	 * in the fileheader).
	 */
	telem = (textElem *)((char *)rtnhdr - offsetof(textElem, userStorage));
	assert(TextAllocated == telem->state);
	stp_move((char *)LNRTAB_ADR(rtnhdr) + (rtnhdr->lnrtab_len * SIZEOF(lnr_tabent)),
		 (char *)rtnhdr + telem->realLen);
	GTM_TEXT_FREE(rtnhdr);
#	endif
}