/****************************************************************
 *								*
 * Copyright (c) 2001-2018 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 "mlkdef.h"
#include "zshow.h"
#include "patcode.h"
#include "compiler.h"	/* for CHARMAXARGS */
#include <rtnhdr.h>
#include "stack_frame.h"
#include "mv_stent.h"	/* for POP_MV_STENT */

#ifdef UTF8_SUPPORTED
#include "gtm_utf8.h"
#include "gtm_icu_api.h"	/* U_ISPRINT() needs this header */
#endif

GBLREF	uint4		*pattern_typemask;
GBLREF	boolean_t	gtm_utf8_mode;
GBLREF	mv_stent	*mv_chain;
GBLREF	unsigned char	*stackbase, *stacktop, *msp, *stackwarn;
GBLREF	int		process_exiting;

LITDEF MSTR_CONST(quote, QUOTE);
LITDEF MSTR_CONST(quote_concat, QUOTE_CONCAT);
LITDEF MSTR_CONST(close_paren, CLOSE_PAREN);
LITDEF MSTR_CONST(close_paren_quote, CLOSE_PAREN_QUOTE);

error_def(ERR_STACKOFLOW);
error_def(ERR_STACKCRIT);

/* The following macro prints the last contiguous sequence of printable (graphic) characters
 * that have been processed so far to the zshow buffer. It also resets the size of the next
 * next sequence that may follow */
#define ZWR_PRINTABLE						\
{								\
	graphic_str->str.len = src_cnt - strstart;		\
	graphic_str->str.addr = v->str.addr + strstart;		\
	zshow_output(output, &graphic_str->str);		\
}

/* The routine that does formatting for ZWRITE command.
 * NOTE: this routine does almost the same formatting as format2zwr(). However,
 * the main difference is this routine feeds the formatted results to the zshow
 * output handling routine (zshow_output()) instead of storing in a pre-allocated
 * destination buffer */
void mval_write(zshow_out *output, mval *v, boolean_t flush)
{
        sm_uc_ptr_t	cp;
	uint4		ch;
	int		fastate = 0, ncommas, src_len, src_cnt, strstart, chlen;
	boolean_t	isctl, isill;
	char		*strnext;
	mval		*graphic_str, lmv;

	MV_FORCE_STR(v);
	src_len = v->str.len;
	if (src_len > 0)
	{
		if (val_iscan(v))
		{
			output->flush = flush;
			zshow_output(output, &v->str);
			return;
		}
		fastate = 0;
		strstart = 0;

		if (!process_exiting)
		{ /* Only if not exiting in case we are called from mdb_condition_handler with stack overflow */
			PUSH_MV_STENT(MVST_MVAL);
			graphic_str = &mv_chain->mv_st_cont.mvs_mval;
		} else
			graphic_str = &lmv;
		graphic_str->mvtype = MV_STR;
		graphic_str->str.len = 0; /* initialize len in case stp_gcol gets called before actual set of len occurs below */

		/* Note that throughout this module, we use v->str.addr[xxx] to access the input string instead of
		 * maintaining a pointer variable. This is because we call zshow_output at lot of places and each
		 * of them can invoke stp_gcol in turn changing the value of v->str.addr.
		 */
		for (src_cnt = 0; src_cnt < src_len; src_cnt += chlen)
		{
			if (!gtm_utf8_mode)
			{
				ch = (unsigned char)v->str.addr[src_cnt];
				isctl = ((pattern_typemask[ch] & PATM_C) != 0);
				isill = FALSE;
				chlen = 1;
			}
			UTF8_ONLY(
			else
			{
				strnext = (char *)UTF8_MBTOWC(&v->str.addr[src_cnt], &v->str.addr[src_len], ch);
				isill = (WEOF == ch) ? (ch = (unsigned char)v->str.addr[src_cnt], TRUE) : FALSE;
				if (!isill)
					isctl = !U_ISPRINT(ch);
				chlen = (int)(strnext - &v->str.addr[src_cnt]);
			}
			)
			switch(fastate)
			{
				case 0:	/* beginning of the string */
				case 1: /* beginning of a new substring followed by a graphic character */
					if (isill)
					{
						if (src_cnt > 0)
						{ 	/* close previous string with quote and prepare for
							   concatenation */
							ZWR_PRINTABLE;
							zshow_output(output, &quote_concat);
						}
						mval_nongraphic(output, LIT_AND_LEN(DOLLARZCH), ch);
						fastate = 3;
						ncommas = 0;
					} else if (isctl)
					{
						if (src_cnt > 0)
						{	/* close previous string with quote and prepare for
							   concatenation */
							ZWR_PRINTABLE;
							zshow_output(output, &quote_concat);
						}
						mval_nongraphic(output, LIT_AND_LEN(DOLLARCH), ch);
						fastate = 2;
						ncommas = 0;
					} else
					{	/* graphic characters */
						if (0 == fastate) /* the initial quote in the beginning */
						{
							zshow_output(output, &quote);
							fastate = 1;
						}
						if ('"' == ch)
						{
							ZWR_PRINTABLE;
							zshow_output(output, &quote);
							strstart = src_cnt;
						}
					}
					break;
				case 2: /* subsequent characters following a non-graphic character in the form
					   of $CHAR(x,) */
					if (isill)
					{
						mval_nongraphic(output, LIT_AND_LEN(CLOSE_PAREN_DOLLARZCH), ch);
						fastate = 3;
					} else if(isctl)
					{
						ncommas++;
						if (CHARMAXARGS == ncommas)
						{
							ncommas = 0;
							mval_nongraphic(output, LIT_AND_LEN(CLOSE_PAREN_DOLLARCH), ch);
						} else
							mval_nongraphic(output, LIT_AND_LEN(COMMA), ch);
					} else
					{
						zshow_output(output, &close_paren_quote);
						if ('"' == ch)
							zshow_output(output, &quote);
						strstart = src_cnt;
						fastate = 1;
					}
					break;
				case 3: /* subsequent characters following an illegal character in the form of $ZCHAR(x,) */
					if(isill)
					{
						ncommas++;
						if (CHARMAXARGS == ncommas)
						{
							ncommas = 0;
							mval_nongraphic(output, LIT_AND_LEN(CLOSE_PAREN_DOLLARZCH), ch);
						} else
							mval_nongraphic(output, LIT_AND_LEN(COMMA), ch);
					} else if (isctl)
					{
						mval_nongraphic(output, LIT_AND_LEN(CLOSE_PAREN_DOLLARCH), ch);
						fastate = 2;
					} else
					{
						zshow_output(output, &close_paren_quote);
						if ('"' == ch)
							zshow_output(output, &quote);
						strstart = src_cnt;
						fastate = 1;
					}
					break;
				default:
					assert(FALSE);
					break;
			}
		}

		/* close up */
		switch(fastate)
		{
			case 1:
				ZWR_PRINTABLE;
				zshow_output(output, &quote);
				break;
			case 2:
			case 3:
				zshow_output(output, &close_paren);
				break;
			default:
				assert(FALSE);
				break;
		}
		if (!process_exiting)
		{
			POP_MV_STENT();
		}
	} else
	{
		zshow_output(output, &quote);
		zshow_output(output, &quote);
	}
	if (flush)
	{
		output->flush = TRUE;
		zshow_output(output, 0);
	}
}
