diff options
Diffstat (limited to 'do/splice')
-rw-r--r-- | do/splice | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/do/splice b/do/splice new file mode 100644 index 0000000000..58aa56c8bf --- /dev/null +++ b/do/splice @@ -0,0 +1,192 @@ +int +do_splice(ary,gimme,arglast) +register ARRAY *ary; +int gimme; +int *arglast; +{ + register STR **st = stack->ary_array; + register int sp = arglast[1]; + int max = arglast[2] + 1; + register STR **src; + register STR **dst; + register int i; + register int offset; + register int length; + int newlen; + int after; + int diff; + STR **tmparyval; + + if (++sp < max) { + offset = (int)str_gnum(st[sp]); + if (offset < 0) + offset += ary->ary_fill + 1; + else + offset -= arybase; + if (++sp < max) { + length = (int)str_gnum(st[sp++]); + if (length < 0) + length = 0; + } + else + length = ary->ary_max + 1; /* close enough to infinity */ + } + else { + offset = 0; + length = ary->ary_max + 1; + } + if (offset < 0) { + length += offset; + offset = 0; + if (length < 0) + length = 0; + } + if (offset > ary->ary_fill + 1) + offset = ary->ary_fill + 1; + after = ary->ary_fill + 1 - (offset + length); + if (after < 0) { /* not that much array */ + length += after; /* offset+length now in array */ + after = 0; + if (!ary->ary_alloc) { + afill(ary,0); + afill(ary,-1); + } + } + + /* At this point, sp .. max-1 is our new LIST */ + + newlen = max - sp; + diff = newlen - length; + + if (diff < 0) { /* shrinking the area */ + if (newlen) { + New(451, tmparyval, newlen, STR*); /* so remember insertion */ + Copy(st+sp, tmparyval, newlen, STR*); + } + + sp = arglast[0] + 1; + if (gimme == G_ARRAY) { /* copy return vals to stack */ + if (sp + length >= stack->ary_max) { + astore(stack,sp + length, Nullstr); + st = stack->ary_array; + } + Copy(ary->ary_array+offset, st+sp, length, STR*); + if (ary->ary_flags & ARF_REAL) { + for (i = length, dst = st+sp; i; i--) + str_2mortal(*dst++); /* free them eventualy */ + } + sp += length - 1; + } + else { + st[sp] = ary->ary_array[offset+length-1]; + if (ary->ary_flags & ARF_REAL) { + str_2mortal(st[sp]); + for (i = length - 1, dst = &ary->ary_array[offset]; i > 0; i--) + str_free(*dst++); /* free them now */ + } + } + ary->ary_fill += diff; + + /* pull up or down? */ + + if (offset < after) { /* easier to pull up */ + if (offset) { /* esp. if nothing to pull */ + src = &ary->ary_array[offset-1]; + dst = src - diff; /* diff is negative */ + for (i = offset; i > 0; i--) /* can't trust Copy */ + *dst-- = *src--; + } + Zero(ary->ary_array, -diff, STR*); + ary->ary_array -= diff; /* diff is negative */ + ary->ary_max += diff; + } + else { + if (after) { /* anything to pull down? */ + src = ary->ary_array + offset + length; + dst = src + diff; /* diff is negative */ + Move(src, dst, after, STR*); + } + Zero(&ary->ary_array[ary->ary_fill+1], -diff, STR*); + /* avoid later double free */ + } + if (newlen) { + for (src = tmparyval, dst = ary->ary_array + offset; + newlen; newlen--) { + *dst = Str_new(46,0); + str_sset(*dst++,*src++); + } + Safefree(tmparyval); + } + } + else { /* no, expanding (or same) */ + if (length) { + New(452, tmparyval, length, STR*); /* so remember deletion */ + Copy(ary->ary_array+offset, tmparyval, length, STR*); + } + + if (diff > 0) { /* expanding */ + + /* push up or down? */ + + if (offset < after && diff <= ary->ary_array - ary->ary_alloc) { + if (offset) { + src = ary->ary_array; + dst = src - diff; + Move(src, dst, offset, STR*); + } + ary->ary_array -= diff; /* diff is positive */ + ary->ary_max += diff; + ary->ary_fill += diff; + } + else { + if (ary->ary_fill + diff >= ary->ary_max) /* oh, well */ + astore(ary, ary->ary_fill + diff, Nullstr); + else + ary->ary_fill += diff; + dst = ary->ary_array + ary->ary_fill; + for (i = diff; i > 0; i--) { + if (*dst) /* TARG was hanging around */ + str_free(*dst); /* after $#foo */ + dst--; + } + if (after) { + dst = ary->ary_array + ary->ary_fill; + src = dst - diff; + for (i = after; i; i--) { + *dst-- = *src--; + } + } + } + } + + for (src = st+sp, dst = ary->ary_array + offset; newlen; newlen--) { + *dst = Str_new(46,0); + str_sset(*dst++,*src++); + } + sp = arglast[0] + 1; + if (gimme == G_ARRAY) { /* copy return vals to stack */ + if (length) { + Copy(tmparyval, st+sp, length, STR*); + if (ary->ary_flags & ARF_REAL) { + for (i = length, dst = st+sp; i; i--) + str_2mortal(*dst++); /* free them eventualy */ + } + Safefree(tmparyval); + } + sp += length - 1; + } + else if (length--) { + st[sp] = tmparyval[length]; + if (ary->ary_flags & ARF_REAL) { + str_2mortal(st[sp]); + while (length-- > 0) + str_free(tmparyval[length]); + } + Safefree(tmparyval); + } + else + st[sp] = &str_undef; + } + return sp; +} + |