/****************************************************************
 *								*
 * 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.	*
 *								*
 ****************************************************************/

#if defined(__MVS__) && !defined(_ISOC99_SOURCE)
#define _ISOC99_SOURCE
#endif

#include "mdef.h"

#include "gtm_stdio.h"
#include "gtm_string.h"
#include "cli.h"
#include "cli_parse.h"
#include "error.h"
#include "cli_disallow.h"
#include "gtmio.h"
#include "op.h"

#define	NO_STRING	"NO"

/* Dynamically allocates (that is, grows) enough space for the current array
 * of parameter strings during the lifetime of the process. Since the number
 * of array elements is limited, it is better to allocate memory as needed
 * rather than constantly free and reallocate. */
#define GROW_HEAP_IF_NEEDED(PARM_ARY_INDEX)									\
{														\
	if (TAREF1(parm_str_len, PARM_ARY_INDEX) > TAREF1(parm_ary_len, PARM_ARY_INDEX))			\
	{													\
		if (TAREF1(parm_ary, PARM_ARY_INDEX))								\
			free(TAREF1(parm_ary, PARM_ARY_INDEX));							\
														\
		TAREF1(parm_ary, PARM_ARY_INDEX) = (char *)malloc(TAREF1(parm_str_len, PARM_ARY_INDEX));	\
														\
		TAREF1(parm_ary_len, PARM_ARY_INDEX) = TAREF1(parm_str_len, PARM_ARY_INDEX);			\
	}													\
	assert(NULL != (TAREF1(parm_ary, PARM_ARY_INDEX))); /* 4SCA: parm_ary_len ensure this */		\
}

#if MAX_OPT_LEN > MAX_LINE
#	error MAX_OPT_LEN is greater than MAX_LINE. Fix STRNCMP_STR and STRNCPY_STR usages below.
#endif

GBLDEF void 		(*func)(void);			/* Function to be dispatched
							   to for this command */

GBLREF char 		cli_err_str[];			/* Parse Error message buffer */
static CLI_ENTRY 	*gpqual_root;			/* pointer to root of
							   subordinate qualifier table */
static CLI_ENTRY 	*gpcmd_qual;			/* pointer command qualifier table */
static CLI_ENTRY 	*gpcmd_verb;			/* pointer to verb table */
static CLI_PARM 	*gpcmd_parm_vals;		/* pointer to parameters for command */

GBLREF char 		cli_token_buf[];
GBLREF CLI_ENTRY 	*cmd_ary;

GBLREF IN_PARMS *cli_lex_in_ptr;

LITREF	mval		literal_notimeout;

error_def(ERR_CLIERR);
error_def(ERR_MUNOACTION);
error_def(ERR_MUPCLIERR);

/*
 * -----------------------------------------------
 * Clear all parameter values and flags of command
 * parameter table for a given command.
 *
 * Arguments:
 *	cmd_parms	- address of parameter array
 *	follow		- whether to clear extra value tables or not
 *	  TRUE		- follow down
 *	  FALSE		- do not follow down
 *
 * Return:
 *	none
 * -----------------------------------------------
 */
void	clear_parm_vals(CLI_ENTRY *cmd_parms, boolean_t follow) 		/* pointer to option's parameter table */
{
	CLI_ENTRY	*root_param;
	int		need_copy;

	need_copy = (gpcmd_qual != cmd_parms);
	while (strlen(cmd_parms->name) > 0)
	{
		if (cmd_parms->pval_str)
			free(cmd_parms->pval_str);
		/* if root table exists, copy over any qualifier values to the new parameter table */
		if ((FALSE != follow) && need_copy &&
			(root_param = find_cmd_param(cmd_parms->name, gpcmd_qual, FALSE)))
		{
			cmd_parms->pval_str = root_param->pval_str;
			cmd_parms->negated = root_param->negated;
			cmd_parms->present = root_param->present;
			root_param->pval_str = 0;
		} else
		{
			cmd_parms->negated = 0;
			cmd_parms->present = 0;
			cmd_parms->pval_str = 0;
			/* dfault_str also implements default values.
			   0: it is not present by default,
			   DEFA_PRESENT: it is present by default, no value,
			   str pointer: it is present by default, with the value pointed to
			*/
			if (cmd_parms->dfault_str)
			{
				cmd_parms->present = CLI_DEFAULT;
				if (CLI_PRESENT != (INTPTR_T)cmd_parms->dfault_str)
					cmd_parms->pval_str = cmd_parms->dfault_str;
			}
			if ((FALSE != follow) && cmd_parms->qual_vals)
				clear_parm_vals(cmd_parms->qual_vals, FALSE);
		}
		cmd_parms++;
	}
}

/*
 * ---------------------------------------------------------
 * Find entry in the qualifier table
 *
 * Arguments:
 *	str	- parameter string
 *	pparm	- pointer to parameter table for this command
 *
 * Return:
 *	if found, index into command table array,
 *	else -1
 * ---------------------------------------------------------
 */
int 	find_entry(char *str, CLI_ENTRY *pparm)
{
	int 		match_ind, res, str_len;
	boolean_t 	len_match;
	int 		ind;
	char 		*sp;

	ind = 0;
	match_ind = -1;
	len_match = FALSE;
	cli_strupper(str);

	str_len = strlen(str);
	while (0 < strlen(sp = (pparm + ind)->name))
	{
		/* As the table is parsed as long as the string in question is lexically smaller
		   than the entry in the table, we go on checking the next entry.
		   If a match is found, the lengths of the two strings (the table entry and the
		   string in question) are compared.
		   When the next entry is checked,
		   if it is not a match, the previous entry is the correct match;
		   if it is a match again (a longer entry), if the first match was a length-match
		   as well, the first entry is the match returned. otherwise an error is returned
		   since we cannot make a decision (does SE match SET or SEP). */

		if (0 == (res = STRNCMP_STR(sp, str, str_len)))
		{
			if (-1 != match_ind)
			{
				if (FALSE == len_match)
					return (-1);
				break;
			} else
			{
				if (str_len == strlen(sp))
					len_match = TRUE;
				match_ind = ind;
			}
		} else
		{
			if (0 < res)
				break;
		}
		ind++;
	}
	if (-1 != match_ind && gpqual_root && 0 == STRNCMP_STR(gpqual_root->name, str, MAX_OPT_LEN))
		return (-1);
	return (match_ind);
}

/*
 * -----------------------------------------------
 * Find command in command table
 *
 * Arguments:
 *	str	- command string
 *
 * Return:
 *	if found, index into command table array,
 *	else -1
 * -----------------------------------------------
 */
int 	find_verb(char *str)
{
	return (find_entry(str, cmd_ary));
}

/*
 * ---------------------------------------------------------
 * Find command parameter in command parameter table
 *
 * Arguments:
 *	str	- parameter string
 *	pparm	- pointer to parameter table for this command
 *
 * Return:
 *	if found, pointer to parameter structure
 *	else 0
 * ---------------------------------------------------------
 */
CLI_ENTRY *find_cmd_param(char *str, CLI_ENTRY *pparm, int follow)
{
	CLI_ENTRY	*pparm_tmp;
	int		ind, ind_match;
	char		*sp;

	ind_match = -1;
	if (NULL == pparm)
		return NULL;
	if (0 <= (ind = find_entry(str, pparm)))
		return pparm + ind;

	if (FALSE == follow)
		return NULL;
	/* if to follow, go through the qual_vals, and check those tables */
	for(ind =0; 0 < strlen((pparm + ind)->name); ind++)
	{
		pparm_tmp = (pparm + ind)->qual_vals;
		if (pparm_tmp)
			ind_match = find_entry(str, pparm_tmp);
		if (-1 != ind_match)
			return pparm_tmp + ind_match;
	}
	return NULL;
}

/*
 * ---------------------------------------------------------
 * Parse one option.
 * Read tokens from the input.
 * Check if it is a valid qualifier or parameter.
 * If it is a parameter, get it, and save it in the
 * global parameter array.
 * If it is a qualifier, get its value and save it in a value table,
 * corresponding to this option.
 *
 * Arguments:
 *	pcmd_parms	- pointer to command parameter table
 *	eof		- pointer to end of file flag
 *
 * Return:
 *	1 - option parsed OK
 *	-1 - failure to parse option
 *	0 - no more tokens, in which case
 *		the eof flag is set on end of file.
 * ---------------------------------------------------------
 */
int 	parse_arg(CLI_ENTRY *pcmd_parms, int *eof)
{
	CLI_ENTRY 	*pparm;
	char 		*opt_str, *val_str;
	int 		neg_flg;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* -----------------------------------------
	 * get qualifier marker, or parameter token
	 * -----------------------------------------
	 */
	if (VAL_LIST == gpcmd_verb->val_type && TREF(parms_cnt) == gpcmd_verb->max_parms)
		return (0);
	if (!cli_look_next_token(eof))
		return (0);
	/* -------------------------------------------------------------------
	 * here cli_token_buf is set by the previous cli_look_next_token(eof)
	 * call itself since it in turn calls cli_gettoken()
	 * -------------------------------------------------------------------
	 */
	if (!cli_is_qualif(cli_token_buf) && !cli_is_assign(cli_token_buf))
	{	/* ----------------------------------------------------
		 * If token is not a qualifier, it must be a parameter
		 *
		 * No need to check for eof on cli_get_string_token(eof) since
		 * already checked that on the previous cli_look_next_token.
		 * now you have to skip initial white spaces before reading
		 * the string since cli_get_string_token considers a space
		 * as a blank token. hence the need for the skip_white_space()
		 * call.
		 * ------------------------------------------------------------
		 */
		skip_white_space();
		cli_get_string_token(eof);
		if (TREF(parms_cnt) >= gpcmd_verb->max_parms)
		{
			SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Too many parameters");
			return (-1);
		}
		TAREF1(parm_str_len, TREF(parms_cnt)) = strlen(cli_token_buf) + 1;
		GROW_HEAP_IF_NEEDED(TREF(parms_cnt));
		memcpy(TAREF1(parm_ary, TREF(parms_cnt)), cli_token_buf, TAREF1(parm_str_len, TREF(parms_cnt)));
		(TREF(parms_cnt))++;
		return (1);
	}
	/* ---------------------------------------------------------------------
	 * cli_gettoken(eof) need not be checked for return value since earlier
	 * itself we have checked for return value in cli_look_next_token(eof)
	 * ---------------------------------------------------------------------
	 */
	cli_gettoken(eof);
	opt_str = cli_token_buf;
	if (!pcmd_parms)
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "No qualifiers allowed for this command");
		return (-1);
	}
	/* ------------------------------------------
	 * Qualifiers must start with qualifier token
	 * ------------------------------------------
	 */
	if (!cli_is_qualif(cli_token_buf))
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Qualifier expected instead of : %s ", opt_str);
		return (-1);
	}
	/* -------------------------
	 * Get the qualifier string
	 * -------------------------
	 */
	if (!cli_look_next_token(eof) || 0 == cli_gettoken(eof))
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Qualifier string missing %s ", opt_str);
		return (-1);
	}
	/* ---------------------------------------
	 * Fold the qualifier string to upper case
	 * ---------------------------------------
	 */
	cli_strupper(opt_str);
	/* -------------------------
	 * See if option is negated and update
	 * -------------------------
	 */
	if (-1 == (neg_flg = cli_check_negated(&opt_str, pcmd_parms, &pparm)))
		return (-1);
	/* -------------------------------------------------------------
	 * If value is disallowed for this qualifier, and an assignment
	 * token is encounter, report error, values not allowed for
	 * negated qualifiers
	 * -------------------------------------------------------------
	 */
	if (neg_flg || VAL_DISALLOWED == pparm->required)
	{
		if (cli_look_next_token(eof) && cli_is_assign(cli_token_buf))
		{
			SNPRINTF(cli_err_str, MAX_CLI_ERR_STR,
			  "Assignment is not allowed for this option : %s",
			  pparm->name);
			return (-1);
		}
	} else
	{	/* --------------------------------------------------
		 * Get Value either optional, or required.
		 * In either case, there must be an assignment token
		 * --------------------------------------------------
		 */
		if (!cli_look_next_token(eof) || !cli_is_assign(cli_token_buf))
		{
	    		if (VAL_REQ == pparm->required)
			{
				SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option : %s needs value", pparm->name);
				return (-1);
			} else
			{
				if (pparm->present)
				{
					/* The option was specified before, so clean up that one,
					 * the last one overrides
					 */
					if (pparm->pval_str)
						free(pparm->pval_str);
					if (pparm->qual_vals)
						clear_parm_vals(pparm->qual_vals, FALSE);

				}
				/* -------------------------------
				 * Allocate memory and save value
				 * -------------------------------
				 */
				if (pparm->parm_values)
				{
					MALLOC_CPY_STR(pparm->pval_str, pparm->parm_values->prompt);
					if (!cli_get_sub_quals(pparm))
						return (-1);
				}
			}
		} else
		{
			cli_gettoken(eof);
			/* ---------------------------------
			 * Get the assignment token + value
			 * ---------------------------------
			 */
			if (!cli_is_assign(cli_token_buf))
			{
				SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Assignment missing after option : %s", pparm->name);
				return (-1);
			}
			/* --------------------------------------------------------
			 * get the value token, "=" is NOT a token terminator here
			 * --------------------------------------------------------
			 */
			if (!cli_look_next_string_token(eof) || 0 == cli_get_string_token(eof))
			{
				SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized option : %s, value expected but not found",
						pparm->name);
				cli_lex_in_ptr->tp = 0;
				return (-1);
			}
			val_str = cli_token_buf;
			if (!cli_numeric_check(pparm, val_str))
			{
				cli_lex_in_ptr->tp = 0;
				return (-1);
			}
			if (pparm->present)
			{	/* The option was specified before, so clean up that one,
				 * the last one overrides
				 */
				if (pparm->pval_str)
					free(pparm->pval_str);
				if (pparm->qual_vals)
					clear_parm_vals(pparm->qual_vals, FALSE);
			}
			/* -------------------------------
			 * Allocate memory and save value
			 * -------------------------------
			 */
			MALLOC_CPY_STR(pparm->pval_str, cli_token_buf);
			if (!cli_get_sub_quals(pparm))
				return (-1);
		}
	}
	if (pparm->present)
		pparm->negated = 0;
	pparm->negated = neg_flg;
	pparm->present = 1;
	if (NULL != pparm->func)
		func = pparm->func;
	/* ----------------------------------------------------------------------------------------------------------------------
	 * If there is another level, update global pointers
	 * Notice that this global pointer updation should be done only at the end of this routine in order to ensure that the
	 * 	check_disallow() function invoked below sees the currently parsed argument as present (i.e. pparm->present = 1)
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	if (pparm->parms)
	{	/*-------------------------------------------------------------------------------------------
		 * Check that the disallow conditions for this level are met before switching to next level
		 *-------------------------------------------------------------------------------------------
		 */
		if (FALSE == check_disallow(gpcmd_verb))
			return (-1);
		gpqual_root = pparm;
		clear_parm_vals(pparm->parms, TRUE);
		gpcmd_qual = pparm->parms;
		gpcmd_verb = pparm;	/* this needs to be done in order for check_disallow() to do the proper disallow check.
					 * an example that will not work otherwise is cli_disallow_mupip_replic_receive() */
	}
	return (1);
}

/* -----------------------------------------------------
 * Check if the value is numeric if it is supposed to be
 *
 * Return:
 *	TRUE	- It is numeric or val_type is not
 *	          numeric anyway
 *	FALSE	- Is not numeric
 * -----------------------------------------------------
 */
boolean_t cli_numeric_check(CLI_ENTRY *pparm, char *val_str)
{
	boolean_t retval = TRUE;

	if (VAL_NUM == pparm->val_type)
	{
		if (pparm->hex_num)
		{
			if (!cli_is_hex(val_str))
			{
				SNPRINTF(cli_err_str, MAX_CLI_ERR_STR,
				  "Unrecognized value: %s, HEX number expected",
				  val_str);
				retval = FALSE;
			}
		} else if (!cli_is_dcm(val_str))
		{
			SNPRINTF(cli_err_str, MAX_CLI_ERR_STR,
			  "Unrecognized value: %s, Decimal number expected",
			  val_str);
			retval = FALSE;
		}
	}
	return (retval);
}

/*---------------------------
 * Check if option is negated
 *---------------------------
 */
int cli_check_negated(char **opt_str_ptr, CLI_ENTRY *pcmd_parm_ptr, CLI_ENTRY **pparm_ptr)
{
	int 		neg_flg;
	CLI_ENTRY	*pcmd_parms;
	char		*opt_str_tmp;

	pcmd_parms = pcmd_parm_ptr;
	opt_str_tmp = *opt_str_ptr;
	if (0 == MEMCMP_LIT(*opt_str_ptr, NO_STRING))
	{
		*opt_str_ptr += SIZEOF(NO_STRING) - 1;
		neg_flg = 1;
	} else
		neg_flg = 0;
	/* --------------------------------------------
	 * search qualifier table for qualifier string
	 * --------------------------------------------
	 */
	if (0 == (*pparm_ptr = find_cmd_param(*opt_str_ptr, pcmd_parms, FALSE)))
	{
		/* Check that the qualifier does not have the NO prefix */
		if (0 == (*pparm_ptr = find_cmd_param(opt_str_tmp, pcmd_parms, FALSE)))
		{
			SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized option : %s", *opt_str_ptr);
			cli_lex_in_ptr->tp = 0;
			return (-1);
		} else
		{
			/* It was a valid qualifier with the prefix NO */
			*opt_str_ptr = opt_str_tmp;
			neg_flg = 0;
		}
	}
	/* ----------------------------------------------------
	 * if option is negated and it is not negatable, error
	 * ----------------------------------------------------
	 */
	if (!(*pparm_ptr)->negatable && neg_flg)
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option %s may not be negated", *opt_str_ptr);
		return (-1);
	}
	return neg_flg;
}

/*
 * --------------------------------------------------
 *
 * Process the qualifier to see if any extra parameters
 * are possible in the <...>
 * Return:
 *	TRUE	- OK
 *	FALSE	- Error
 * --------------------------------------------------
 */
boolean_t cli_get_sub_quals(CLI_ENTRY *pparm)
{
	CLI_ENTRY 	*pparm_qual, *pparm1;
	char		local_str[MAX_LINE], tmp_str[MAX_LINE + 1], *tmp_str_ptr;
	char 		*ptr_next_val, *ptr_next_comma, *ptr_equal;
	int		len_str, neg_flg, ptr_equal_len;
	boolean_t	val_flg, has_a_qual;

	has_a_qual = FALSE;
	pparm_qual = pparm->qual_vals;
	if (!pparm_qual)
		return TRUE;
	if ((VAL_STR == pparm->val_type) || (VAL_LIST == pparm->val_type))
	{
		STRNCPY_STR(local_str, pparm->pval_str, SIZEOF(local_str) - 1);
		ptr_next_val = local_str;
		while (NULL != ptr_next_val)
		{	/* WARNING assignment below */
			STRNCPY_STR(tmp_str, ptr_next_val, (len_str = MIN(STRLEN(ptr_next_val), SIZEOF(tmp_str))));
			tmp_str[len_str] = 0;
			tmp_str_ptr = tmp_str;
			ptr_next_comma = strchr(tmp_str_ptr, ',');
			if (NULL == ptr_next_comma)
				ptr_next_comma = tmp_str_ptr + len_str;
			else
				*ptr_next_comma = 0;
			ptr_equal = strchr(tmp_str_ptr, '=');
			if (ptr_equal && (ptr_equal < ptr_next_comma) )
				*ptr_equal = 0;
			else
				ptr_equal = NULL;
			/* Convert just the qualifier to upper case (e.g. in "enable,on,file=x.mjl" --> "ENABLE,ON,FILE=x.mjl")
			 * Do not convert the values of parameters to upper case as that might result in incorrect translation
			 * if UTF8 characters are present in the command line. The logic below anyways needs only the qualifiers
			 * to be translated.
			 */
			cli_strupper(tmp_str);
			/* -------------------------
			 * See if option is negated
			 * -------------------------
			 */
			if (-1 == (neg_flg = cli_check_negated( &tmp_str_ptr, pparm_qual, &pparm1)))
				return FALSE;
			if (1 == neg_flg)
				len_str -=  STRLEN(NO_STRING);

			if ((ptr_equal) && (ptr_equal + 1 < ptr_next_comma))
				val_flg = TRUE;
			else
				val_flg = FALSE;
			/* -------------------------------------------------------------
			 * If value is disallowed for this qualifier, and an assignment
			 * is encountered, report error, values not allowed for
			 * negated qualifiers
			 * -------------------------------------------------------------
			 */
			if (neg_flg || VAL_DISALLOWED == pparm1->required)
			{
				if (val_flg)
				{
					SNPRINTF(cli_err_str, MAX_CLI_ERR_STR,
					  "Assignment is not allowed for this option : %s",
					  pparm1->name);
					cli_lex_in_ptr->tp = 0;
					return FALSE;
				}
			} else
			{
				if ((!val_flg) && VAL_REQ == pparm1->required)
				{
					if (ptr_equal)
						SNPRINTF(cli_err_str, MAX_CLI_ERR_STR,
						  "Unrecognized option : %s, value expected but not found",
						  pparm1->name);
					else
						SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option : %s needs value", tmp_str_ptr);
					cli_lex_in_ptr->tp = 0;
					return FALSE;
				}
				if (!cli_numeric_check(pparm1, ptr_equal + 1))
				{
					cli_lex_in_ptr->tp = 0;
					return FALSE;
				}
				if (pparm1->present)
				{
					/* The option was specified before, so clean up that one,
					 * the last one overrides
					 */
					if (pparm1->pval_str)
						free(pparm1->pval_str);
				}
				if ((!val_flg) && (VAL_NOT_REQ == pparm1->required) && pparm1->parm_values)
					MALLOC_CPY_STR(pparm1->pval_str, pparm1->parm_values->prompt);
				if (val_flg)
				{
					ptr_equal_len = STRLEN(ptr_equal + 1);
					pparm1->pval_str = malloc(ptr_equal_len + 1);
					STRNCPY_STR(pparm1->pval_str, ptr_next_val + (ptr_equal - tmp_str_ptr) + 1, ptr_equal_len);
					pparm1->pval_str[ptr_equal_len] = 0;
				}

			}
			if (pparm1->present)
				pparm->negated = 0;
			pparm1->negated = neg_flg;
			pparm1->present = CLI_PRESENT;
			has_a_qual = TRUE;
			if ((tmp_str_ptr + len_str) != ptr_next_comma)
			{
				ptr_next_val = strchr(ptr_next_val, ',') + 1;
				if (!ptr_next_val || !*ptr_next_val)
				{
					SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Option expected");
					cli_lex_in_ptr->tp = 0;
					return FALSE;

				}
			} else
				ptr_next_val = NULL;
		}
	}
	/* do one last parse on the qualifier table, if there is any other qualifier present,
	 * the DEFA_PRESENT one is not necessary, otherwise leave it as it is.
	 */
	if (has_a_qual)
	{
		pparm1 = pparm_qual;
		while (0 != *pparm1->name)
		{
			if (CLI_DEFAULT == pparm1->present)
				pparm1->present = CLI_ABSENT;
			pparm1++;
		}
	}

	return TRUE;
}

/*
 * -----------------------------------------------------------
 * Parse one command.
 * Get tokens from the input stream.
 * See if the first token is a command name, as it should be,
 * and if it is, check if optional arguments that follow,
 * are legal, and if they are, get their values and
 * save them in a value table, corresponding to this
 * option.
 * If any of these conditions are not met, parse error occures.
 *
 * Return:
 *	0 - command parsed OK
 *	EOF - end of file
 *	<> - failure to parse command
 * -----------------------------------------------------------
 */
int parse_cmd(void)
{
	int 	res, cmd_ind;
	char 	*cmd_str;
	int 	opt_cnt;
	int 	eof, cmd_err;
        DCL_THREADGBL_ACCESS;

        SETUP_THREADGBL_ACCESS;
	opt_cnt = 0;
	gpqual_root = NULL;
	func = 0;
	cmd_err = 0;
	TREF(parms_cnt) = 0;			/* Parameters count */
	*cli_err_str = 0;

	cmd_str = cli_token_buf;
	/* ------------------------
	 * If no more tokens exist
	 * ------------------------
	 */
	if (0 == cli_gettoken(&eof))
	{
		if (eof)
			return (EOF);
		return (0);
	}
	/* ------------------------------
	 * Find command in command table
	 * ------------------------------
	 */
	if (-1 != (cmd_ind = find_verb(cmd_str)))
	{
		gpcmd_qual = cmd_ary[cmd_ind].parms;
		gpcmd_parm_vals = cmd_ary[cmd_ind].parm_values;
		gpcmd_verb = &cmd_ary[cmd_ind];
		if (gpcmd_qual)
			clear_parm_vals(gpcmd_qual, TRUE);
		func = cmd_ary[cmd_ind].func;
		/* ----------------------
		 * Parse command options
		 * ----------------------
		 */
		do
		{
			res = parse_arg(gpcmd_qual, &eof);
			if (1 == res)
			{
				opt_cnt++;
			}
		} while (1 == res);
	} else
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Unrecognized command: %s", cmd_str);
		cli_lex_in_ptr->tp = 0;
		res = -1;
	}
	if ((1 > opt_cnt) && (-1 != res) && (VAL_REQ == cmd_ary[cmd_ind].required))
	{
		SNPRINTF(cli_err_str, MAX_CLI_ERR_STR, "Command argument expected, but not found");
		res = -1;
	}
	/*------------------------------------------------------
	 * Check that the disallow conditions are met (to allow)
	 *------------------------------------------------------
	 */
	if ((-1 != res) && (FALSE == check_disallow(gpcmd_verb)))
		res = -1;
	/* -------------------------------------
	 * If parse error, display error string
	 * -------------------------------------
	 */
	if (-1 == res)
	{
		func = 0;
		eof = 0;
	}
	else
		return (0);
	/* -------------------------
	 * If gettoken returned EOF
	 * -------------------------
	 */
	if (eof)
		return (EOF);
	else
		return (ERR_CLIERR);
}

/* ------------------------------------------------------------
 * See if command parameter is present on the command line,
 * and if it is, return the pointer to its table entry.
 *
 * Arguments:
 *	parm_str	- parameter string to search for
 *
 * Return:
 *	0 - not present
 *	pointer to parameter entry
 * ------------------------------------------------------------
 */
CLI_ENTRY *get_parm_entry(char *parm_str)
{
	CLI_ENTRY	*pparm;
	boolean_t	root_match;
	char		local_str[MAX_LINE], *tmp_ptr;

	SNPRINTF(local_str, MAX_LINE, "%s", parm_str);
	root_match = (gpqual_root && !STRNCMP_STR(gpqual_root->name, local_str, MAX_OPT_LEN));

	/* ---------------------------------------
	 * search qualifier table for this option
	 * ---------------------------------------
	 */
	if (!gpcmd_qual)
		return (NULL);
	if (NULL == strchr(local_str,'.'))
		pparm = find_cmd_param(local_str, gpcmd_qual, TRUE);
	else
	{
		tmp_ptr= strchr(local_str,'.');
		/* there should be at least 1 character after the . */
		assert (tmp_ptr + 1 < local_str + strlen(local_str));
		*tmp_ptr = 0;
		pparm = find_cmd_param(local_str, gpcmd_qual, FALSE);
		if (pparm)
			pparm = find_cmd_param(tmp_ptr+1, pparm->qual_vals, FALSE);

	}
	if (root_match && !pparm)
		return (gpqual_root);
	else if (pparm)
		return (pparm);
	else
		return (NULL);
}

/*
 * ------------------------------------------------------------
 * See if command parameter is present on the command line
 *
 * Arguments:
 *	entry	- parameter string to search for
 *
 * Return:
 *	0 - not present (i.e. CLI_ABSENT)
 *	<> 0 - present  (either CLI_PRESENT or CLI_NEGATED)
 * ------------------------------------------------------------
 */
int cli_present(char *entry)
{
	CLI_ENTRY	*pparm;
	char		local_str[MAX_LINE];

	STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1);
	local_str[SIZEOF(local_str) - 1] = '\0';
	cli_strupper(local_str);
	if (pparm = get_parm_entry(local_str))
	{
		if (pparm->negated)
			return (CLI_NEGATED);
		if ((CLI_PRESENT == pparm->present) || (CLI_DEFAULT == pparm->present))
			return (CLI_PRESENT);
	}

	return (CLI_ABSENT);
}

/*
 * ------------------------------------------------------------
 * Get the command parameter value
 *
 * Arguments:
 *	entry		- parameter string to search for
 *	val_buf		- if parameter is present, it is copied to
 *			  this buffer.
 *
 * Return:
 *	0 - not present
 *	<> 0 - ok
 * ------------------------------------------------------------
 */
boolean_t cli_get_value(char *entry, char val_buf[])
{
	CLI_ENTRY	*pparm;
	char		local_str[MAX_LINE];
#	ifdef DEBUG
	int             ind, len;
	CLI_ENTRY	*parm_vals;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
#	endif
	STRNCPY_STR(local_str, entry, MAX_LINE - 1);
	local_str[MAX_LINE - 1] = '\0';

	cli_strupper(local_str);
	if (NULL == (pparm = get_parm_entry(local_str)))
		return (FALSE);
	if (!pparm->present || NULL == pparm->pval_str)
		return (FALSE);
	else
	{
#		ifdef DEBUG
		if (TREF(cli_get_str_max_len) && (NULL != (parm_vals = pparm->qual_vals)))
		{
			for (ind = 0; ; ind++)
			{
				len = strlen(parm_vals[ind].name);
				if (!len)
					break;
				/* Assert that we have allocated enough space to store all possible values for this parameter.
				 * If this assert fails, fix caller of cli_get_str to allocate enough space.
				 */
				assert(len <= TREF(cli_get_str_max_len));
			}
		}
#		endif
		STRNCPY_STR(val_buf, pparm->pval_str, MAX_LINE);
		val_buf[MAX_LINE - 1] = '\0';
	}
	return (TRUE);
}

/*
 * --------------------------------------------------
 * See if the qualifier given by 'entry' is negated
 * on the command line
 *
 * Return:
 *	TRUE	- Negated
 *	FALSE	- otherwise
 * --------------------------------------------------
 */
boolean_t cli_negated(char *entry) 		/* entity */
{
	CLI_ENTRY	*pparm;
	char		local_str[MAX_LINE];

	STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1);
	local_str[SIZEOF(local_str) - 1] = '\0';
	cli_strupper(local_str);
	if (pparm = get_parm_entry(local_str))
		return (pparm->negated);
	return (FALSE);
}


boolean_t cli_get_parm(char *entry, char val_buf[])
{
	char		*gets_res, local_str[MAX_LINE], *sp;
	int		eof, ind, match_ind, parm_len, res;
	mval		prompt, dummy, *input_line;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	ind = 0;
	assert(0 != gpcmd_parm_vals);
	STRNCPY_STR(local_str, entry, SIZEOF(local_str) - 1);
	local_str[SIZEOF(local_str) - 1] = '\0';
	cli_strupper(local_str);
	match_ind = -1;
	while (0 < strlen(sp = (gpcmd_parm_vals + ind)->name)) /* implicit assignment intended */
	{
		if (0 == (res = STRNCMP_STR(sp, local_str, MAX_OPT_LEN))) /* implicit assignment intended */
		{
			if (-1 != match_ind)
				return (FALSE);
			else
				match_ind = ind;
		} else
		{
			if (0 < res)
				break;
		}
		ind++;
	}
	if (-1 != match_ind)
	{
		if (NULL == TAREF1(parm_ary, match_ind))
		{
			if (!((gpcmd_parm_vals + match_ind)->parm_required)) /* Value not required */
				return FALSE;
			/* If no value and required, prompt for it */
			dummy.mvtype = dummy.str.len = 0;
			input_line = push_mval(&dummy);
			prompt.mvtype = MV_STR;
			prompt.str.addr = (gpcmd_parm_vals + match_ind)->prompt;
			for (prompt.str.len = 0, sp = prompt.str.addr; 0 != *sp; sp++, prompt.str.len++)
				;
			op_write(&prompt);
			op_read(input_line, (mval *)&literal_notimeout);
			if (input_line->str.len)
			{
				parm_len = input_line->str.len;
				TAREF1(parm_str_len, match_ind) = parm_len + 1;
				GROW_HEAP_IF_NEEDED(match_ind);
				if (parm_len)
					memcpy(TAREF1(parm_ary, match_ind), input_line->str.addr, parm_len);
				*(TAREF1(parm_ary, match_ind) + parm_len) = '\0';
			} else
			{	/* No string was returned so create a real ghost to point to.
				   Note that this should be revisited since this is NOT what should
				   be happening. We should be returning FALSE here but need to instead
				   return a null parm since current behaviors have a dependency on it
				   SE 10/2003
				 */
				TAREF1(parm_str_len, match_ind) = 1;
				GROW_HEAP_IF_NEEDED(match_ind);
				*TAREF1(parm_ary, match_ind) = '\0';
			}
		} else if (-1 == *TAREF1(parm_ary, match_ind) && 1 == TAREF1(parm_str_len, match_ind))
			return (FALSE);
		strcpy(val_buf, TAREF1(parm_ary, match_ind));
		if (!cli_look_next_token(&eof) || (0 == cli_gettoken(&eof)))
		{
			TAREF1(parm_str_len, match_ind) = 1;
			GROW_HEAP_IF_NEEDED(match_ind);
			*TAREF1(parm_ary, match_ind) = -1;
		} else
		{
			parm_len = (int)(STRLEN(cli_token_buf) + 1);
			if (MAX_LINE < parm_len)
			{
				PRINTF("Parameter string too long\n");
				FFLUSH(stdout);
				return (FALSE);
			}
			TAREF1(parm_str_len, match_ind) = parm_len;
			GROW_HEAP_IF_NEEDED(match_ind);
			memcpy(TAREF1(parm_ary, match_ind), cli_token_buf, parm_len);
		}
	} else
	{
		/* -----------------
		 * check qualifiers
		 * -----------------
		 */
		if (!cli_get_value(local_str, val_buf))
			return (FALSE);
	}
	return (TRUE);
}

#ifdef GTM_TRIGGER
int parse_triggerfile_cmd(void)
{
	int 		res, cmd_ind;
	int 		opt_cnt;
	int 		eof;
	char		cmd[] = "TRIGGER";
	char		*ptr;
        DCL_THREADGBL_ACCESS;

        SETUP_THREADGBL_ACCESS;
	opt_cnt = 0;
	gpqual_root = 0;
	func = 0;
	TREF(parms_cnt) = 0;			/* Parameters count */
	*cli_err_str = 0;
	cmd_ind = find_verb(cmd);
	assert(-1 != cmd_ind);
	gpcmd_qual = cmd_ary[cmd_ind].qual_vals;
	gpcmd_parm_vals = NULL;
	gpcmd_verb = cmd_ary[cmd_ind].qual_vals;
	if (gpcmd_qual)
		clear_parm_vals(gpcmd_qual, TRUE);
	func = cmd_ary[cmd_ind].func;
	/* ----------------------
	 * Parse command options
	 * ----------------------
	 */
	do
	{
		res = parse_arg(gpcmd_qual, &eof);
	} while (1 == res);
	/* -------------------------------------
	 * If parse error, display error string
	 * -------------------------------------
	 */
	if (-1 == res)
		func = 0;
	else
		return (0);
	/* -------------------------
	 * If gettoken returned EOF
	 * -------------------------
	 */
	if (eof)
		return (EOF);
	else
		return (ERR_MUNOACTION);
}
#endif
