/****************************************************************
 *								*
 *	Copyright 2001, 2011 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"

#include "gtm_unistd.h"
#include "gtm_stat.h"
#include "gtm_stdio.h"
#include "gtm_string.h"
#include "gtm_limits.h"
#include "io.h"
#include "iormdef.h"
#include "io_params.h"
#include "eintr_wrappers.h"
#ifdef __MVS__
#include "gtm_zos_io.h"
#include "gtm_zos_chset.h"
#endif

GBLREF io_pair		io_curr_device;
GBLREF	boolean_t	gtm_utf8_mode;
ZOS_ONLY(GBLREF boolean_t	gtm_tag_utf8_as_ascii;)
#ifdef __MVS__
error_def(ERR_BADTAG);
#endif
error_def(ERR_DEVOPENFAIL);
error_def(ERR_TEXT);



LITREF	mstr		chset_names[];
LITREF unsigned char	io_params_size[];

/* WARNING, this routine is called from ioff_open as well as from the dispatch table. */
/* WARNING, this routine is called from iopi_open as well as from the dispatch table. */

short	iorm_open(io_log_name *dev_name, mval *pp, int fd, mval *mspace, int4 timeout)
{
	io_desc		*iod;
 	d_rm_struct	*d_rm;
	off_t		size;
	unsigned char	ch;
	int		fstat_res, width_bytes;
	int4		recsize_before;
	struct stat	statbuf;
	int 		p_offset;
	boolean_t	utf_active, def_recsize_before, text_tag, newversion = FALSE;
	gtm_chset_t	width_chset, dummy_chset;
	long		pipe_buff_size;
#ifdef __MVS__
	int		file_tag, obtained_tag, realfiletag;
	char		*errmsg;
#endif

	iod = dev_name->iod;
	size = 0;
	p_offset = 0;
	assert((params) *(pp->str.addr + p_offset) < (unsigned char)n_iops);
	assert(NULL != iod);
	assert(0 <= iod->state && n_io_dev_states > iod->state);
	assert(rm == iod->type);
	if (dev_never_opened == iod->state)
	{
		iod->dev_sp = (void *)malloc(SIZEOF(d_rm_struct));
		memset(iod->dev_sp, 0, SIZEOF(d_rm_struct));
		d_rm = (d_rm_struct *)iod->dev_sp;
		iod->state = dev_closed;
		d_rm->stream = FALSE;
		iod->width = DEF_RM_WIDTH;
		iod->length = DEF_RM_LENGTH;
		d_rm->recordsize = DEF_RM_RECORDSIZE;
		d_rm->def_width = d_rm->def_recsize = TRUE;
		d_rm->fixed = FALSE;
		d_rm->noread = FALSE;
		d_rm->fifo = FALSE;
		d_rm->pipe = FALSE;
		d_rm->padchar = DEF_RM_PADCHAR;
		d_rm->inbuf = NULL;
		d_rm->outbuf = NULL;
	} else
		d_rm = (d_rm_struct *)iod->dev_sp;
	if (dev_closed == iod->state)
	{
		d_rm->lastop = RM_NOOP;
		d_rm->out_bytes = 0;
		d_rm->crlast = FALSE;
		d_rm->done_1st_read = FALSE;
		d_rm->done_1st_write = FALSE;
		assert(0 <= fd);
		d_rm->fildes = fd;
		FSTAT_FILE(fd, &statbuf, fstat_res);
		if (-1 == fstat_res)
			rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2,
					LEN_AND_LIT("Error in fstat"), errno);
		for (p_offset = 0; iop_eol != *(pp->str.addr + p_offset); )
		{
			if (iop_append == (ch = *(pp->str.addr + p_offset++)))
			{
				if (!d_rm->fifo && !d_rm->pipe && (off_t)-1 == (size = lseek(fd, (off_t)0, SEEK_END)))
					rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
						  ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to end of file"), errno);
				if (0 < statbuf.st_size)
				{	/* Only disable BOM writing if there is something in the file already (not empty) */
					d_rm->done_1st_read = FALSE;
					d_rm->done_1st_write = FALSE;
				}
				break;
			} else if (iop_newversion == ch)
				newversion = TRUE;
			p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 :
					io_params_size[ch]);
		}
		if (!d_rm->fifo && !d_rm->pipe && fd != 0)
		{
			if ((off_t)-1 == (size = lseek(fd, (off_t)0, SEEK_CUR)))
				rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
				  ERR_TEXT, 2, LEN_AND_LIT("Error setting file pointer to the current position"), errno);
			if (size == statbuf.st_size)
				iod->dollar.zeof = TRUE;
		} else
		{
			pipe_buff_size = fpathconf(fd, _PC_PIPE_BUF);
			d_rm->pipe_buff_size = (-1 == pipe_buff_size) ? _POSIX_PIPE_BUF : pipe_buff_size;
		}
		if (1 == fd)
			d_rm->filstr = NULL;
		else if (NULL == (d_rm->filstr = FDOPEN(fd, "r")) && NULL == (d_rm->filstr = FDOPEN(fd, "w")))
			rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io,
				  ERR_TEXT, 2, LEN_AND_LIT("Error in stream open"), errno);
	}
	recsize_before = d_rm->recordsize;
	def_recsize_before = d_rm->def_recsize;
	iorm_use(iod, pp);
	/* Now that recordsize and CHSET parms have been handled (if any), set WIDTH if necessary */
	if (recsize_before != d_rm->recordsize || def_recsize_before != d_rm->def_recsize)
	{	/* record size was specified even if the same value */
		SET_WIDTH_BYTES
		assert(width_bytes <= d_rm->recordsize);	/* or else RECSIZENOTEVEN error would have been issued */
		iod->width = d_rm->recordsize;/* we are guaranteed this width will hold at least one character due to above check */
	}
	/* Now that recordsize and CHSET parms have been handled (if any), allocate the record buffer if needed (utf8 fixed) */
	if (dev_closed == iod->state)
	{
#ifdef __MVS__
		/* need to get file tag info before set policy which can change what is returned */
		if (-1 == gtm_zos_check_tag(fd, &file_tag, &text_tag))
			rts_error(VARLSTCNT(9) ERR_DEVOPENFAIL, 2, dev_name->len, dev_name->dollar_io, ERR_TEXT, 2,
				LEN_AND_LIT("Error in check_tag fstat"), errno);
		SET_CHSET_FROM_TAG(file_tag, iod->file_chset);
		iod->text_flag = text_tag;
		if (!d_rm->pipe && 2 < fd)
		{	/* not for stdin, stdout, stderr or pipes */
			if (iod->newly_created || newversion)
			{	/* tag the file.  The macros also modify text_tag and file_tag. */
				if (d_rm->fifo && (iod->is_ochset_default || d_rm->noread))
				{	/* If FIFO, set tag per ichset if no ochset or READONLY */
					SET_TAG_FROM_CHSET(iod->ichset, iod->file_chset, TRUE);
				} else
				{
					SET_TAG_FROM_CHSET(iod->ochset, iod->file_chset, TRUE);
				}
				iod->file_tag = (unsigned int)file_tag;
				iod->text_flag = text_tag;
				if (-1 == gtm_zos_set_tag(fd, file_tag, text_tag, TAG_FORCE, &realfiletag))
				{
					errmsg = STRERROR(errno);
					rts_error(VARLSTCNT(10) ERR_BADTAG, 4, dev_name->len, dev_name->dollar_io, realfiletag,
						file_tag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg));
				}
				if (gtm_utf8_mode && gtm_tag_utf8_as_ascii && (CHSET_UTF8 == iod->ochset))
					iod->process_chset = iod->ochset;
				else
					iod->process_chset = iod->file_chset;
			} else
			{
				iod->file_tag = (unsigned int)file_tag;	/* save real tag for file */
				if (iod->is_ochset_default || d_rm->noread)
				{	/* set tag per ichset if no ochset or READONLY */
					SET_TAG_FROM_CHSET(iod->ichset, dummy_chset, FALSE);
				} else
				{
					SET_TAG_FROM_CHSET(iod->ochset, dummy_chset, FALSE);
				}
				if (-1 == (obtained_tag = gtm_zos_tag_to_policy(fd, file_tag, &realfiletag)))
				{
					errmsg = STRERROR(errno);
					rts_error(VARLSTCNT(10) ERR_BADTAG, 4, dev_name->len, dev_name->dollar_io, realfiletag,
						file_tag, ERR_TEXT, 2, RTS_ERROR_STRING(errmsg));
				}
				SET_CHSET_FROM_TAG(obtained_tag, iod->process_chset);
			}
		} else
			iod->process_chset = iod->file_chset;	/* stdin, stdout, stderr */
		if (!gtm_utf8_mode || !IS_UTF_CHSET(iod->ochset))
			iod->ochset = CHSET_M;
		if (!gtm_utf8_mode || !IS_UTF_CHSET(iod->ichset))
			iod->ichset = CHSET_M;
		if (CHSET_BINARY == iod->process_chset)
		{
			d_rm->fixed = TRUE;
			iod->wrap = TRUE;
		}
#endif
		if (IS_UTF_CHSET(iod->ichset))
		{
			assert(NULL == d_rm->inbuf);			/* We shouldn't have an existing buffer at this point */
			assert(gtm_utf8_mode);
			d_rm->bufsize = d_rm->fixed ? (d_rm->recordsize + 4) : 20;		/* +4 for one extra utf8 char */
			d_rm->inbuf = malloc(d_rm->bufsize);
			d_rm->inbuf_pos = d_rm->inbuf_top = d_rm->inbuf_off = d_rm->inbuf;	/* Force read first time thru */
		}
		if (IS_UTF16_CHSET(iod->ochset))
		{
			assert(NULL == d_rm->outbuf);			/* We shouldn't have an existing buffer at this point */
			d_rm->outbufsize = d_rm->recordsize + 4;
			d_rm->outbuf = malloc(d_rm->outbufsize);
			d_rm->out_bytes = 0;
		}
	} else
	{	/* check for changed recordsize */
		if (IS_UTF_CHSET(iod->ichset) && (recsize_before != d_rm->recordsize))
		{
			if (NULL != d_rm->inbuf)
				free(d_rm->inbuf);
			d_rm->bufsize = d_rm->fixed ? (d_rm->recordsize + 4) : 20;		/* +4 for one extra utf8 char */
			d_rm->inbuf = malloc(d_rm->bufsize);
			d_rm->inbuf_pos = d_rm->inbuf_top = d_rm->inbuf_off = d_rm->inbuf;	/* Force read first time thru */
		}
		if (IS_UTF16_CHSET(iod->ochset) && (recsize_before != d_rm->recordsize))
		{
			if (NULL != d_rm->outbuf)
				free(d_rm->outbuf);
			d_rm->outbufsize = d_rm->recordsize + 4;
			d_rm->outbuf = malloc(d_rm->outbufsize);
			d_rm->out_bytes = 0;
		}
	}
	iod->state = dev_open;
	return TRUE;
}
