/****************************************************************
 *								*
 * Copyright (c) 2003-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 "gtm_stdio.h"

#include "gdsroot.h"
#include "gdsblk.h"
#include "gdsbt.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "buddy_list.h"
#include "hashtab_int4.h"	/* needed for muprec.h */
#include "hashtab_int8.h"	/* needed for muprec.h */
#include "hashtab_mname.h"	/* needed for muprec.h */
#include "muprec.h"
#include "jnl_typedef.h"
#include "copy.h"
#include "min_max.h"
#include "format_targ_key.h"
#include "mlkdef.h"
#include "zshow.h"
#include "mur_jnl_ext.h"
#include "op.h"
#include "real_len.h"		/* for real_len() prototype */
#include "gtmmsg.h"

GBLREF	gv_key		*gv_currkey;
GBLREF 	mur_gbls_t	murgbl;
GBLREF	mur_opt_struct	mur_options;
GBLREF	boolean_t	is_updproc;
GBLREF	char		muext_code[][2];
LITREF	char		*jrt_label[JRT_RECTYPES];

error_def(ERR_JNLBADRECFMT);
error_def(ERR_MUINFOUINT4);
error_def(ERR_TEXT);

#define LAB_LEN 	7
#define LAB_TERM	"\\"
#define LAB_TERM_SZ	(SIZEOF(LAB_TERM) - 1)

/* This routine formats and outputs journal extract records
   corresponding to M SET, KILL, ZKILL, TSTART, ZTSTART, and ZTRIGGER commands, $ZTRIGGER function (LGTRIG) and $ZTWORMHOLE */
void	mur_extract_set(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	enum jnl_record_type	rectype;
	int			max_blen, actual, extract_len, val_extr_len, val_len;
	char			*val_ptr, *ptr, *buff;
	jnl_string		*keystr;
	boolean_t		do_format2zwr, is_ztstart, jnlext_write_done;

	if (!mur_options.detail)
		extract_len = 0;
	else
		EXT_DET_COMMON_PREFIX(jctl);
	rectype = (enum jnl_record_type)rec->prefix.jrec_type;
	if (IS_FUPD_TUPD(rectype))
	{
		if (!mur_options.detail)
		{
			if (IS_TUPD(rectype))
			{
				EXT2BYTES(&muext_code[MUEXT_TSTART][0]); /* TSTART */
				is_ztstart = FALSE;
			} else /* if (IS_FUPD(rectype)) */
			{
				EXT2BYTES(&muext_code[MUEXT_ZTSTART][0]); /* ZTSTART */
				is_ztstart = TRUE;
			}
		} else
		{
			if (IS_TUPD(rectype))
			{
				strcpy(murgbl.extr_buff + extract_len, "TSTART \\");
				is_ztstart = FALSE;
			} else /* if (IS_FUPD(rectype)) */
			{
				strcpy(murgbl.extr_buff + extract_len, "ZTSTART\\");
				is_ztstart = TRUE;
			}
			extract_len = STRLEN(murgbl.extr_buff);
		}
		EXTTIME(rec->prefix.time);
		EXTQW(rec->prefix.tn);
		if (mur_options.detail)
			EXTINT(rec->prefix.checksum);
		EXTPID(plst);
		EXTQW(rec->jrec_set_kill.token_seq.jnl_seqno);
		if (!is_ztstart)
			EXT_STRM_SEQNO(rec->jrec_set_kill.strm_seqno);
		jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
		jnlext_write_done = TRUE;
	} else
		jnlext_write_done = FALSE;
	/* Output the SET or KILL or ZKILL or ZTWORMHOLE or LGTRIG or ZTRIG record */
	if (!mur_options.detail)
	{
		extract_len = 0;
		if (IS_SET(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_SET][0]);
		} else if (IS_KILL(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_KILL][0]);
		} else if (IS_ZKILL(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_ZKILL][0]);
		} else if (IS_ZTWORM(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_ZTWORM][0]);
		} else if (IS_LGTRIG(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_LGTRIG][0]);
		} else if (IS_ZTRIG(rectype))
		{
			EXT2BYTES(&muext_code[MUEXT_ZTRIG][0]);
		} else
			assert(FALSE);	/* The assert will disappear in pro but not the ";" to properly terminate the else */
	} else
	{
		if (IS_FUPD_TUPD(rectype))
		{
			memcpy(murgbl.extr_buff, "                       ", 23);
			extract_len = 23;
		} else
			extract_len = STRLEN(murgbl.extr_buff);
		strcpy(murgbl.extr_buff + extract_len, "       \\");
		memcpy(murgbl.extr_buff + extract_len, jrt_label[rectype], LAB_LEN);
		extract_len += LAB_LEN;
		memcpy(murgbl.extr_buff + extract_len, LAB_TERM, LAB_TERM_SZ);
		extract_len += LAB_TERM_SZ;
	}
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	EXTQW(rec->jrec_set_kill.token_seq.token);
	assert(IS_SET_KILL_ZKILL_ZTWORM_LGTRIG_ZTRIG(rectype));
	assert(&rec->jrec_set_kill.strm_seqno == &rec->jrec_ztworm.strm_seqno);
	assert(&rec->jrec_set_kill.strm_seqno == &rec->jrec_lgtrig.strm_seqno);
	EXT_STRM_SEQNO(rec->jrec_set_kill.strm_seqno);
	assert(&rec->jrec_set_kill.update_num == &rec->jrec_ztworm.update_num);
	assert(&rec->jrec_set_kill.update_num == &rec->jrec_lgtrig.update_num);
	EXTINT(rec->jrec_set_kill.update_num);
	do_format2zwr = FALSE;
	if (IS_SET_KILL_ZKILL_ZTRIG(rectype))
	{
		keystr = (jnl_string *)&rec->jrec_set_kill.mumps_node;
		EXTINT(keystr->nodeflags);
		buff = &murgbl.extr_buff[extract_len];
		max_blen = MIN(MAX_ZWR_KEY_SZ, murgbl.max_extr_record_length - extract_len);
		assert(MAX_ZWR_KEY_SZ == max_blen);	/* We allocated enough for key and data expansion for ZWR format */
		ptr = (char *)format_targ_key((uchar_ptr_t)buff, max_blen, gv_currkey, TRUE);
		assert(NULL != ptr);
		if (NULL != ptr)
			extract_len += (int)(ptr - &murgbl.extr_buff[extract_len]);
		if (IS_SET(rectype))
		{
			murgbl.extr_buff[extract_len++] = '=';
			val_ptr = &keystr->text[keystr->length];
			GET_MSTR_LEN(val_len, val_ptr);
			val_ptr += SIZEOF(mstr_len_t);
			do_format2zwr = TRUE;
		}
	} else if (IS_ZTWORM(rectype) || IS_LGTRIG(rectype))
	{
		assert(&rec->jrec_ztworm.ztworm_str == &rec->jrec_lgtrig.lgtrig_str);
		keystr = (jnl_string *)&rec->jrec_ztworm.ztworm_str;
		val_len = keystr->length;
		val_ptr = &keystr->text[0];
		do_format2zwr = TRUE;
	}
	if (do_format2zwr)
	{
		if (ZWR_EXP_RATIO(val_len) <= murgbl.max_extr_record_length - extract_len)
		{
			ptr = &murgbl.extr_buff[extract_len];
			val_extr_len = murgbl.max_extr_record_length - extract_len;
			format2zwr((sm_uc_ptr_t)val_ptr, val_len, (unsigned char *)ptr, &val_extr_len);
			extract_len += val_extr_len;
		} else
		{
			gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(9) ERR_JNLBADRECFMT,
				3, jctl->jnl_fn_len, jctl->jnl_fn, jctl->rec_offset,
				ERR_TEXT, 2, LEN_AND_LIT("Length of the record is too high for zwr format"));
			if (mur_options.verbose || mur_options.detail)
			{
				gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUINFOUINT4, 4,
					LEN_AND_LIT("After max expansion record length"),
					ZWR_EXP_RATIO(val_len), ZWR_EXP_RATIO(val_len));
				gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_MUINFOUINT4, 4, LEN_AND_LIT("Buffer size"),
					murgbl.max_extr_record_length - extract_len,
					murgbl.max_extr_record_length - extract_len);
			}
			assert(FALSE);
		}
	}
	murgbl.extr_buff[extract_len++] = '\\';
	jnlext_write(jctl, (!jnlext_write_done ? rec : NULL), recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_null(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int			extract_len;
	char			*ptr;

	extract_len = 0;
	if (!mur_options.detail)
	{
		EXT2BYTES(&muext_code[MUEXT_NULL][0]);
	} else
	{
		EXT_DET_PREFIX(jctl);
	}
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	EXTQW(rec->jrec_null.jnl_seqno);
	EXT_STRM_SEQNO(rec->jrec_null.strm_seqno);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_align(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	extract_len;
	char	*ptr;

	if (!mur_options.detail)
		return;
	EXT_DET_PREFIX(jctl);
	EXTTIME(rec->prefix.time);
	EXTQW(0);
	if (mur_options.detail)
		EXTINT(((struct_jrec_align *)rec)->checksum);	/* Note: Cannot use "rec->prefix.checksum" as JRT_ALIGN
								 * has checksum at a different offset than other records.
								 */
	EXTPID(plst);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_blk(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	extract_len;
	blk_hdr	pblk_head;
	char	*ptr;

	if (!mur_options.detail)
		return;
	EXT_DET_PREFIX(jctl);
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	EXTINT(rec->jrec_pblk.blknum);
	EXTINT(rec->jrec_pblk.bsiz);
	memcpy((char*)&pblk_head, (char*)&rec->jrec_pblk.blk_contents[0], SIZEOF(blk_hdr));
	EXTQW(pblk_head.tn);
	EXTINT(rec->jrec_pblk.ondsk_blkver);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_epoch(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	extract_len, idx;
	seq_num	strm_seqno;
	char	*ptr;

	if (!mur_options.detail)
		return;
	EXT_DET_PREFIX(jctl);
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	assert((0 == rec->jrec_epoch.fully_upgraded) || (1 == rec->jrec_epoch.fully_upgraded));
	EXTQW(rec->jrec_epoch.jnl_seqno);
	EXTINT(rec->jrec_epoch.blks_to_upgrd);
	EXTINT(rec->jrec_epoch.free_blocks);
	EXTINT(rec->jrec_epoch.total_blks);
	EXTINT(rec->jrec_epoch.fully_upgraded); /* actually boolean_t */
	/* Extract upto 16 strm_seqno only if they are non-zero */
	for (idx = 0; idx < MAX_SUPPL_STRMS; idx++)
	{
		strm_seqno = rec->jrec_epoch.strm_seqno[idx];
		if (strm_seqno)
		{
			strm_seqno = SET_STRM_INDEX(strm_seqno, idx);
			EXT_STRM_SEQNO(strm_seqno);
		}
	}
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void    mur_extract_inctn(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
        int		extract_len;
        char		*ptr;
	inctn_detail_t	*detail;
	unsigned short	opcode;

	if (!mur_options.detail)
		return;
	EXT_DET_PREFIX(jctl);
        EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	opcode = rec->jrec_inctn.detail.blknum_struct.opcode;
	EXTINT(opcode);
	/* Any changes to the switch block here (e.g. new inctn opcodes) needs corresponding changes in jnl_write_inctn_rec.c */
	switch (opcode)
	{
		case inctn_bmp_mark_free_gtm:
		case inctn_bmp_mark_free_mu_reorg:
		case inctn_blkupgrd:
		case inctn_blkdwngrd:
		case inctn_blkupgrd_fmtchng:
		case inctn_blkdwngrd_fmtchng:
		case inctn_blkmarkfree:
		case inctn_blkreencrypt:
			EXTINT(rec->jrec_inctn.detail.blknum_struct.blknum);
			break;
		case inctn_gdsfilext_gtm:
		case inctn_gdsfilext_mu_reorg:
		case inctn_db_format_change:
			EXTINT(rec->jrec_inctn.detail.blks2upgrd_struct.blks_to_upgrd_delta);
			break;
		default:
			EXTINT(0);	/* nothing to extract in this case */
			break;
	}
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_eof(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int			extract_len = 0;
	char			*ptr;

	if (!mur_options.detail)
	{
		EXT2BYTES(&muext_code[MUEXT_EOF][0]);
	} else
	{
		EXT_DET_PREFIX(jctl);
	}
	EXTTIME(rec->jrec_eof.prefix.time);
	EXTQW(rec->jrec_eof.prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	EXTQW(rec->jrec_eof.jnl_seqno);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void    mur_extract_trunc(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int		extract_len = 0;
	char		*ptr;

	if (!mur_options.detail)
		return;
	EXT_DET_PREFIX(jctl);
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	EXTINT(rec->jrec_trunc.orig_total_blks);
	EXTINT(rec->jrec_trunc.orig_free_blocks);
	EXTINT(rec->jrec_trunc.total_blks_after_trunc);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_pfin(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	extract_len;
	char	*ptr;

	extract_len = 0;
	if (!mur_options.detail)
	{
		EXT2BYTES(&muext_code[MUEXT_PFIN][0]);
	} else
	{
		EXT_DET_PREFIX(jctl);
	}
	EXTTIME(rec->jrec_pfin.prefix.time);
	EXTQW(rec->jrec_pfin.prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

void	mur_extract_pini(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	extract_len = 0;
	char			*ptr;

	if (!mur_options.detail)
	{
		EXT2BYTES(&muext_code[MUEXT_PINI][0]);
	} else
	{
		EXT_DET_PREFIX(jctl);
	}
	EXTTIME(rec->prefix.time);
	EXTQW(rec->prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	extract_len = extract_process_vector((jnl_process_vector *)&rec->jrec_pini.process_vector[CURR_JPV], extract_len);
	extract_len = extract_process_vector((jnl_process_vector *)&rec->jrec_pini.process_vector[ORIG_JPV], extract_len);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

/* This routine formats and outputs journal extract records corresponding to M TCOMMIT and ZTCOMMIT commands */
void	mur_extract_tcom(jnl_ctl_list *jctl, enum broken_type recstat, jnl_record *rec, pini_list_struct *plst)
{
	int	actual, extract_len = 0;
	char	*ptr;

	if (!mur_options.detail)
	{
		if (rec->prefix.jrec_type == JRT_ZTCOM)
		{
			EXT2BYTES(&muext_code[MUEXT_ZTCOMMIT][0]);
		} else
		{
			EXT2BYTES(&muext_code[MUEXT_TCOMMIT][0]);
		}
	} else
	{
		EXT_DET_PREFIX(jctl);
	}
	EXTTIME(rec->jrec_tcom.prefix.time);
	EXTQW(rec->jrec_tcom.prefix.tn);
	if (mur_options.detail)
		EXTINT(rec->prefix.checksum);
	EXTPID(plst);
	assert(offsetof(struct_jrec_ztcom, token) == offsetof(struct_jrec_tcom, token_seq));
	EXTQW(rec->jrec_tcom.token_seq.jnl_seqno);
	if (JRT_TCOM == rec->prefix.jrec_type)
	{
		EXT_STRM_SEQNO(rec->jrec_tcom.strm_seqno);
		EXTINT(rec->jrec_tcom.num_participants);
		EXTTXT((unsigned char *)&rec->jrec_tcom.jnl_tid[0], SIZEOF(rec->jrec_tcom.jnl_tid));
	} else
		EXTINT(rec->jrec_ztcom.participants);
	jnlext_write(jctl, rec, recstat, murgbl.extr_buff, extract_len);
}

int extract_process_vector(jnl_process_vector *pv, int extract_len)
{
	int			actual;
	char			*ptr;

	/* EXTTIME(MID_TIME(pv->jpv_time)); */
	EXTINT(pv->jpv_pid);
	EXTTXT((unsigned char *)&pv->jpv_node[0], JPV_LEN_NODE);
	EXTTXT((unsigned char *)&pv->jpv_user[0], JPV_LEN_USER);
	EXTTXT((unsigned char *)&pv->jpv_terminal[0], JPV_LEN_TERMINAL);
	EXTINTVMS(pv->jpv_mode);
	EXTTIMEVMS(MID_TIME(pv->jpv_login_time));
	EXTINTVMS(pv->jpv_image_count);
	EXTTXTVMS(pv->jpv_prcnam, JPV_LEN_PRCNAM);
	return extract_len;
}
