Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1072 lines
23 KiB
C

static char sccsid[] = "@(#)86 1.29.3.16 src/bos/usr/ccs/lib/libdbx/tree.c, libdbx, bos411, 9428A410j 5/11/94 14:22:22";
/*
* COMPONENT_NAME: (CMDDBX) - dbx symbolic debugger
*
* FUNCTIONS: amper, build, buildcmdlist, concrete, isarray,
* isfunction, notregsym, prtree,
* ptrrename, tfree, unrval, unrval_routine,
* build_add_sub, copynode
*
* ORIGINS: 26, 27, 83
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1993
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
* Copyright (c) 1982 Regents of the University of California
*
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#ifdef KDBXRT
#include "rtnls.h" /* MUST BE FIRST */
#endif
/* include file for message texts */
#include "dbx_msg.h"
nl_catd scmc_catd; /* Cat descriptor for scmc conversion */
/*
* Parse tree management.
*/
#ifdef _NO_PROTO
#include <varargs.h>
#else
#include <stdarg.h>
#endif /* _NO_PROTO */
#include "defs.h"
#include "tree.h"
#include "operators.h"
#include "debug.h"
#include "eval.h"
#include "events.h"
#include "symbols.h"
#include "scanner.h"
#include "source.h"
#include "object.h"
#include "mappings.h"
#include "process.h"
#include "machine.h"
extern boolean hexaddr;
extern int sourcefiles;
extern int inclfiles;
extern Language cobLang;
#define is_pointer(node) \
(((node)->nodetype->class == PTR) || \
(((node)->nodetype->class == VAR || (node)->nodetype->class == MEMBER) \
&& ((node)->nodetype->type->class == PTR)))
/*
* Strip away indirection from a node, thus returning a node for
* interpreting the expression as an lvalue.
*/
public Node unrval (exp)
Node exp;
{
Node p;
if (exp->op == O_RVAL) {
p = exp->value.arg[0];
dispose(exp);
} else if ((exp->op == O_INDIR) || (exp->op == O_INDIRA)) {
p = exp->value.arg[0];
if (p->op == O_RVAL) {
p->op = O_INDIR;
p->nodetype = exp->nodetype;
}
dispose(exp);
} else {
p = exp;
}
return p;
}
/*
* Special case: Strip away indirection from a node, AND if
* it is a routine return a node for interpreting the
* expression as an lvalue. Else just leave it alone.
*/
public Node unrval_routine (exp)
Node exp;
{
Node p, saved;
saved = exp;
if (exp->op == O_RVAL)
{
p = exp->value.arg[0];
if (isroutine(p->value.sym))
{
dispose(exp);
}
else
{
p = saved;
}
}
else
{
p = exp;
}
return p;
}
/*
* Create a node for renaming a node to a pointer type.
*/
public Node ptrrename (p, t)
Node p;
Node t;
{
t->nodetype = newSymbol(nil, 0, PTR, t->nodetype, nil);
return build(O_TYPERENAME, p, t);
}
private notregsym (s)
Symbol s;
{
if (s->storage == INREG || (s->param && preg(s, nil) != -1))
{
error(catgets(scmc_catd, MS_tree, MSG_379,
"cannot take address of register"));
}
}
private notenumsym(s)
Symbol s;
{
if (s->class == CONST && s->type->class == SCAL)
{
error(catgets(scmc_catd, MS_tree, MSG_622,
"cannot take address of enumeration constant"));
}
}
/*
* Return the tree for a unary ampersand operator.
*/
public Node amper (p)
register Node p;
{
register Node r;
Symbol s;
extern Language pascalLang;
checkref(p);
switch (p->op) {
case O_RVAL:
if (p->nodetype->class == LABEL &&
p->nodetype->language == pascalLang)
{
r = copynode(p->nodetype->symvalue.constval);
r->nodetype = t_addr;
dispose(p);
break;
}
/* if we have a pointer to member that */
/* is a function, do not modify the */
/* tree because this is printed the */
/* same whether or not 'address of' */
/* was specified */
if ((rtype(p->nodetype)->class == FFUNC) &&
(p->value.arg[0]->op == O_DOTSTAR)) {
r = p;
break;
}
case O_INDIR:
case O_INDIRA:
r = p->value.arg[0];
if ((r->op == O_SYM) && (p->op != O_INDEX)) {
notregsym(r->value.sym);
}
r->nodetype = t_addr;
dispose(p);
break;
case O_INDEX:
r = p;
break;
case O_SYM:
s = p->value.sym;
if (isblock(s)) {
if (prolloc(s)) {
r = build(O_LCON, prolloc(s));
} else /* unnamed blocks have prolloc == 0 */
r = build(O_LCON, codeloc(s));
} else {
notregsym(s);
notenumsym(s);
r = build(O_LCON, address(s, nil));
}
r->nodetype = t_addr;
dispose(p);
break;
case O_DOT:
r = p;
r->nodetype = t_addr;
break;
default:
beginerrmsg();
(*rpt_error)(stderr, catgets(scmc_catd, MS_tree, MSG_381,
"expected variable, found \""));
prtree( rpt_error, stderr, p);
(*rpt_error)(stderr, "\"");
tfree(p);
enderrmsg();
/* NOTREACHED */
}
return r;
}
/*
* Create a command list from a single command.
*/
public Cmdlist buildcmdlist (cmd)
Command cmd;
{
Cmdlist cmdlist;
cmdlist = list_alloc();
cmdlist_append(cmd, cmdlist);
return cmdlist;
}
/*
* Print out a tree.
*/
public prtree( report, f, p)
int (*report)( );
File f;
register Node p;
{
Operator op;
if (p != nil) {
op = p->op;
if (ord(op) > ord(O_LASTOP)) {
panic( catgets(scmc_catd, MS_tree, MSG_425,
"Cannot display evaluation tree for unknown operation %d"), p->op);
return;
}
switch (op) {
case O_NAME:
(*report)( f, "%s", ident(p->value.name));
break;
case O_SYM:
printname( report, f, p->value.sym, false);
break;
case O_QLINE:
if ((sourcefiles > 1) || inclfiles) {
prtree( report, f, p->value.arg[0]);
(*report)( f, ":");
}
prtree( report, f, p->value.arg[1]);
break;
case O_CLEAR:
if ((sourcefiles > 1) || inclfiles) {
prtree( report, f, p->value.arg[0]);
(*report)( f, ":");
}
prtree( report, f, p->value.arg[1]);
break;
case O_LCON:
if (hexaddr) {
(*report)( f, "0x");
addrprint( report, f,p->value.lcon,false,0);
} else
(*report)( f, "%d", p->value.lcon);
break;
case O_ULCON:
(*report)( f, "%u", (unsigned int) p->value.lcon);
break;
case O_LLCON:
(*report)(f, "%lld", p->value.llcon);
break;
case O_ULLCON:
(*report)(f, "%llu", p->value.llcon);
break;
case O_CCON:
(*report)( f, "'%c'", p->value.lcon);
break;
case O_FCON:
(*report)( f, "%g", p->value.fcon);
break;
case O_QCON:
printquad (report, f, p->value.qcon);
break;
case O_KCON:
(*report)( f, "(%g,%g)", p->value.kcon.real,
p->value.kcon.imag);
break;
case O_QKCON:
(*report)(f, "(");
printquad (report, f, p->value.qkcon.real);
(*report)(f, ",");
printquad (report, f, p->value.qkcon.imag);
(*report)(f, ")");
case O_SCON:
(*report)(f, "\"%s\"", p->value.scon);
break;
/* Pascal set constant */
case O_SETCON:
(*report)(f, "[");
prtree( report, f, p->value.arg[0]);
(*report)(f, "]");
break;
case O_ADDR:
(*report)( f, "&");
prtree( report, f, p->value.arg[0]);
break;
case O_INDEX:
prtree( report, f, p->value.arg[0]);
(*report)( f, "[");
prtree( report, f, p->value.arg[1]);
(*report)( f, "]");
break;
case O_COMMA:
prtree( report, f, p->value.arg[0]);
if (p->value.arg[1] != nil) {
(*report)( f, ", ");
prtree( report, f, p->value.arg[1]);
}
break;
case O_SCOPE:
prtree( report, f, p->value.arg[0]);
(*report)( f, "::");
prtree( report, f, p->value.arg[1]);
break;
case O_NAMELIST:
printmparams( f, p->value.arg[0] );
break;
case O_UNRES:
if (p->value.arg[0] != nil)
{
prtree( report, f, p->value.arg[0]);
(*report)( f, "::");
}
prtree( report, f, p->value.arg[1]);
break;
case O_BSET:
(*report)( f, "build set[");
prtree( report, f, p->value.arg[0]);
(*report)( f, "]");
break;
case O_RVAL:
case O_CPPREF:
case O_ITOF:
prtree( report, f, p->value.arg[0]);
break;
case O_CALL:
prtree( report, f, p->value.arg[0]);
if (p->value.arg[1]!= nil) {
(*report)( f, "(");
prtree( report, f, p->value.arg[1]);
(*report)( f, ")");
}
break;
case O_INDIR:
case O_INDIRA:
prtree( report, f, p->value.arg[0]);
(*report)( f, "^");
break;
case O_DOT:
prtree(report, f, p->value.arg[0]);
(*report)( f, ".%s", symname(p->value.arg[1]->value.sym));
break;
case O_DOTSTAR:
prtree( report, f, p->value.arg[0]);
(*report)( f, ".*");
prtree(report, f, p->value.arg[1]);
break;
case O_FREE:
prtree( report, f, p->value.arg[0]);
break;
case O_PTRRENAME:
prtree( report, f, p->value.arg[1]);
(*report)( f, "(");
prtree( report, f, p->value.arg[0]);
(*report)( f, " *)");
break;
case O_TYPERENAME:
prtree( report, f, p->value.arg[1]);
(*report)( f, "(");
prtree( report, f, p->value.arg[0]);
(*report)( f, ")");
break;
default:
switch (degree(op)) {
case BINARY:
prtree( report, f, p->value.arg[0]);
(*report)( f, "%s", opinfo[ord(op)].opstring);
prtree( report, f, p->value.arg[1]);
break;
case UNARY:
(*report)( f, "%s", opinfo[ord(op)].opstring);
prtree( report, f, p->value.arg[0]);
break;
default:
if (opinfo[ord(op)].opstring == nil) {
(*report)( f, "[op %d]", ord(op));
} else {
(*report)( f, "%s", opinfo[ord(op)].opstring);
}
break;
}
break;
}
}
}
/*
* Free storage associated with a tree.
*/
public tfree (p)
Node p;
{
int i;
short numb_args;
if (p == nil) {
return;
}
switch (p->op) {
case O_CLEAR:
dispose(p->value.arg[0]->value.scon);
dispose(p->value.arg[0]);
tfree(p->value.arg[1]);
break;
case O_SCON:
dispose(p->value.scon);
break;
case O_LCON:
case O_ULCON:
case O_LLCON:
case O_ULLCON:
break;
default:
numb_args = nargs(p->op);
for (i = 0; i < numb_args; i++) {
tfree(p->value.arg[i]);
}
break;
}
dispose(p);
}
/*
* Determine whether or not a symbol is an array symbol instead of a function.
*/
public Boolean isarray(n)
Node n;
{
Symbol s;
if (n->op == O_DOT)
n = n->value.arg[1];
if (n->op == O_SYM) {
s = n->value.sym;
if(s->language == cobLang) {
if (!((s->class == VAR) || (s->class == TOCVAR) ||
(s->class == REF) || (s->class == REFFIELD))) {
return false;
} else {
Symbol p;
for (p = s; p->class == REFFIELD;
p = p->symvalue.field.parent) {
if (p->type->class == ARRAY)
return true;
}
return (Boolean) (p->type->class == ARRAY);
}
} else {
return (((s->class == VAR) || (s->class == TOCVAR) ||
(s->class == REF) || (s->class == REFFIELD) ||
(s->class == MEMBER)) &&
(s->type->class == ARRAY));
}
} else
return false;
}
/*
* Determine whether or not a symbol is a function.
*/
public Boolean isfunction(n)
Node n;
{
Symbol s;
if (n->op == O_SYM) {
s = n->value.sym;
return ((s->class == FUNC) || (s->class == PROC) ||
(s->class == CSECTFUNC));
} else
return false;
}
/*
* Build a tree.
*/
#ifdef _NO_PROTO
/*VARARGS1*/
public Node build(va_alist)
va_dcl
#else
public Node build(Operator op, ...)
#endif /* _NO_PROTO */
{
#ifdef _NO_PROTO
Operator op;
#endif /* _NO_PROTO */
register va_list ap;
register Node p, q;
Symbol s, t;
int i;
short numb_args;
#ifdef _NO_PROTO
va_start(ap);
op = va_arg(ap, Operator);
#else
va_start(ap, op);
#endif /* _NO_PROTO */
p = new(Node);
p->op = op;
p->nodetype = nil;
switch (op) {
case O_NAME:
p->value.name = va_arg(ap, Name);
break;
case O_SYM:
case O_PRINTCALL:
case O_PRINTRTN:
case O_PROCRTN:
p->value.sym = va_arg(ap, Symbol);
break;
/* Pascal Set Constant */
case O_SETCON:
p->value.lcon = va_arg(ap, long);
p->nodetype = newSymbol( identname("$setcon",true), 0, SET,
va_arg(ap, Symbol), nil);
break;
case O_DEBUG:
case O_LCON:
case O_CCON:
case O_CASE:
case O_TRACEOFF:
p->value.lcon = va_arg(ap, long);
break;
case O_ULCON:
p->value.lcon = va_arg(ap, unsigned long);
break;
case O_LLCON:
p->value.llcon = va_arg(ap, LongLong);
break;
case O_ULLCON:
p->value.llcon = va_arg(ap, uLongLong);
break;
case O_FCON:
p->value.fcon = va_arg(ap, double);
break;
case O_QCON:
p->value.qcon = va_arg(ap, quadf);
break;
case O_KCON:
p->value.kcon.real = va_arg(ap, double);
p->value.kcon.imag = va_arg(ap, double);
break;
case O_QKCON:
p->value.qkcon.real = va_arg(ap, quadf);
p->value.qkcon.imag = va_arg(ap, quadf);
break;
#ifdef KDBX
case O_LLDB:
#endif /* KDBX */
case O_SCON:
case O_CHFILE:
case O_EDIT:
case O_SOURCE:
case O_PROMPT:
p->value.scon = va_arg(ap, String);
if (op == O_SCON)
p->value.fscon.strsize = va_arg(ap, int);
break;
case O_RVAL:
case O_INDIR:
case O_INDIRA:
case O_CPPREF:
p->value.arg[0] = va_arg(ap, Node);
break;
case O_CALL:
case O_CALLPROC:
q = va_arg(ap, Node);
p->value.arg[0] = q;
p->value.arg[1] = va_arg(ap, Node);
if (q->op == O_SYM) {
s = q->value.sym;
if (s->class == TYPE or s->class == TAG) {
p->op = O_TYPERENAME;
p->value.arg[0] = p->value.arg[1];
p->value.arg[1] = q;
q = p->value.arg[0];
if (q == nil) {
error( catgets(scmc_catd, MS_resolve, MSG_615,
"Too few arguments to type rename."));
}
if (q->value.arg[1] != nil) {
error( catgets(scmc_catd, MS_resolve, MSG_616,
"Too many arguments to type rename."));
}
p->value.arg[0] = q->value.arg[0];
} else if (s->class == MODULE) {
for (t = lookup(s->name); t != nil; t = t->next_sym) {
if (t->block == s and isroutine(t)) {
q->value.sym = t;
q->nodetype = t;
break;
}
}
}
}
break;
case O_ADDEVENT:
case O_ONCE:
case O_IF:
p->value.trace.exp = NULL;
p->value.trace.place = NULL;
p->value.trace.cond = va_arg(ap, Node);
p->value.trace.inst = false;
p->value.trace.event = NULL;
p->value.trace.actions = va_arg(ap, Cmdlist);
break;
case O_TRACEON:
p->value.trace.exp = NULL;
p->value.trace.place = NULL;
p->value.trace.cond = NULL;
p->value.trace.inst = va_arg(ap, Boolean);
p->value.trace.event = NULL;
p->value.trace.actions = va_arg(ap, Cmdlist);
break;
case O_STOPIFCHANGED:
p->value.trace.exp = va_arg(ap, Node);
p->value.trace.place = NULL;
p->value.trace.cond = va_arg(ap, Node);
p->value.trace.inst = NULL;
p->value.trace.event = NULL;
p->value.trace.actions = NULL;
break;
case O_STEP:
p->value.step.source = va_arg(ap, Boolean);
p->value.step.skipcalls = va_arg(ap, Boolean);
break;
case O_EXAMINE:
p->value.examine.mode = va_arg(ap, String);
p->value.examine.beginaddr = va_arg(ap, Node);
p->value.examine.endaddr = va_arg(ap, Node);
p->value.examine.count = va_arg(ap, int);
break;
default:
numb_args = nargs(op);
for (i = 0; i < numb_args; i++) {
p->value.arg[i] = va_arg(ap, Node);
}
break;
}
va_end(ap);
/* Check for basic types here */
if ((int) p->op >= (int) O_ALIAS) {
check(p);
assigntypes(p);
}
else
switch(p->op) {
case O_LCON:
p->nodetype = t_int;
break;
case O_ULCON:
p->nodetype = dt_uint;
break;
case O_LLCON:
p->nodetype = t_longlong;
break;
case O_ULLCON:
p->nodetype = t_ulonglong;
break;
case O_CCON:
p->nodetype = t_char;
break;
case O_FCON:
p->nodetype = t_real;
break;
case O_QCON:
p->nodetype = t_quad;
break;
case O_KCON:
p->nodetype = t_complex;
break;
case O_QKCON:
p->nodetype = t_qcomplex;
break;
case O_SETCON:
break;
default:
check(p);
assigntypes(p);
break;
}
if (tracetree) {
(*rpt_output)(stdout,
"built %s node 0x%x with arg[0] 0x%x arg[1] 0x%x\n",
opname(p->op), p, p->value.arg[0], p->value.arg[1]);
fflush(stdout);
}
return p;
}
/*
* Build a tree without performing "check" and "assigntypes"
*/
#ifdef _NO_PROTO
/*VARARGS1*/
public Node cons(va_alist)
va_dcl
#else
public Node cons(Operator op, ...)
#endif /* _NO_PROTO */
{
#ifdef _NO_PROTO
Operator op;
#endif /* _NO_PROTO */
register va_list ap;
register Node p;
int i;
short numb_args;
#ifdef _NO_PROTO
va_start(ap);
op = va_arg(ap, Operator);
#else
va_start(ap, op);
#endif /* _NO_PROTO */
p = new(Node);
p->op = op;
p->nodetype = nil;
switch (op) {
case O_NAME:
p->value.name = va_arg(ap, Name);
break;
case O_SYM:
case O_PRINTCALL:
case O_PRINTRTN:
case O_PROCRTN:
p->value.sym = va_arg(ap, Symbol);
break;
/* Pascal Set Constant */
case O_SETCON:
p->value.lcon = va_arg(ap, long);
p->nodetype = newSymbol(identname("$setcon", true), 0, SET,
va_arg(ap, Symbol), nil);
break;
case O_DEBUG:
case O_LCON:
case O_CCON:
case O_CASE:
case O_TRACEOFF:
p->value.lcon = va_arg(ap, long);
break;
case O_ULCON:
p->value.lcon = va_arg(ap, unsigned long);
break;
case O_LLCON:
p->value.llcon = va_arg(ap, LongLong);
break;
case O_ULLCON:
p->value.llcon = va_arg(ap, uLongLong);
break;
case O_FCON:
p->value.fcon = va_arg(ap, double);
break;
case O_QCON:
p->value.qcon = va_arg(ap, quadf);
break;
case O_KCON:
p->value.kcon.real = va_arg(ap, double);
p->value.kcon.imag = va_arg(ap, double);
break;
case O_QKCON:
p->value.qkcon.real = va_arg(ap, quadf);
p->value.qkcon.imag = va_arg(ap, quadf);
break;
case O_SCON:
case O_CHFILE:
case O_EDIT:
case O_SOURCE:
case O_PROMPT:
p->value.scon = va_arg(ap, String);
if (op == O_SCON)
p->value.fscon.strsize = va_arg(ap, int);
break;
case O_RVAL:
case O_INDIR:
case O_INDIRA:
case O_CPPREF:
p->value.arg[0] = va_arg(ap, Node);
break;
case O_CALL:
case O_CALLPROC:
p->value.arg[0] = va_arg(ap, Node);
p->value.arg[1] = va_arg(ap, Node);
break;
case O_ADDEVENT:
case O_ONCE:
case O_IF:
p->value.trace.exp = NULL;
p->value.trace.place = NULL;
p->value.trace.cond = va_arg(ap, Node);
p->value.trace.inst = false;
p->value.trace.event = NULL;
p->value.trace.actions = va_arg(ap, Cmdlist);
break;
case O_TRACEON:
p->value.trace.exp = NULL;
p->value.trace.place = NULL;
p->value.trace.cond = NULL;
p->value.trace.inst = va_arg(ap, Boolean);
p->value.trace.event = NULL;
p->value.trace.actions = va_arg(ap, Cmdlist);
break;
case O_STOPIFCHANGED:
p->value.trace.exp = va_arg(ap, Node);
p->value.trace.place = NULL;
p->value.trace.cond = va_arg(ap, Node);
p->value.trace.inst = NULL;
p->value.trace.event = NULL;
p->value.trace.actions = NULL;
break;
case O_STEP:
p->value.step.source = va_arg(ap, Boolean);
p->value.step.skipcalls = va_arg(ap, Boolean);
break;
case O_EXAMINE:
p->value.examine.mode = va_arg(ap, String);
p->value.examine.beginaddr = va_arg(ap, Node);
p->value.examine.endaddr = va_arg(ap, Node);
p->value.examine.count = va_arg(ap, int);
break;
default:
numb_args = nargs(op);
for (i = 0; i < numb_args; i++) {
p->value.arg[i] = va_arg(ap, Node);
}
break;
}
va_end(ap);
if (tracetree) {
(*rpt_output)(stdout,
"built %s node 0x%x with arg[0] 0x%x arg[1] 0x%x\n",
opname(p->op), p, p->value.arg[0], p->value.arg[1]);
fflush(stdout);
}
return p;
}
/*
* Build an O_ADD or O_SUB node, possibly scaling one of the operands
* appropriately for pointer arithmetic.
*/
public Node build_add_sub(op, n1, n2)
Operator op;
Node n1, n2;
{
long TypeSize;
Node ntemp, r;
if (op == O_ADD && /* swap nodes in the case of (i + ptr) */
(n2->nodetype->class == VAR || n2->nodetype->class == MEMBER) &&
n2->nodetype->type->class == PTR) {
ntemp = n1;
n1 = n2;
n2 = ntemp;
}
/* if n1 is a pointer and n2 is an "int" type */
/* NOTE : second part of "if" test is important because
pointers are compatible with ints */
if (is_pointer(n1) && !is_pointer(n2) && compatible(n2->nodetype, t_int))
{
Symbol n1_type = (n1->nodetype->class == PTR) ? n1->nodetype
: n1->nodetype->type;
/* build a node so n2 gets multiplied by 4 */
TypeSize = size(n1_type->type);
n2 = build(O_MUL, n2, build(O_LCON, TypeSize));
r = build(op, n1, n2);
r->nodetype = n1_type;
}
else
{
r = build(op, n1, n2);
}
return r;
}
/*
* NAME: copynode
*
* FUNCTION: make a copy of a node
*
* PARAMETERS:
* current - input Node
*
* RECOVERY OPERATION: NONE NEEDED
*
* DATA STRUCTURES: NONE
*
* RETURNS: a new copy of the node
*/
Node copynode( Node current )
{
Node current_copy;
short index;
short num_args;
if( current == NULL ) return NULL;
current_copy = new(Node);
current_copy->op = current->op;
current_copy->nodetype = current->nodetype;
num_args = nargs( current->op );
for( index = 0; index < num_args; index++ ) {
current_copy->value.arg[index] =
copynode( current->value.arg[index] );
}
if( index == 0 ) {
switch( current->op ) {
case O_SCON:
current_copy->value.scon =
malloc( (strlen(current->value.scon) + 1) * sizeof(char));
strcpy(current_copy->value.scon, current->value.scon);
break;
default:
current_copy->value = current->value;
break;
}
}
return current_copy;
}