fis-gtm/sr_port/op_fnname.c

253 lines
8.7 KiB
C

/****************************************************************
* *
* Copyright 2001, 2009 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 <stdarg.h>
#include "gtm_string.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "stringpool.h"
#include "rtnhdr.h"
#include "mv_stent.h"
#include "fnname.h"
#include "op.h"
#include "gvsub2str.h"
#include "mvalconv.h"
GBLREF gv_key *gv_currkey;
GBLREF spdesc stringpool;
GBLREF mv_stent *mv_chain;
GBLREF unsigned char *msp, *stackwarn, *stacktop;
#ifdef TEST_DOLLAR_NAME_GCOL
#define TEST_FAKE_STRINGPOOL_FULL stringpool.free = stringpool.top /* force gcol at various stages of $NAME processing */
#else
#define TEST_FAKE_STRINGPOOL_FULL
#endif
#define COPY_ARG_TO_STP \
{ \
boolean_t has_str_repsn; \
\
has_str_repsn = MV_IS_STRING(arg) || !MV_DEFINED(arg); \
TEST_FAKE_STRINGPOOL_FULL; \
mval_lex(arg, &format_out); \
if (MV_IS_CANONICAL(arg)) \
{ /* mval_lex doesn't create string representation for canonical arg \
* that already has a string representation. */ \
assert(arg->str.len == format_out.len ); \
assert(arg->str.addr == format_out.addr); \
TEST_FAKE_STRINGPOOL_FULL; \
ENSURE_STP_FREE_SPACE(arg->str.len); \
memcpy(stringpool.free, arg->str.addr, arg->str.len); \
/* use arg 'coz gcol doesn't preserve format_out */ \
} \
if (has_str_repsn) \
{ /* mval_lex copies arg at stringpool.free and USUALLY leaves \
* stringpool.free unchanged. Caller (us) has to update \
* stringpool.free to keep dst protected. EXCEPT: MV_FORCE_STR \
* in mval_lex creates string representation for canonical \
* numbers that did not have string representation and updates \
* stringpool.free as that representation "belongs" to the mval \
* in question - no need to update stringpool.free for such \
* cases. Lucky for us, it sits in the perfect place in the \
* middle of our work has_str_repsn includes not defined check \
* in case of noundef - if undef it comes back as empty string. \
*/ \
stringpool.free += format_out.len; \
} \
} \
#define COPY_SUBSCRIPTS \
for ( ; ; ) \
{ \
arg = va_arg(var, mval *); \
COPY_ARG_TO_STP; \
dst->str.len += format_out.len; \
depth_count--; \
if (0 == depth_count) \
break; \
*stringpool.free++ = ','; \
dst->str.len++; \
}
void op_fnname(UNIX_ONLY_COMMA(int sub_count) mval *finaldst, ...)
{
int space_needed;
int depth_count, fnname_type;
mval *dst, *arg, *depth;
VMS_ONLY(int sub_count;)
mstr format_out;
va_list var;
unsigned char *sptr, *key_ptr, *key_top;
error_def(ERR_MAXSTRLEN);
error_def(ERR_GVNAKED);
error_def(ERR_FNNAMENEG);
error_def(ERR_STACKOFLOW);
error_def(ERR_STACKCRIT);
/* Implementation note: $NAME does not edit check the result, such as if the key size exceeds the maximum for a global.
* So, the result if used in other operations (such as SET, KILL) may generate run time errors (GVSUBOFLOW, etc) */
VAR_START(var, finaldst);
VMS_ONLY(va_count(sub_count);)
assert(3 <= sub_count);
fnname_type = va_arg(var, int);
depth = va_arg(var, mval *); /* if second arg to $NAME not specified, compiler sets depth_count to MAXPOSINT4 */
depth_count = MV_FORCE_INT(depth);
sub_count -=3;
if (depth_count < 0)
{
va_end(var);
rts_error(VARLSTCNT(1) ERR_FNNAMENEG);
}
/* Note about garbage collection : *dst and all possible *arg's in this function are anchored in the stringpool chains
* and preserved during garbage collection (run time argument mvals provided by compiler). So, if we maintain dst->str.len
* as we copy stuff to the stringpool, we are protected from mval_lex->stp_gcol shuffling strings around. Since the
* temporary allocation algorithm used by the compiler may re-use temporaries, it is possible that one of the subscript
* arguments is the destination for the result of $NAME. We have to assign the result mval as the last step, only after
* all arguments have been processed. We create a temporary place-holder for the result on the M stack so that the result
* is also on the stringpool chain and preserved during garbage collection.
* e.g. S X(1)=$NAME($J("hello",100))
* The result of $J is a temporary that is also an argument to $NAME. This temporary also happens to be the temporary to
* hold the result of $NAME.
*/
PUSH_MV_STENT(MVST_MVAL); /* create a temporary on M stack */
dst = &mv_chain->mv_st_cont.mvs_mval;
dst->mvtype = MV_STR;
dst->str.len = 0;
if (fnname_type == FNNAKGBL)
{
if (!gv_currkey || gv_currkey->prev == 0)
{
va_end(var);
rts_error(VARLSTCNT(1) ERR_GVNAKED);
}
/* Reserve enough space for naked reference. Include space for ^() and a maximum of sub_count ',' separators for
* subscripts specified as arguments to $NAME() in addition to those in the naked reference */
space_needed = (int)((STR_LIT_LEN("^()") + ZWR_EXP_RATIO(MAX_KEY_SZ) + sub_count));
TEST_FAKE_STRINGPOOL_FULL;
ENSURE_STP_FREE_SPACE(space_needed);
sptr = stringpool.free;
*sptr++ = '^';
key_ptr = (unsigned char *)&gv_currkey->base[0];
key_top = (unsigned char *)&gv_currkey->base[gv_currkey->prev];
for ( ; (*sptr = *key_ptr++); sptr++)
;
if (0 != depth_count)
{
*sptr++ = '(';
if (key_ptr < key_top)
{
for ( ; ; )
{
sptr = gvsub2str(key_ptr, sptr, TRUE); /* gvsub2str assumes enough buffer available */
while (*key_ptr++)
;
if (depth_count != MAXPOSINT4) /* although this may not make a difference in reality, */
depth_count--; /* do not disturb depth_count (used later) if default */
*sptr++ = ',';
if (0 == depth_count /* fewer subscripts requested than in naked reference */
|| key_ptr >= key_top) /* more subscripts requested than in naked reference */
break;
}
}
/* Naked reference copied, now copy remaining subscripts. From this point on, maintain dst to protect
* against potential string shuffling by mval_lex->stp_gcol
*/
dst->str.len = INTCAST(sptr - stringpool.free);
dst->str.addr = (char *)stringpool.free;
assert(dst->str.len < space_needed);
stringpool.free = sptr;
depth_count = ((sub_count < depth_count) ? sub_count : depth_count);
if (0 != depth_count)
{
COPY_SUBSCRIPTS;
} else
{ /* take off extra , if depth doesn't go into new subs */
stringpool.free--;
dst->str.len--;
}
*stringpool.free++ = ')';
dst->str.len++;
} else
{ /* naked reference, zero depth => result is just the global name */
dst->str.len = INTCAST(sptr - stringpool.free);
dst->str.addr = (char *)stringpool.free;
assert(dst->str.len < space_needed);
stringpool.free = sptr;
}
} else
{
space_needed = (int)(STR_LIT_LEN("^[,]()") + MAX_MIDENT_LEN + sub_count - 1); /* ^[,]GLVN(max of sub_count-1 *
* subscript separator commas) */
TEST_FAKE_STRINGPOOL_FULL;
/* We don't account for subscripts here as they are processed by mval_lex which reserves space if necessary */
ENSURE_STP_FREE_SPACE(space_needed);
dst->str.addr = (char *)stringpool.free;
if (fnname_type & FNGBL)
{
*stringpool.free++ = '^';
dst->str.len++;
}
if (fnname_type & (FNEXTGBL1 | FNEXTGBL2))
{
*stringpool.free++ = ((fnname_type & FNVBAR) ? '|' : '[');
dst->str.len++;
arg = va_arg(var, mval *);
COPY_ARG_TO_STP;
dst->str.len += format_out.len;
sub_count--;
if (fnname_type & FNEXTGBL2)
{
*stringpool.free++ = ',';
dst->str.len++;
arg = va_arg(var, mval *);
COPY_ARG_TO_STP;
dst->str.len += format_out.len;
sub_count--;
}
*stringpool.free++ = ((fnname_type & FNVBAR) ? '|' : ']');
dst->str.len++;
}
arg = va_arg(var, mval *);
assert(MV_IS_STRING(arg) && (arg->str.len <= MAX_MIDENT_LEN));
memcpy(stringpool.free, arg->str.addr, arg->str.len);
stringpool.free += arg->str.len;
dst->str.len += arg->str.len;
sub_count--;
depth_count = ((sub_count < depth_count) ? sub_count : depth_count);
if (0 != depth_count)
{
*stringpool.free++ = '(';
dst->str.len++;
COPY_SUBSCRIPTS;
*stringpool.free++ = ')';
dst->str.len++;
}
}
va_end(var);
if (MAX_STRLEN < dst->str.len)
rts_error(VARLSTCNT(1) ERR_MAXSTRLEN);
*finaldst = *dst;
POP_MV_STENT(); /* don't need no temporary no more */
return;
}