/****************************************************************
 *								*
 * Copyright (c) 2001-2019 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_string.h"

#include <rtnhdr.h>
#include "stack_frame.h"
#include "min_max.h"

#define OFFSET_LEN 5

GBLREF	unsigned char	*stackbase, *stacktop;
GBLREF	unsigned short	proc_act_type;
GBLREF	mstr		*err_act;

unsigned char *symb_line(unsigned char *in_addr, unsigned char *out, int max_len, unsigned char **b_line, rhdtyp *routine)
{
	unsigned char	temp[OFFSET_LEN], *adjusted_in_addr;
	lab_tabent	*max_label, *label_table, *last_label;
	lnr_tabent	*line_table, *last_line;
	int4		len, ct, offset, in_addr_offset;
	boolean_t	mpc_reset_to_linestart;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if (!ADDR_IN_CODE(in_addr, routine))
		return out;
	routine = CURRENT_RHEAD_ADR(routine);
	USHBIN_ONLY(
		assert(routine->labtab_adr);
		assert(routine->lnrtab_adr);
		);
	NON_USHBIN_ONLY(
		assert(routine->labtab_ptr >= 0);
		assert(routine->lnrtab_ptr >= 0);
		);
	assert(routine->labtab_len >= 0);
	assert(routine->lnrtab_len >= 0);
	if ((SFT_DEV_ACT == proc_act_type) || ((SFT_ZTRAP == proc_act_type) && (err_act == &((TREF(dollar_ztrap)).str))))
	{	/* This means we got an error while trying to compile the device-exception or ZTRAP string as
		 * part of handling yet another primary error. The primary error would have reset fp->mpc to
		 * the beginning of the line so note this down.
		 */
		mpc_reset_to_linestart = TRUE;
	} else
		mpc_reset_to_linestart = FALSE;
	label_table = LABTAB_ADR(routine);
	last_label = label_table + routine->labtab_len;
	max_label = label_table++;
	adjusted_in_addr = in_addr + (mpc_reset_to_linestart ? 1 : 0);
	while (label_table < last_label)
	{	/* Label table entries are sorted by label name (for faster lookup by indirects using op_labaddr) so we
		 * scan to find all of the label addresses that meet our specification (adjusted addr > label address) and
		 * keep the one with the lowest linenumber offset. This means we have to go through all of them rather than
		 * stop at the first label meeting our address criteria but this is not a high-use module so it is ok.
		 */
		if (adjusted_in_addr > LABEL_ADDR(routine, label_table))
		{	/* Now check if this label is a keeper by checking for minimum LNR_OFFSET */
			if (max_label->LABENT_LNR_OFFSET <= label_table->LABENT_LNR_OFFSET)
				max_label = label_table;
		}
		label_table++;
	}
	line_table = LABENT_LNR_ENTRY(routine, max_label);
	len = MIN(max_label->lab_name.len, max_len);
	if (len)
	{
		memcpy(out, max_label->lab_name.addr, len);
		out += len;
		max_len -= len;
		assert(0 < max_len);
		if (0 == max_len)
			return out;
	}
	offset = 0;
	in_addr_offset = (int4)(adjusted_in_addr - CODE_BASE_ADDR(routine));
	last_line = LNRTAB_ADR(routine);
	last_line += routine->lnrtab_len;
	for ( ; ++line_table < last_line ; offset++)
	{	/* Find first line that is >= input addr. The previous line is the target line.
		 * In addition, ensure we never return NULL label and ZERO offset i.e. no ^MODULENAME.
		 * Line #s start at 1 so in this case, return +1^MODULENAME.
		 * The "(offset || len)" check below takes care of this.
		 */
		if ((in_addr_offset <= *line_table) && (offset || len))
		{
			if (b_line)
				*b_line = LINE_NUMBER_ADDR(routine, (line_table - 1));
			break;
		}
	}
	if (line_table >= last_line && b_line)
		*b_line = LINE_NUMBER_ADDR(routine, (line_table - 1));
	if (offset)
	{
		*out++ = '+';
		for (ct = OFFSET_LEN - 1; ct >= 0 ; ct--)
		{
			temp[ct] = (offset % 10) + '0';
			if ((offset /= 10) == 0)
				break;
		}
		len = MIN(OFFSET_LEN - ct, max_len);
		memcpy (out, &temp[ct], len);
		out += len;
		max_len -= len;
		assert(0 < max_len);
		if (0 == max_len)
			return out;
	}
	*out++ = '^';
	len = MIN(routine->routine_name.len, --max_len);
	memcpy(out, routine->routine_name.addr, len);
	out += len;
	max_len -= len;
	assert(0 <= max_len);
	return out;
}
