fis-gtm/sr_unix/trigger_parse.c

1471 lines
40 KiB
C

/****************************************************************
* *
* Copyright 2010 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_ctype.h"
#include "gtm_string.h"
#include "cli.h"
#include "gdsroot.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include <rtnhdr.h>
#include "gv_trigger.h"
#include "gtm_trigger.h"
#include "trigger.h"
#include "trigger_parse_protos.h"
#include "trigger_scan_string.h"
#include "zshow.h" /* for zwr2format() prototype */
#include "min_max.h"
#include "util.h"
#include "subscript.h"
#include "cli_parse.h"
#include "compiler.h"
#include "gtm_utf8.h"
#include "gtmctype.h"
GBLREF CLI_ENTRY *cmd_ary;
GBLREF gd_region *gv_cur_region;
GBLREF CLI_ENTRY trigger_cmd_ary[];
#define BITS_PER_INT (SIZEOF(uint4) * 8) /* Number of bits in an integer */
#define MAX_PIECE_VALUE (BITS_PER_INT * 1024) /* Largest value allowed in -pieces string */
#define MAX_PIECE_INT (MAX_PIECE_VALUE / 32) /* Number of integers it takes to hold MAX_PIECE_VALUE bits */
#define MAX_PIECE_CHARS (MAX_PIECE_INT * 4) /* Number of 8-bit bytes in MAX_PIECE_INT integers */
#define MAX_LVN_COUNT MAX_GVSUBSCRIPTS /* Maximum number of "lvn=" in trigger subscript */
#define MAX_OPTIONS_LEN 1024 /* Maximum size of the "options" string */
#define MAX_DCHAR_LEN 1024 /* Maximum size of $C or $ZCH string */
#define MAX_DELIM_LEN 1024 /* Maximum size of the string for a delimiter - $C, $ZCH, "x", ... */
#define MAX_ERROR_MSG_LEN 72
#define TRIGR_PRECOMP_RTNNAME "trigcomptest#"
#define CONV_CH(PTR, STR, LEN) \
{ \
if (PRINTABLE(*PTR)) \
{ \
STR[0] = '"'; \
STR[1] = *PTR; \
STR[2] = '"'; \
LEN = 3; \
} else \
format2zwr((sm_uc_ptr_t)PTR, 1, STR, &LEN); \
}
#define ERROR_MSG_RETURN(STR, LEN, PTR) \
{ \
CONV_STR_AND_PRINT(STR, LEN, PTR); \
return FALSE; \
}
#define ERROR_STR_RETURN(STR) \
{ \
util_out_print_gtmio(STR, FLUSH); \
return FALSE; \
}
#define CONV_CH_AND_PRINT(STR, PTR) \
{ \
int out_len; \
unsigned char out_str[64]; /* Plenty of room for $ZCH(xxx) */ \
\
CONV_CH(PTR, out_str, out_len); \
util_out_print_gtmio(STR, FLUSH, out_len, out_str); \
}
#define CONV_CH_AND_STR_AND_PRINT(STR, PTR1, LEN, PTR2) \
{ \
int out_len1, out_len2; \
unsigned char out_str1[64]; /* Plenty of room for $ZCH(xxx) */ \
unsigned char out_str2[MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE]; \
\
CONV_CH(PTR1, out_str1, out_len1); \
CONV_TO_ZWR(LEN, PTR2, out_len2, out_str2); \
util_out_print_gtmio(STR, FLUSH, out_len1, out_str1, out_len2, out_str2); \
}
#define CONV_NUM_AND_STR_AND_PRINT(STR, NUM, LEN, PTR) \
{ \
int out_len; \
unsigned char out_str[MAX_ZWR_EXP_RATIO * OUT_BUFF_SIZE]; \
\
CONV_TO_ZWR(LEN, PTR, out_len, out_str); \
util_out_print_gtmio(STR, FLUSH, NUM, out_len, out_str); \
}
#define UPDATE_DST(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \
{ \
if (!HAVE_STAR) \
{ \
if (MAX_LEN < ++DST_LEN) \
{ \
util_out_print_gtmio("Trigger definition too long", FLUSH); \
return FALSE; \
} \
*DST_PTR++ = *PTR++; \
} \
else PTR++; \
LEN--; \
}
#define UPDATE_TRIG_PTRS(VAL, NEXT_VAL, LEN, MAX_OUTPUT_LEN) \
{ \
NEXT_VAL = VAL + LEN; \
if (0 > --MAX_OUTPUT_LEN) \
{ \
util_out_print_gtmio("Trigger definition too long", FLUSH); \
return FALSE; \
} \
*NEXT_VAL++ = '\0'; \
}
#define PROCESS_NUMERIC(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \
{ \
UPDATE_DST(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN); \
while (ISDIGIT_ASCII(*PTR)) \
{ \
if (!HAVE_STAR) \
{ \
if (MAX_LEN < ++DST_LEN) \
{ \
util_out_print_gtmio("Subscript too long", FLUSH); \
return FALSE; \
} \
*DST_PTR++ = *PTR++; \
} else \
PTR++; \
LEN--; \
} \
}
#define PROCESS_STRING(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, MAX_LEN) \
{ \
char *ptr1; \
int add_len; \
\
ptr1 = scan_to_end_quote(PTR, LEN, MAX_LEN); \
if (NULL == ptr1) \
{ \
util_out_print_gtmio("Invalid string", FLUSH); \
return FALSE; \
} \
add_len = (int)(ptr1 - PTR); \
LEN -= add_len; \
if (!HAVE_STAR) \
{ \
if (MAX_LEN < (DST_LEN + add_len)) \
{ \
util_out_print_gtmio("Trigger definition too long", FLUSH); \
return FALSE; \
} \
memcpy(DST_PTR, PTR, add_len); \
DST_PTR += add_len; \
DST_LEN += add_len; \
} \
PTR = ptr1; \
}
#define PROCESS_AND_GET_NUMERIC(PTR, LEN, HAVE_STAR, DST_PTR, DST_LEN, NUM, MAX_LEN) \
{ \
char *ptr1; \
int add_len; \
\
ptr1 = PTR; \
A2I(ptr1, PTR + LEN, NUM); \
add_len = (int)(ptr1 - PTR); \
LEN -= add_len; \
if (!HAVE_STAR) \
{ \
if (MAX_LEN < (DST_LEN + add_len)) \
{ \
util_out_print_gtmio("Subscript too long", FLUSH); \
return FALSE; \
} \
memcpy(DST_PTR, PTR, add_len); \
DST_PTR += add_len; \
DST_LEN += add_len; \
} \
PTR = ptr1; \
}
#define GET_CLI_STR(QUAL, MAX_OUTPUT_LEN, VALUE, RES) \
{ \
unsigned short qual_len; \
\
qual_len = (unsigned short)MAX_OUTPUT_LEN; \
assert(MAX_OUTPUT_LEN == qual_len); /* make sure it fits in short */ \
if (FALSE == cli_get_str(QUAL, VALUE, &qual_len)) \
return FALSE; \
RES = qual_len; \
}
#define GET_CLI_STR_AND_CHECK(QUAL, QUAL_PRESENT, MAX_OUTPUT_LEN, CHECK_FN, VAL, VAL_LEN, NEXT_VAL, ERR_MSG) \
{ \
unsigned short qual_str_len; \
\
if (CLI_PRESENT == (QUAL_PRESENT = cli_present(QUAL))) \
{ \
GET_CLI_STR(QUAL, MAX_OUTPUT_LEN, VAL, qual_str_len); \
VAL_LEN = qual_str_len; \
if (!CHECK_FN(VAL, &VAL_LEN)) \
{ \
if (0 == STR_LIT_LEN(ERR_MSG)) \
return FALSE; \
else \
{ \
ERROR_MSG_RETURN(ERR_MSG, VAL_LEN, VAL); \
} \
} \
} else \
VAL_LEN = 0; \
/* cli_get_str() will put the string in the buffer (based on max_output_len) \
* but the check_*() function might take another pass over the string in the \
* output buffer and consolidate it -- for example piece can reduce 2;3;4;5 \
* to 2:5. So don't adjust max_output_len until we know the actual length. \
*/ \
MAX_OUTPUT_LEN -= VAL_LEN; \
UPDATE_TRIG_PTRS(VAL, NEXT_VAL, VAL_LEN, MAX_OUTPUT_LEN); \
}
GBLREF char cli_err_str[];
GBLREF boolean_t gtm_cli_interpret_string;
LITREF unsigned char lower_to_upper_table[];
LITREF mval gvtr_cmd_mval[GVTR_CMDTYPES];
STATICFNDEF char *scan_to_end_quote(char *ptr, int len, int max_len)
{
int str_len = 0;
if ((1 >= len) || ('"' != *ptr))
return NULL; /* Invalid string - it needs at least "" */
if (max_len < ++str_len)
{
util_out_print_gtmio("String too long", FLUSH);
return NULL;
}
ptr++;
len--;
for ( ; 0 < len; len--, ptr++)
{ /* Scan until the closing quote */
if ('"' == *ptr)
{
if (1 == len)
break;
if ('"' == *(ptr + 1))
{
if (max_len < ++str_len)
{
util_out_print_gtmio("String too long", FLUSH);
return NULL;
}
ptr++;
len--;
} else
break;
}
if (max_len < ++str_len)
{
util_out_print_gtmio("String too long", FLUSH);
return NULL;
}
}
return (('"' == *ptr) ? ptr + 1 : NULL);
}
STATICFNDEF boolean_t process_dollar_char(char **src_ptr, int *src_len, boolean_t have_star, char **d_ptr, int *dst_len)
{
int char_count;
char *char_ptr;
int len;
char *dst_ptr;
char dst_string[MAX_DCHAR_LEN];
int lcl_dst_len;
mstr m_dst;
mstr m_src;
char *ptr;
int q_len;
char *tmp_dst_ptr;
tmp_dst_ptr = dst_ptr = *d_ptr;
ptr = *src_ptr;
len = *src_len;
lcl_dst_len = *dst_len;
assert('$' == *ptr);
UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN);
if (0 == len)
return FALSE;
switch (*ptr)
{
case 'c':
case 'C':
UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN);
if ((0 < len) && ('(' == *ptr))
break;
else if ((3 < len) && ('H' == lower_to_upper_table[*ptr])
&& ('A' == lower_to_upper_table[*(ptr + 1)])
&& ('R' == lower_to_upper_table[*(ptr + 2)]) && ('(' == *(ptr + 3)))
{
ptr += 3;
len -= 3;
break;
}
else
return FALSE;
break;
case 'z':
case 'Z':
UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN);
if ((2 < len) && ('C' == lower_to_upper_table[*ptr])
&& ('H' == lower_to_upper_table[*(ptr + 1)]) && ('(' == *(ptr + 2)))
{
ptr += 2;
len -= 2;
}
else if ((4 < len) && ('C' == lower_to_upper_table[*ptr])
&& ('H' == lower_to_upper_table[*(ptr + 1)])
&& ('A' == lower_to_upper_table[*(ptr + 2)])
&& ('R' == lower_to_upper_table[*(ptr + 3)]) && ('(' == *(ptr + 4)))
{
ptr += 4;
len -= 4;
}
else
return FALSE;
if (MAX_GVSUBS_LEN < lcl_dst_len + 2)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
MEMCPY_LIT(dst_ptr, "CH");
dst_ptr += 2;
lcl_dst_len += 2;
break;
default:
return FALSE;
}
assert('(' == *ptr);
UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN);
while ((0 < len) && (')' != *ptr))
{
UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN);
}
q_len = 0;
if (!have_star)
{
if (MAX_GVSUBS_LEN < ++lcl_dst_len)
{
util_out_print_gtmio("$[Z]CHAR expression too long", FLUSH);
return FALSE;
}
*dst_ptr++ = *ptr++;
*dst_ptr = '\0';
m_src.addr = tmp_dst_ptr;
m_src.len = (mstr_len_t)(dst_ptr - tmp_dst_ptr);
m_dst.addr = dst_string;
m_dst.len = 0;
if (!zwr2format(&m_src, &m_dst))
return FALSE;
lcl_dst_len = *dst_len; /* Reset length because we're creating the final version now */
if (MAX_GVSUBS_LEN < ++lcl_dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*tmp_dst_ptr++ = '"';
char_ptr = m_dst.addr;
if (MAX_GVSUBS_LEN < (lcl_dst_len + m_dst.len))
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
for (char_count = 0; m_dst.len > char_count; char_count++)
{
if ('"' == *char_ptr)
{
if (MAX_GVSUBS_LEN < ++lcl_dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*tmp_dst_ptr++ = '"';
q_len++;
}
*tmp_dst_ptr++ = *char_ptr++;
lcl_dst_len++;
}
if (MAX_GVSUBS_LEN < ++lcl_dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*tmp_dst_ptr++ = '"';
dst_ptr = tmp_dst_ptr;
}
else
ptr++;
assert(!have_star || ((dst_ptr == *d_ptr) && (lcl_dst_len == *dst_len)));
*src_ptr = ptr;
*src_len = len + 2 + q_len; /* Allow for the open and close quotes and any internal quotes */
*d_ptr = dst_ptr;
*dst_len = lcl_dst_len;
return TRUE;
}
STATICFNDEF boolean_t process_delim(char *delim_str, uint4 *delim_len)
{
int char_count;
mstr dst;
int dst_len;
char *dst_ptr;
char dst_string[MAX_DELIM_LEN + 1];
uint4 len;
char *ptr;
char *ptr1;
int q_len;
char src_string[MAX_DELIM_LEN + 1];
mstr src;
char *src_ptr;
if (MAX_DELIM_LEN < *delim_len)
{
util_out_print_gtmio("Delimiter too long", FLUSH);
return FALSE;
}
ptr = delim_str;
len = *delim_len;
src_ptr = src_string;
dst_len = 0;
/* If ", scan to end quote
* If $, look for char --> c or zchar --> zch
* If _, leave it
*/
while (0 < len)
{
switch (*ptr)
{
case '"':
PROCESS_STRING(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN);
break;
case '$':
UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN);
if (0 == len)
{
util_out_print_gtmio("Invalid entry in delimiter", FLUSH);
return FALSE;
}
if ((3 < len) && ('C' == lower_to_upper_table[*ptr])
&& ('H' == lower_to_upper_table[*(ptr + 1)])
&& ('A' == lower_to_upper_table[*(ptr + 2)])
&& ('R' == lower_to_upper_table[*(ptr + 3)]))
{
if (MAX_DELIM_LEN < ++dst_len)
{
util_out_print_gtmio("Trigger definition too long", FLUSH);
return FALSE;
}
*src_ptr++ = 'C';
ptr += 4;
len -= 4;
} else if ((4 < len) && ('Z' == lower_to_upper_table[*ptr])
&& ('C' == lower_to_upper_table[*(ptr + 1)])
&& ('H' == lower_to_upper_table[*(ptr + 2)])
&& ('A' == lower_to_upper_table[*(ptr + 3)])
&& ('R' == lower_to_upper_table[*(ptr + 4)]))
{
if (MAX_DELIM_LEN < (dst_len + 3))
{
util_out_print_gtmio("Trigger definition too long", FLUSH);
return FALSE;
}
MEMCPY_LIT(src_ptr, "ZCH");
src_ptr += 3;
dst_len += 3;
ptr += 5;
len -= 5;
} else
{
UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN);
}
break;
default:
UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN);
break;
}
}
*src_ptr = '\0';
src.addr = src_string;
src.len = (mstr_len_t)(src_ptr - src_string);
dst.addr = dst_string;
dst.len = 0;
if (!zwr2format(&src, &dst))
{
util_out_print_gtmio("Invalid delimiter", FLUSH);
return FALSE;
}
if (MAX_DELIM_LEN < dst.len)
{
util_out_print_gtmio("Delimiter too long", FLUSH);
return FALSE;
}
memcpy(delim_str, dst_string, dst.len);
*delim_len = dst.len;
return TRUE;
}
boolean_t check_trigger_name(char *name_str, uint4 *name_len)
{
uint4 len, lcl_name_len;
unsigned char *ptr;
/* To conform to the other uses of the check_* functions and their use in the GET_CLI_STR_AND_CHECK macro
* name_len is passed in as a uint4 *, but in this case a uint4 would suffice. To mitigate the dereferencing
* issues, a local copy is used.
*/
lcl_name_len = *name_len;
if (MAX_USER_TRIGNAME_LEN < lcl_name_len)
return FALSE;
ptr = (unsigned char *)name_str;
if (('%' != *ptr) && !ISALPHA_ASCII(*ptr))
return FALSE;
for (len = lcl_name_len - 1, ptr++; (0 < len) && ISALNUM_ASCII(*ptr); ptr++, len--)
;
return (0 == len);
}
STATICFNDEF boolean_t process_options(char *option_str, uint4 option_len, boolean_t *isolation, boolean_t *noisolation,
boolean_t *consistency, boolean_t *noconsistency)
{
char local_options[MAX_OPTIONS_LEN];
char *ptr;
if (MAX_OPTIONS_LEN < option_len)
{
util_out_print_gtmio("Too many options", FLUSH);
return FALSE;
}
memcpy(local_options, option_str, option_len);
*isolation = *noisolation = *consistency = *noconsistency = FALSE;
ptr = local_options;
for ( ; 0 < option_len; ptr++, option_len--)
*ptr = TOUPPER(*ptr);
ptr = strtok(local_options, ",");
do
{
switch (*ptr)
{
case 'C':
if (1 == STRLEN(ptr))
*consistency = TRUE;
else
{
assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_CONSISTENCY));
*consistency = TRUE;
}
break;
case 'I':
if (1 == STRLEN(ptr))
*isolation = TRUE;
else
{
assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_ISOLATION));
*isolation = TRUE;
}
break;
case 'N':
assert('O' == *(ptr + 1));
if ('C' == *(ptr + 2))
{
assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOCONSISTENCY));
*noconsistency = TRUE;
} else
{
assert('I' == *(ptr + 2));
assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOISOLATION));
*noisolation = TRUE;
}
break;
default:
GTMASSERT; /* Parsing should have found invalid command */
break;
}
} while (ptr = strtok(NULL, ","));
return !((*isolation && *noisolation) || (*consistency && *noconsistency));
}
STATICFNDEF boolean_t process_subscripts(char *subscr_str, uint4 *subscr_len, char **next_str)
{
boolean_t alternation;
char dst[MAX_GVSUBS_LEN];
int dst_len;
char *dst_ptr;
int len;
boolean_t have_pattern;
boolean_t have_range;
boolean_t have_star;
boolean_t have_dot;
int i;
int lvn_count;
uint4 lvn_len[MAX_LVN_COUNT];
char *lvn_start;
char *lvn_str[MAX_LVN_COUNT];
int multiplier;
boolean_t newsub;
int num1;
int num2;
char *ptr;
char *ptr1;
char *save_dst_ptr;
int save_len;
char *start_dst_ptr;
uint4 start_len;
uint4 subsc_count;
uint4 tmp_len;
boolean_t valid_sub;
error_def(ERR_INVSTRLEN);
have_pattern = have_range = valid_sub = have_star = FALSE;
ptr = subscr_str;
start_len = len = *subscr_len;
dst_len = 0;
newsub = TRUE;
subsc_count = lvn_count = 0;
start_dst_ptr = dst_ptr = dst;
while ((0 < len) && (')' != *ptr))
{
if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr))
{
PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
newsub = FALSE;
valid_sub = TRUE;
}
else if (ISALPHA_ASCII(*ptr) || ('%' == *ptr))
{
if (!newsub)
{
util_out_print_gtmio("Invalid subscript", FLUSH);
return FALSE;
}
lvn_start = ptr;
tmp_len = 0;
do
{
if (MAX_MIDENT_LEN < ++tmp_len)
{
util_out_print_gtmio("Variable name too long", FLUSH);
return FALSE;
}
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = *ptr++;
len--;
} while (ISALNUM_ASCII(*ptr));
if ('=' != *ptr)
{
util_out_print_gtmio("Invalid variable name in subscript", FLUSH);
return FALSE;
}
for (i = 0; i < lvn_count; i++)
{
if ((lvn_len[i] == tmp_len) && (0 == strncmp(lvn_str[i], lvn_start, tmp_len)))
{
util_out_print_gtmio("Duplicate variable name in subscript", FLUSH);
return FALSE;
}
}
lvn_str[lvn_count] = lvn_start;
lvn_len[lvn_count] = tmp_len;
lvn_count++;
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = *ptr++; /* move past = */
start_dst_ptr = dst_ptr;
len--;
if ((0 < len) && ((',' == *ptr) || (')' == *ptr)))
{
util_out_print_gtmio("Variable name not followed by valid subscript", FLUSH);
return FALSE;
}
continue;
} else
{
switch (*ptr)
{
case '"':
PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
valid_sub = TRUE;
newsub = FALSE;
break;
case '$':
if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len))
{
util_out_print_gtmio("Invalid subscript", FLUSH);
return FALSE;
}
newsub = FALSE;
valid_sub = TRUE;
break;
case '?':
if (have_range)
{
util_out_print_gtmio("Range and pattern match not valid in same subscript", FLUSH);
return FALSE;
}
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
ptr1 = ptr;
alternation = FALSE;
while ((0 < len) && ((',' != *ptr) || alternation) && ((')' != *ptr) || alternation)
&& (';' != *ptr))
{
num1 = num2 = -1;
have_dot = FALSE;
if (ISDIGIT_ASCII(*ptr))
{
PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num1,
MAX_GVSUBS_LEN);
}
if ('.' == *ptr)
{
have_dot = TRUE;
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = *ptr++;
len--;
if (ISDIGIT_ASCII(*ptr))
{
PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num2,
MAX_GVSUBS_LEN);
}
if (-1 == num1)
num1 = 0;
}
switch (*ptr)
{
case '(':
if (!alternation && (0 <= num1))
{
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len,
MAX_GVSUBS_LEN);
alternation = TRUE;
continue;
}
break;
case ',':
if (alternation && (-1 == num1) && (-1 == num2))
{
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len,
MAX_GVSUBS_LEN);
continue;
}
break;
case ')':
if (alternation && (-1 == num1) && (-1 == num2))
{
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len,
MAX_GVSUBS_LEN);
alternation = FALSE;
continue;
}
break;
default:
break;
}
if ((0 <= num1) && (0 <= num2) && (num2 < num1))
{
util_out_print_gtmio("Invalid pattern match range", FLUSH);
return FALSE;
}
switch (toupper(*ptr))
{
case 'E':
if (have_dot && (0 >= num1) && (-1 == num2))
{ /* Treat ?.E the same as * */
have_star = TRUE;
dst_ptr = start_dst_ptr;
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = '*';
}
/* Note: we're dropping into the following case */
case 'A':
case 'C':
case 'K':
case 'L':
case 'N':
case 'P':
case 'U':
case 'V':
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
break;
case '"':
PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len,
MAX_GVSUBS_LEN);
break;
/* Note: we're dropping into the default/error case */
default:
CONV_CH_AND_PRINT("Unexpected character !AD in pattern code", ptr);
return FALSE;
}
}
have_pattern = TRUE;
valid_sub = TRUE;
newsub = FALSE;
break;
case ':':
if (have_range)
{
util_out_print_gtmio("Range within a range not allowed", FLUSH);
return FALSE;
}
if (have_pattern)
{
util_out_print_gtmio("Pattern not allowed as a range", FLUSH);
return FALSE;
}
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr))
{
PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
}
else if ('"' == *ptr)
{
PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
} else if ('$' == *ptr)
{
if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len))
{
util_out_print_gtmio("Invalid range value", FLUSH);
return FALSE;
}
} else if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr))
{
util_out_print_gtmio("Invalid string range", FLUSH);
return FALSE;
} else
{ /* A range with no lower end - just scan the numeric or string */
ptr1 = ptr;
if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr))
{
PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
} else if ('"' == *ptr1)
{
PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
} else if ((0 < len) && ((',' != *ptr) && (';' != *ptr) && (')' != *ptr)))
{
util_out_print_gtmio("Range value must be integer or string", FLUSH);
return FALSE;
} else if (!valid_sub)
{ /* this is a single ":" - treat it just like a * */
have_star = TRUE;
dst_ptr = start_dst_ptr;
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = '*';
}
}
valid_sub = TRUE;
newsub = FALSE;
have_range = TRUE;
break;
case '*':
if (!have_star)
{
have_star = TRUE;
dst_ptr = start_dst_ptr;
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = '*';
}
ptr++;
len--;
if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr))
{
util_out_print_gtmio("Invalid use of *", FLUSH);
return FALSE;
} else
valid_sub = TRUE;
newsub = FALSE;
break;
case ';':
UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN);
/* Delete extraneous ; in the subscript */
if ((!have_star) && (newsub || ((0 < len)
&& ((',' == *ptr) || (';' == *ptr) || ISALPHA_ASCII(*ptr)
|| ('%' == *ptr) || (')' == *ptr)))))
dst_ptr--;
valid_sub = FALSE;
have_pattern = have_range = FALSE;
break;
case ',':
if (newsub)
{
util_out_print_gtmio("Empty subscript not allowed", FLUSH);
return FALSE;
}
if (MAX_GVSUBSCRIPTS <= ++subsc_count)
{
util_out_print_gtmio("Too many subscripts", FLUSH);
return FALSE;
}
if (MAX_GVSUBS_LEN < ++dst_len)
{
util_out_print_gtmio("Subscript too long", FLUSH);
return FALSE;
}
*dst_ptr++ = *ptr++;
len--;
start_dst_ptr = dst_ptr;
newsub = TRUE;
have_range = have_pattern = valid_sub = have_star = FALSE;
break;
default:
CONV_CH_AND_PRINT("Invalid character !AD in subscript", ptr);
return FALSE;
}
}
}
if ((0 == len) && (')' != *ptr))
{
util_out_print_gtmio("Missing \")\" after global subscript", FLUSH);
return FALSE;
}
if ((start_len == len) || newsub)
{
util_out_print_gtmio("Empty subscript not allowed", FLUSH);
return FALSE;
}
if ((')' == *ptr) && (MAX_GVSUBSCRIPTS <= ++subsc_count))
{
util_out_print_gtmio("Too many subscripts", FLUSH);
return FALSE;
}
memcpy(subscr_str, dst, (int)(dst_ptr - dst));
*subscr_len = (uint4)(dst_ptr - dst);
*next_str = ptr + 1;
return TRUE;
}
STATICFNDEF boolean_t process_pieces(char *piece_str, uint4 *piece_len)
{
uint bit;
uint4 bitmap[MAX_PIECE_INT];
uint bitval;
uint bitword;
boolean_t have_low_num;
boolean_t have_num;
boolean_t have_num_in_str;
uint i;
uint4 len;
int low;
int num;
char *ptr;
char *ptr1;
boolean_t have_error = FALSE;
int num_len;
memset((void *)bitmap, 0, MAX_PIECE_CHARS);
ptr = piece_str;
len = *piece_len;
have_num = have_low_num = FALSE;
if ('"' == *ptr)
{
ptr++;
len--;
}
while ((0 < len) && !have_error)
{
switch (*ptr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
ptr1 = ptr;
A2I(ptr1, ptr + len, num);
if ((0 == num) || (MAX_PIECE_VALUE < num))
{
CONV_NUM_AND_STR_AND_PRINT("Invalid value !UL in PIECES - !AD", num, *piece_len, piece_str);
have_error = TRUE;
break;
}
len -= (int)(ptr1 - ptr);
ptr = ptr1;
bitmap[num / BITS_PER_INT] |= 1 << (num % BITS_PER_INT);
have_num = TRUE;
have_low_num = TRUE;
break;
case ';':
if (!have_num)
{
CONV_STR_AND_PRINT("Expected an integer in PIECES - ", *piece_len, piece_str);
have_error = TRUE;
break;
}
have_num = FALSE;
ptr++;
len--;
break;
case ':':
if (!have_low_num)
{
CONV_STR_AND_PRINT("Expected an integer for lower value in range in PIECES - ",
*piece_len, piece_str);
have_error = TRUE;
break;
}
ptr++;
len--;
low = num;
if (!ISDIGIT_ASCII(*ptr))
{
CONV_STR_AND_PRINT("Expected an integer for upper value in range in PIECES - ",
*piece_len, piece_str);
have_error = TRUE;
break;
}
ptr1 = ptr;
A2I(ptr1, ptr + len, num);
if (MAX_PIECE_VALUE < num)
{
CONV_NUM_AND_STR_AND_PRINT("Invalid value \"!UL\" in PIECES - !AD", num,
*piece_len, piece_str);
have_error = TRUE;
break;
}
if (num <= low)
{
CONV_STR_AND_PRINT("Low value of range not less than high value in PIECES - ",
*piece_len, piece_str);
have_error = TRUE;
break;
}
len -= (int)(ptr1 - ptr);
ptr = ptr1;
for (i = low + 1; i <= num; i++)
bitmap[i / BITS_PER_INT] |= 1 << (i % BITS_PER_INT);
have_num = TRUE;
have_low_num = FALSE;
break;
case '"':
if (1 == len)
{
len--;
break;
}
default:
CONV_CH_AND_STR_AND_PRINT("Invalid character !AD in PIECES - !AD", ptr, *piece_len, piece_str);
have_error = TRUE;
break;
}
}
if ((0 != len) || !have_num || have_error)
{
if (!have_error)
CONV_STR_AND_PRINT("Invalid PIECE entry - ", *piece_len, piece_str);
return FALSE;
}
have_num = have_low_num = have_num_in_str = FALSE;
ptr = piece_str;
len = 0;
for (i = 0; i < MAX_PIECE_INT; i++)
{
if ((0 == bitmap[i] && !have_low_num))
continue;
bitword = bitmap[i];
bit = 0;
while ((0 != bitword) || have_low_num)
{
bitval = bitword & 1;
if (!have_low_num)
{
if (0 == bitval)
{
bitword = bitword >> 1;
bit++;
continue;
}
have_num = TRUE;
have_low_num = TRUE;
low = i * BITS_PER_INT + bit;
if (have_num_in_str)
{
*ptr++ = ';';
len++;
}
num_len = 0;
I2A(ptr, num_len, low);
len += num_len;
ptr += num_len;
have_num_in_str = TRUE;
}
while ((1 == bitval) && (BITS_PER_INT > bit))
{
bitword = bitword >> 1;
bitval = bitword & 1;
bit++;
}
if (BITS_PER_INT == bit)
break;
num = (0 == bit) ? i * BITS_PER_INT - 1 : i * BITS_PER_INT + bit - 1;
have_low_num = FALSE;
if (num == low)
continue;
*ptr++ = ':';
len++;
num_len = 0;
I2A(ptr, num_len, num);
len += num_len;
ptr += num_len;
}
}
*ptr = '\0';
*piece_len = len;
return TRUE;
}
STATICFNDEF boolean_t process_xecute(char *xecute_str, uint4 *xecute_len, boolean_t multi_line)
{
uint4 dst_len;
char dst_string[MAX_SRCLINE];
uint4 src_len;
gv_trigger_t trigdsc;
src_len = *xecute_len;
if ((src_len == XTENDED_START_LEN) && (0 == memcmp(XTENDED_START, xecute_str, XTENDED_START_LEN)))
return TRUE;
if (!multi_line && (NULL != memchr(xecute_str, '\n', src_len)))
{
util_out_print_gtmio("Newline not allowed in XECUTE string", FLUSH);
return FALSE;
}
if (!multi_line && MAX_SRCLINE < src_len + 1) /* allow room for leading blank */
{
return FALSE;
}
if (0 == src_len)
{
util_out_print_gtmio("Empty XECUTE string is invalid", FLUSH);
return FALSE;
}
if (!multi_line)
{
dst_string[0] = ' ';
if (!trigger_scan_string(xecute_str, &src_len, dst_string + 1, &dst_len) || (1 != src_len))
{
util_out_print_gtmio("Invalid XECUTE string", FLUSH);
return FALSE;
}
memcpy(xecute_str, dst_string, ++dst_len);
*xecute_len = dst_len;
trigdsc.xecute_str.str.addr = dst_string;
trigdsc.xecute_str.str.len = dst_len;
} else
{
trigdsc.xecute_str.str.addr = xecute_str;
trigdsc.xecute_str.str.len = src_len;
}
/* Test compile the string - first build up the parm list to trigger compile routine */
trigdsc.rtn_desc.rt_name.addr = TRIGR_PRECOMP_RTNNAME;
trigdsc.rtn_desc.rt_name.len = SIZEOF(TRIGR_PRECOMP_RTNNAME) - 1;
trigdsc.rtn_desc.rt_adr = NULL;
return (0 == gtm_trigger_complink(&trigdsc, FALSE));
}
boolean_t trigger_parse(char *input, uint4 input_len, char *trigvn, char **values, uint4 *value_len, int4 *max_len,
boolean_t *multi_line_xecute)
{
boolean_t cli_status;
int cmd_ind;
int count;
int eof;
int extra;
boolean_t found_zkill;
boolean_t found_kill;
uint4 len;
int4 max_output_len;
int parse_ret;
boolean_t delim_present, name_present, pieces_present, xecute_present, zdelim_present;
char *ptr;
char *ptr1;
char *ptr2;
int4 rec_num;
CLI_ENTRY *save_cmd_ary;
boolean_t set_present;
int trigvn_len;
boolean_t in_multi_line_xecute, out_multi_line_xecute;
max_output_len = *max_len;
in_multi_line_xecute = out_multi_line_xecute = *multi_line_xecute;
if (in_multi_line_xecute)
{
if ((XTENDED_STOP_LEN != input_len) || (0 != memcmp(XTENDED_STOP, input, XTENDED_STOP_LEN)))
{
ptr1 = values[XECUTE_SUB] + value_len[XECUTE_SUB];
if (0 < (max_output_len - input_len - 1))
{
memcpy(ptr1, input, input_len);
ptr1 += input_len;
*ptr1 = '\n';
max_output_len -= (input_len + 1);
value_len[XECUTE_SUB] += input_len + 1;
*max_len = max_output_len;
return TRIG_SUCCESS;
} else
{
*multi_line_xecute = FALSE;
util_out_print_gtmio("Trigger definition too long", FLUSH);
return FALSE;
}
}
out_multi_line_xecute = FALSE;
if (!process_xecute(values[XECUTE_SUB], &value_len[XECUTE_SUB], TRUE))
{
*multi_line_xecute = FALSE;
ERROR_MSG_RETURN("Error parsing XECUTE string: ", value_len[XECUTE_SUB], values[XECUTE_SUB]);
}
*multi_line_xecute = out_multi_line_xecute;
*max_len = max_output_len;
return TRIG_SUCCESS;
}
ptr1 = input;
len = (uint4)input_len;
if ('^' != *ptr1++)
{
ERROR_MSG_RETURN("Missing global name:\n", input_len, input);
}
ptr = ptr1;
len--;
if (('%' != *ptr1) && !ISALPHA_ASCII(*ptr1))
{
ERROR_MSG_RETURN("Invalid global name:\n", input_len, input);
}
ptr1++;
while (ISALNUM_ASCII(*ptr1))
ptr1++;
if (('(' != *ptr1) && !ISSPACE_ASCII(*ptr1))
{
ERROR_MSG_RETURN("Invalid global name:\n", input_len, input);
}
trigvn_len = (int)(ptr1 - ptr);
if (MAX_MIDENT_LEN < trigvn_len)
{
trigvn_len = MAX_MIDENT_LEN;
util_out_print_gtmio("Warning: global name truncated to ^!AD", FLUSH, trigvn_len, ptr);
}
memcpy(trigvn, ptr, trigvn_len);
trigvn[trigvn_len] = '\0';
/* lookup global to get collation info */
len -= (uint4)trigvn_len;
ptr2 = ptr1;
if ('(' == *ptr1)
{
ptr1++;
len--;
if (!process_subscripts(ptr1, &len, &ptr2))
{
ERROR_MSG_RETURN("", input_len, input);
}
CHECK_FOR_ROOM_IN_OUTPUT_AND_COPY(ptr1, values[GVSUBS_SUB], len, max_output_len);
} else
len = 0;
if (0 > --max_output_len)
{
util_out_print_gtmio("Trigger definition too long", FLUSH);
return TRIG_FAILURE;
}
values[GVSUBS_SUB][len] = '\0';
value_len[GVSUBS_SUB] = (uint4)len;
save_cmd_ary = cmd_ary;
cmd_ary = &trigger_cmd_ary[0];
gtm_cli_interpret_string = FALSE;
cli_str_setup(input_len - (uint4)(ptr2 - input), ptr2);
parse_ret = parse_triggerfile_cmd();
gtm_cli_interpret_string = TRUE;
cmd_ary = save_cmd_ary;
if (parse_ret)
{
ERROR_MSG_RETURN("Parse error in input:\n", input_len, input);
}
values[CMD_SUB] = values[GVSUBS_SUB] + value_len[GVSUBS_SUB] + 1;
if (CLI_PRESENT == cli_present("COMMANDS"))
{
GET_CLI_STR("COMMANDS", max_output_len, values[CMD_SUB], value_len[CMD_SUB]);
ptr = values[CMD_SUB];
count = 0;
found_zkill = found_kill = FALSE;
/* The following order (set, kill, zkill, ztkill) needs to be maintained to match
* the corresponding bit value orders in gv_trig_cmd_table.h
*/
if (CLI_PRESENT == (set_present = cli_present("COMMANDS.SET")))
{
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_SET].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_SET].str.addr,
max_output_len);
}
if (CLI_PRESENT == cli_present("COMMANDS.KILL"))
{
found_kill = TRUE;
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_KILL].str.len, gvtr_cmd_mval[GVTR_CMDTYPE_KILL].str.addr,
max_output_len);
}
if (CLI_PRESENT == cli_present("COMMANDS.ZKILL"))
{
found_zkill = TRUE;
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.len,
gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.addr, max_output_len);
}
if (CLI_PRESENT == cli_present("COMMANDS.ZWITHDRAW") && !found_zkill)
{
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.len,
gvtr_cmd_mval[GVTR_CMDTYPE_ZKILL].str.addr, max_output_len);
}
if (CLI_PRESENT == cli_present("COMMANDS.ZTKILL"))
{
if (found_kill)
ERROR_MSG_RETURN("KILL and ZTKILL incompatible: ", value_len[CMD_SUB], values[CMD_SUB]);
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZTKILL].str.len,
gvtr_cmd_mval[GVTR_CMDTYPE_ZTKILL].str.addr, max_output_len);
}
if (CLI_PRESENT == cli_present("COMMANDS.ZTRIGGER"))
{
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, gvtr_cmd_mval[GVTR_CMDTYPE_ZTRIGGER].str.len,
gvtr_cmd_mval[GVTR_CMDTYPE_ZTRIGGER].str.addr, max_output_len);
}
*ptr = '\0';
value_len[CMD_SUB] = STRLEN(values[CMD_SUB]);
} else
value_len[CMD_SUB] = 0;
UPDATE_TRIG_PTRS(values[CMD_SUB], values[DELIM_SUB], value_len[CMD_SUB], max_output_len);
GET_CLI_STR_AND_CHECK("DELIM", delim_present, max_output_len, process_delim, values[DELIM_SUB], value_len[DELIM_SUB],
values[TRIGNAME_SUB], "Error parsing DELIM string: ");
GET_CLI_STR_AND_CHECK("NAME", name_present, max_output_len, check_trigger_name, values[TRIGNAME_SUB],
value_len[TRIGNAME_SUB], values[OPTIONS_SUB], "Error parsing NAME string: ");
if (CLI_PRESENT == cli_present("OPTIONS"))
{
boolean_t isolation, noisolation, consistency, noconsistency;
GET_CLI_STR("OPTIONS", max_output_len, values[OPTIONS_SUB], value_len[OPTIONS_SUB]);
if (!process_options(values[OPTIONS_SUB], value_len[OPTIONS_SUB], &isolation, &noisolation, &consistency,
&noconsistency))
{
ERROR_MSG_RETURN("Inconsistent values in OPTIONS string: ", value_len[OPTIONS_SUB], values[OPTIONS_SUB]);
}
count = 0;
ptr = values[OPTIONS_SUB];
if (isolation)
{
ADD_STRING(count, ptr, STRLEN(HASHT_OPT_ISOLATION), HASHT_OPT_ISOLATION, max_output_len);
}
else if (noisolation)
{
ADD_STRING(count, ptr, STRLEN(HASHT_OPT_NOISOLATION), HASHT_OPT_NOISOLATION, max_output_len);
}
if (consistency)
{
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, STRLEN(HASHT_OPT_CONSISTENCY), HASHT_OPT_CONSISTENCY, max_output_len);
}
else if (noconsistency)
{
ADD_COMMA_IF_NEEDED(count, ptr, max_output_len);
ADD_STRING(count, ptr, STRLEN(HASHT_OPT_NOCONSISTENCY), HASHT_OPT_NOCONSISTENCY, max_output_len);
}
*ptr = '\0';
value_len[OPTIONS_SUB] = STRLEN(values[OPTIONS_SUB]);
} else
value_len[OPTIONS_SUB] = 0;
UPDATE_TRIG_PTRS(values[OPTIONS_SUB], values[PIECES_SUB], value_len[OPTIONS_SUB], max_output_len);
GET_CLI_STR_AND_CHECK("PIECES", pieces_present, max_output_len, process_pieces, values[PIECES_SUB], value_len[PIECES_SUB],
values[ZDELIM_SUB], "");
GET_CLI_STR_AND_CHECK("ZDELIM", zdelim_present, max_output_len, process_delim, values[ZDELIM_SUB], value_len[ZDELIM_SUB],
values[XECUTE_SUB], "Error parsing ZDELIM string: ");
/* Since process_xecute() does a test compile of the xecute string, it changes the parsing table from MUPIP TRIGGER
* to GT.M. To avoid problems with saving and restoring the parse state (which would have to be done with globals
* that are static in cli_parse.c), it is much easier to put process_xecute() last in the list of qualifiers to check -
* solving the problem. In other words, don't try to neaten things up by making the qualifiers alphabetical!
*/
if (CLI_PRESENT == (xecute_present = cli_present("XECUTE")))
{
GET_CLI_STR("XECUTE", max_output_len, values[XECUTE_SUB], value_len[XECUTE_SUB]);
out_multi_line_xecute |= (value_len[XECUTE_SUB] == XTENDED_START_LEN)
&& (0 == memcmp(XTENDED_START, values[XECUTE_SUB], XTENDED_START_LEN));
if (!process_xecute(values[XECUTE_SUB], &value_len[XECUTE_SUB], out_multi_line_xecute))
{
ptr = values[XECUTE_SUB];
len = value_len[XECUTE_SUB];
ERROR_MSG_RETURN("Error parsing XECUTE string: ", len, ptr);
}
} else
value_len[XECUTE_SUB] = 0;
if (!in_multi_line_xecute)
{
max_output_len -= value_len[XECUTE_SUB];
UPDATE_TRIG_PTRS(values[XECUTE_SUB], values[CHSET_SUB], value_len[XECUTE_SUB], max_output_len);
ptr = values[XECUTE_SUB] + value_len[XECUTE_SUB];
*ptr = '\0';
if (!xecute_present && !out_multi_line_xecute)
{
ERROR_STR_RETURN("XECUTE value missing");
}
if (0 == value_len[CMD_SUB])
{
ERROR_STR_RETURN("COMMANDS value missing");
}
if (delim_present && zdelim_present)
{
ERROR_STR_RETURN("Can't have both DELIM and ZDELIM in same entry");
}
if ((delim_present || zdelim_present) && !set_present)
{
ERROR_STR_RETURN("DELIM and ZDELIM need a commands=SET");
}
if (pieces_present && (!delim_present && !zdelim_present))
{
ERROR_STR_RETURN("PIECES need either DELIM or ZDELIM");
}
if (MAX_HASH_INDEX_LEN < (trigvn_len + 1 + value_len[GVSUBS_SUB] + 1 + value_len[DELIM_SUB] + 1
+ value_len[ZDELIM_SUB] + 1 + value_len[XECUTE_SUB] + 1))
{
ERROR_STR_RETURN("Entry too large to properly index");
}
}
*multi_line_xecute = out_multi_line_xecute;
*max_len = max_output_len;
return TRIG_SUCCESS;
}