312 lines
12 KiB
C
312 lines
12 KiB
C
/****************************************************************
|
|
* *
|
|
* 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_string.h"
|
|
#include "gtm_stdio.h"
|
|
|
|
#include "lv_val.h"
|
|
#include "gdsroot.h"
|
|
#include "gdskill.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "zwrite.h"
|
|
#include "mlkdef.h"
|
|
#include "zshow.h"
|
|
#include "filestruct.h"
|
|
#include "gdscc.h"
|
|
#include "copy.h"
|
|
#include "jnl.h"
|
|
#include "buddy_list.h"
|
|
#include "hashtab_int4.h" /* needed for tp.h */
|
|
#include "tp.h"
|
|
#include "merge_def.h"
|
|
#include "gvname_info.h"
|
|
#include "op_merge.h"
|
|
#include "op.h"
|
|
#include "gtmmsg.h"
|
|
#include "sgnl.h"
|
|
#include "stringpool.h"
|
|
#include "alias.h"
|
|
#include "callg.h"
|
|
|
|
GBLREF lvzwrite_datablk *lvzwrite_block;
|
|
GBLREF zshow_out *zwr_output;
|
|
GBLREF gv_namehead *gv_target;
|
|
GBLREF gv_key *gv_currkey;
|
|
GBLREF int merge_args;
|
|
GBLREF merge_glvn_ptr mglvnp;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF symval *curr_symval;
|
|
GBLREF zwr_hash_table *zwrhtab; /* Used to track aliases during zwrites */
|
|
GBLREF uint4 zwrtacindx; /* When creating $ZWRTACxxx vars for ZWRite, this holds xxx */
|
|
|
|
LITDEF MSTR_CONST(semi_star, " ;*");
|
|
LITDEF MSTR_CONST(dzwrtac_clean, "$ZWRTAC=\"\"");
|
|
|
|
error_def(ERR_MAXNRSUBSCRIPTS);
|
|
error_def(ERR_MERGEINCOMPL);
|
|
|
|
void lvzwr_out_targkey(mstr *one);
|
|
|
|
|
|
void lvzwr_out_targkey(mstr *one)
|
|
{
|
|
int n, nsubs;
|
|
|
|
zshow_output(zwr_output, lvzwrite_block->curr_name);
|
|
nsubs = lvzwrite_block->curr_subsc;
|
|
if (nsubs)
|
|
{
|
|
*one->addr = '(';
|
|
zshow_output(zwr_output, one);
|
|
for (n = 0 ; ; )
|
|
{
|
|
mval_write(zwr_output, ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual, FALSE);
|
|
if (++n < nsubs)
|
|
{
|
|
*one->addr = ',';
|
|
zshow_output(zwr_output, one);
|
|
} else
|
|
{
|
|
*one->addr = ')';
|
|
zshow_output(zwr_output, one);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void lvzwr_out(lv_val *lvp)
|
|
{
|
|
char buff;
|
|
uchar_ptr_t lastc;
|
|
int n, nsubs, sbs_depth;
|
|
lv_val *dst_lv, *res_lv, *lvpc;
|
|
mstr one;
|
|
mval *subscp, *val, outindx;
|
|
ht_ent_addr *tabent_addr;
|
|
ht_ent_mname *tabent_mname;
|
|
boolean_t htent_added, dump_container;
|
|
zwr_alias_var *newzav, *zav;
|
|
mident_fixed zwrt_varname;
|
|
lvzwrite_datablk *newzwrb;
|
|
gparam_list param_list; /* for op_putindx call through callg */
|
|
|
|
val = &lvp->v;
|
|
assert(lvzwrite_block);
|
|
if (!merge_args)
|
|
{ /* The cases that exist here are:
|
|
* 1) This is a container variable. If the lv_val it refers to has been printed, show that association.
|
|
* Else, "create" a $ZWRTACxxx var/index that will define the value. Then before returning, cause
|
|
* that container var to be dumped with the appropriate $ZWRTACxxx index as the var name.
|
|
* 2) This is an alias base variable. If first time seen, we print normally but record it and put a
|
|
* ";#" tag on the end to signify it is an alias var (doesn't affect value). If we look it up and it
|
|
* is not the first time this lv_val has been printed, then we instead print the statement needed to
|
|
* alias it to the first seen var.
|
|
* 3) This is just a normal var needing to be printed normally.
|
|
*/
|
|
htent_added = FALSE;
|
|
one.addr = &buff;
|
|
one.len = 1;
|
|
lvzwrite_block->zav_added = FALSE;
|
|
if (lvp->v.mvtype & MV_ALIASCONT)
|
|
{ /* Case 1 -- have an alias container */
|
|
assert(curr_symval->alias_activity);
|
|
assert(!LV_IS_BASE_VAR(lvp)); /* verify is subscripted var */
|
|
lvpc = (lv_val *)lvp->v.str.addr;
|
|
assert(lvpc);
|
|
assert(LV_IS_BASE_VAR(lvpc)); /* Verify base var lv_val */
|
|
if (tabent_addr = (ht_ent_addr *)lookup_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc))
|
|
{ /* The value was found, we have a reference we can print now */
|
|
assert(HTENT_VALID_ADDR(tabent_addr, zwr_alias_var, zav));
|
|
*one.addr = '*';
|
|
zshow_output(zwr_output, &one);
|
|
lvzwr_out_targkey(&one);
|
|
*one.addr = '=';
|
|
zshow_output(zwr_output, &one);
|
|
zav = (zwr_alias_var *)tabent_addr->value;
|
|
assert(0 < zav->zwr_var.len);
|
|
zwr_output->flush = TRUE;
|
|
zshow_output(zwr_output, (const mstr *)&zav->zwr_var);
|
|
return;
|
|
}
|
|
/* This lv_val isn't known to us yet. Scan the hash curr_symval hash table to see if it is known as a
|
|
* base variable as we could have a "forward reference" here.
|
|
*/
|
|
tabent_mname = als_lookup_base_lvval(lvpc);
|
|
/* note even though both paths below add a zav, not bothering to set zav_added because that flag is
|
|
* really only (currently) cared about in reference to processing a basevar so we wouldn't
|
|
* be in this code path anyway. Comment here to record potential usage if that changes.
|
|
*/
|
|
if (tabent_mname)
|
|
{ /* Found a base var it can reference -- create a zwrhtab entry for it */
|
|
assert(tabent_mname->key.var_name.len);
|
|
newzav = als_getzavslot();
|
|
newzav->zwr_var = tabent_mname->key.var_name;
|
|
htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr);
|
|
assert(htent_added);
|
|
dump_container = FALSE;
|
|
} else
|
|
{ /* Unable to find lv_val .. must be "orphaned" so we generate a new $ZWRTAC var for it. The first
|
|
* check however is if this is the first $ZWRTAC var being generated for this $ZWR. If yes, generate
|
|
* a $ZWRTAC="" line to preceed it. This will be a flag to load to clear out all existing $ZWRTAC
|
|
* temp vars so there is no pollution between loads of ZWRitten data.
|
|
*/
|
|
if (0 == zwrtacindx++)
|
|
{ /* Put out "dummy" statement that will clear all the $ZWRTAC vars for a clean slate */
|
|
zwr_output->flush = TRUE;
|
|
zshow_output(zwr_output, &dzwrtac_clean);
|
|
}
|
|
MEMCPY_LIT(zwrt_varname.c, DOLLAR_ZWRTAC);
|
|
lastc = i2asc((uchar_ptr_t)zwrt_varname.c + STR_LIT_LEN(DOLLAR_ZWRTAC), zwrtacindx);
|
|
newzav = als_getzavslot();
|
|
newzav->zwr_var.addr = zwrt_varname.c;
|
|
newzav->zwr_var.len = INTCAST(((char *)lastc - &zwrt_varname.c[0]));
|
|
s2pool(&newzav->zwr_var);
|
|
htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr);
|
|
assert(htent_added);
|
|
dump_container = TRUE;
|
|
}
|
|
/* Note value_printed flag in newzav not set since we are NOT dumping the value at this point
|
|
* but only the association. Since the flag is not set, we *will* dump it when we get to that
|
|
* actual variable.
|
|
*/
|
|
*one.addr = '*';
|
|
zshow_output(zwr_output, &one);
|
|
lvzwr_out_targkey(&one);
|
|
*one.addr = '=';
|
|
zshow_output(zwr_output, &one);
|
|
zwr_output->flush = TRUE;
|
|
zshow_output(zwr_output, (const mstr *)&newzav->zwr_var);
|
|
if (dump_container)
|
|
{ /* We want to dump the entire container variable but the name doesn't match the var we are
|
|
* currently dumping so push a new lvzwrite_block onto the stack, fill it in for the current var
|
|
* and call lvzwr_var() to handle it. When done, dismantle the temp lvzwrite_block.
|
|
*/
|
|
newzwrb = (lvzwrite_datablk *)malloc(SIZEOF(lvzwrite_datablk));
|
|
memset(newzwrb, 0, SIZEOF(lvzwrite_datablk));
|
|
newzwrb->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_LVSUBSCRIPTS);
|
|
newzwrb->curr_name = &newzav->zwr_var;
|
|
newzwrb->prev = lvzwrite_block;
|
|
lvzwrite_block = newzwrb;
|
|
lvzwr_var(lvpc, 0);
|
|
assert(newzav->value_printed);
|
|
assert(newzwrb == lvzwrite_block);
|
|
free(newzwrb->sub);
|
|
lvzwrite_block = newzwrb->prev;
|
|
free(newzwrb);
|
|
}
|
|
return;
|
|
} else if (LV_IS_BASE_VAR(lvp) && IS_ALIASLV(lvp))
|
|
{ /* Case 2 -- alias base variable (only base vars have reference counts). Note this can occur with
|
|
* TP save/restore vars since we increment both trefcnt and crefcnt for these hidden copied references.
|
|
* Because of that, we can't assert alias_activity but otherwise it shouldn't affect processing.
|
|
*/
|
|
if (!(htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvp, NULL, &tabent_addr)))
|
|
{ /* Entry already existed -- need to output association rather than values */
|
|
assert(tabent_addr);
|
|
zav = (zwr_alias_var *)tabent_addr->value;
|
|
assert(zav);
|
|
if (zav->value_printed)
|
|
{ /* Value has already been output -- print association this time */
|
|
*one.addr = '*'; /* Flag as creating an alias */
|
|
zshow_output(zwr_output, &one);
|
|
/* Now for (new) variable name */
|
|
zshow_output(zwr_output, lvzwrite_block->curr_name);
|
|
*one.addr = '=';
|
|
zshow_output(zwr_output, &one);
|
|
/* .. and the var name aliasing to (the first seen with this lv_val) */
|
|
assert(zav->zwr_var.len);
|
|
zwr_output->flush = TRUE;
|
|
zshow_output(zwr_output, &zav->zwr_var);
|
|
return;
|
|
}
|
|
/* Else the value for this entry has not yet been printed so let us fall into case 3
|
|
* and get that done. Also set the flag so we mark it as an alias. Note this can happen if
|
|
* a container value for a name is encountered before the base var it points to. We will
|
|
* properly resolve the entry but its value won't have been printed until we actually encounter
|
|
* it in the tree.
|
|
*/
|
|
htent_added = TRUE; /* to force the ;# tag at end of value printing */
|
|
zav->value_printed = TRUE; /* value will be output shortly below */
|
|
} else
|
|
{ /* Entry was added so is first appearance -- give it a value to hold onto and print it */
|
|
newzav = als_getzavslot();
|
|
newzav->zwr_var = *lvzwrite_block->curr_name;
|
|
newzav->value_printed = TRUE; /* or rather it will be shortly.. */
|
|
tabent_addr->value = (void *)newzav;
|
|
lvzwrite_block->zav_added = TRUE;
|
|
/* Note fall into case 3 to print var and value if exists */
|
|
}
|
|
}
|
|
/* Case 3 - everything else */
|
|
if (!MV_DEFINED(val))
|
|
return;
|
|
MV_FORCE_STR(val);
|
|
lvzwr_out_targkey(&one);
|
|
*one.addr = '=';
|
|
zshow_output(zwr_output, &one);
|
|
mval_write(zwr_output, val, !htent_added);
|
|
if (htent_added)
|
|
{ /* output the ";#" tag to indicate this is an alias output */
|
|
zwr_output->flush = TRUE;
|
|
zshow_output(zwr_output, &semi_star);
|
|
}
|
|
} else
|
|
{ /* MERGE assignment from local variable */
|
|
nsubs = lvzwrite_block->curr_subsc;
|
|
if (MARG1_IS_GBL(merge_args))
|
|
{ /* Target global var */
|
|
memcpy(gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->end + 1);
|
|
gv_currkey->end = mglvnp->gblp[IND1]->s_gv_currkey->end;
|
|
for (n = 0 ; n < nsubs; n++)
|
|
{
|
|
subscp = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual;
|
|
MV_FORCE_STR(subscp);
|
|
mval2subsc(subscp, gv_currkey);
|
|
if (!subscp->str.len && (ALWAYS != gv_cur_region->null_subs))
|
|
sgnl_gvnulsubsc();
|
|
}
|
|
MV_FORCE_STR(val);
|
|
op_gvput(val);
|
|
} else
|
|
{ /* Target local var - pre-process target in case is a container */
|
|
assert(MARG1_IS_LCL(merge_args));
|
|
dst_lv = mglvnp->lclp[IND1];
|
|
if (!LV_IS_BASE_VAR(dst_lv))
|
|
{
|
|
LV_SBS_DEPTH(dst_lv, FALSE, sbs_depth);
|
|
if (MAX_LVSUBSCRIPTS < (sbs_depth + nsubs))
|
|
rts_error(VARLSTCNT(3) ERR_MERGEINCOMPL, 0, ERR_MAXNRSUBSCRIPTS);
|
|
}
|
|
param_list.arg[0] = dst_lv; /* this is already protected from stp_gcol by op_merge so no need to
|
|
* push this into the stack for stp_gcol protection. */
|
|
for (n = 0 ; n < nsubs; n++)
|
|
{ /* Note: no need to do push these mvals on the stack before calling op_putindx
|
|
* as lvzwrite_block->sub is already protected by stp_gcol_src.h.
|
|
*/
|
|
param_list.arg[n+1] = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual;
|
|
}
|
|
param_list.n = n + 1;
|
|
dst_lv = (lv_val *)callg((callgfnptr)op_putindx, ¶m_list);
|
|
MV_FORCE_STR(val);
|
|
DECR_AC_REF(dst_lv, TRUE);
|
|
dst_lv->v = *val;
|
|
dst_lv->v.mvtype &= ~MV_ALIASCONT; /* Make sure alias container property does not pass */
|
|
}
|
|
}
|
|
}
|