/****************************************************************
 *								*
 * Copyright (c) 2001-2015 Fidelity National Information 	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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"

#include "gtm_stdio.h"
#include "gtm_string.h"

#include "toktyp.h"
#include <rtnhdr.h>
#include "stack_frame.h"
#include "indir_enum.h"
#include "cmd_qlf.h"
#include "gtm_caseconv.h"
#include "op.h"
#include "do_indir_do.h"
#include "valid_mname.h"
#include "linktrc.h"

GBLREF	stack_frame		*frame_pointer;
GBLREF	command_qualifier	cmd_qlf;
GBLREF	boolean_t		is_tracing_on;

int do_indir_do(mval *v, unsigned char argcode)
{
	mval		label;
	lnr_tabent	USHBIN_ONLY(*)*addr;
	mident_fixed	ident;
	rhdtyp		*current_rhead;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if (valid_labname(&v->str))
	{
		memcpy(ident.c, v->str.addr, v->str.len);
		if (!(cmd_qlf.qlf & CQ_LOWER_LABELS))
			lower_to_upper((uchar_ptr_t)ident.c, (uchar_ptr_t)ident.c, v->str.len);
		label.mvtype = MV_STR;
		label.str.len = v->str.len;
		label.str.addr = &ident.c[0];
#		ifdef AUTORELINK_SUPPORTED
		DBGINDCOMP((stderr, "do_indir_do: routine resolved to 0x"lvaddr"\n", frame_pointer->rvector));
		TADR(lnk_proxy)->rtnhdr_adr = frame_pointer->rvector;	/* So op_labaddr has access to it */
		op_labaddr(0, &label, 0);
		addr = &TADR(lnk_proxy)->lnr_adr;
#		else
		addr = op_labaddr(frame_pointer->rvector, &label, 0);
#		endif
		current_rhead = CURRENT_RHEAD_ADR(frame_pointer->rvector);
		if (argcode == indir_do)
		{	/* If we aren't in an indirect, exfun_frame() is the best way to copy the stack frame as it does not
			 * require re-allocation of the various tables (temps, linkage, literals, etc). But if we are in an
			 * indirect, the various stackframe fields cannot be copied as the indirect has different values so
			 * re-create the frame from the values in the routine header via new_stack_frame().
			 */
			if (!(frame_pointer->flags & SFF_INDCE))
			{
				if (!is_tracing_on)
					exfun_frame();
				else
					exfun_frame_sp();
			} else
			{
				if (!is_tracing_on)
				{
					new_stack_frame(CURRENT_RHEAD_ADR(frame_pointer->rvector),
#							ifdef HAS_LITERAL_SECT
							(unsigned char *)LINKAGE_ADR(current_rhead),
#							else
							PTEXT_ADR(current_rhead),
#							endif
							USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr))
							/* On non-shared binary calculate the transfer address to be passed to
							 * new_stack_frame as follows:
							 *  1) get the number stored at addr; this is the offset to the line number
							 *     entry
							 *  2) add the said offset to the address of the routine header; this is the
							 *     address of line number entry
							 *  3) dereference the said address to get the line number of the actual
							 *     program
							 *  4) add the said line number to the address of the routine header
							 */
							NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead
								+ *(int4 *)((char *)current_rhead + *addr))));

				} else
				{
					new_stack_frame_sp(CURRENT_RHEAD_ADR(frame_pointer->rvector),
#							   ifdef HAS_LITERAL_SECT
							   (unsigned char *)LINKAGE_ADR(current_rhead),
#							   else
							   PTEXT_ADR(current_rhead),
#							   endif
							   USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr))
							   /* On non-shared binary calculate the transfer address to be passed to
							    * new_stack_frame as follows:
							    *  1) get the number stored at addr; this is the offset to the line
							    *     number entry
							    *  2) add the said offset to the address of the routine header; this is
							    *     the address of line number entry
							    *  3) dereference the said address to get the line number of the actual
							    *     program
							    *  4) add the said line number to the address of the routine header
							    */
							   NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead
								+ *(int4 *)((char *)current_rhead + *addr))));
				}
				return TRUE;
			}
		}
		/* On non-shared binary calculate the mpc pointer similarly to the descriptions above. */
		frame_pointer->mpc =
			USHBIN_ONLY(LINE_NUMBER_ADDR(current_rhead, *addr))
			NON_USHBIN_ONLY((unsigned char *)((char *)current_rhead + *(int4 *)((char *)current_rhead + *addr)));
#		ifdef HAS_LITERAL_SECT
		frame_pointer->ctxt = (unsigned char *)LINKAGE_ADR(current_rhead);
#		else
		frame_pointer->ctxt = PTEXT_ADR(current_rhead);
#		endif
		return TRUE;
	} else
		return FALSE;
}
