/*
 * blkcpy - general values and related routines used by the calculator
 *
 * Copyright (C) 1999-2007,2021-2023  Landon Curt Noll and Ernest Bowen
 *
 * Primary author:  Landon Curt Noll
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Under source code control:   1997/04/18 20:41:26
 * File existed as early as:    1997
 *
 * Share and enjoy!  :-)        http://www.isthe.com/chongo/tech/comp/calc/
 */


#include <stdio.h>
#include <sys/types.h>
#include "calc.h"
#include "alloc.h"
#include "value.h"
#include "file.h"
#include "blkcpy.h"
#include "str.h"


#include "errtbl.h"
#include "banned.h"     /* include after system header <> includes */


/*
 * copystod - copy num indexed items from source value to destination value
 *
 * given:
 *      ssi = source starting index
 *      num = number of items (octets or values) to be copied
 *      sdi = destination starting index
 *
 * returns:
 *      zero if successful, otherwise error-code number
 */
int
copystod(VALUE *svp, long ssi, long num, VALUE *dvp, long dsi)
{
        BLOCK *sblk;
        BLOCK *dblk;
        bool noreloc;

        sblk = NULL;
        dblk = NULL;

        /*
         * check protections
         */
        if (svp->v_subtype & V_NOCOPYFROM)
                return E_COPY_13;
        if (dvp->v_subtype & V_NOCOPYTO)
                return E_COPY_14;
        noreloc = ((dvp->v_subtype & V_NOREALLOC) != 0);

        /*
         * determine/check source type
         */
        switch(svp->v_type) {
        case V_NBLOCK:
                if (svp->v_nblock->subtype & V_NOCOPYFROM)
                        return E_COPY_15;
                sblk = svp->v_nblock->blk;
                if (sblk->data == NULL)
                        return E_COPY_08;
                break;
        case V_BLOCK:
                sblk = svp->v_block;
                break;
        case V_STR:
        case V_OCTET:
        case V_NUM:
        case V_FILE:
        case V_MAT:
        case V_LIST:
                break;
        default:
                return E_COPY_09;
        }

        /*
         * determine/check destination type
         */
        switch(dvp->v_type) {
        case V_NBLOCK:
                if (dvp->v_nblock->subtype & V_NOCOPYTO)
                        return E_COPY_16;
                noreloc |=((dvp->v_nblock->subtype & V_NOREALLOC) != 0);
                dblk = dvp->v_nblock->blk;
                if (dblk->data == NULL)
                        return E_COPY_10;
                break;
        case V_BLOCK:
                noreloc = ((dvp->v_subtype & V_NOREALLOC) != 0);
                dblk = dvp->v_block;
                break;
        case V_STR:
        case V_NUM:
        case V_FILE:
        case V_MAT:
        case V_LIST:
                break;
        default:
                return E_COPY_11;
        }

        /*
         * copy based on source
         */
        switch (svp->v_type) {
        case V_BLOCK:
        case V_NBLOCK:
                /*
                 * copy from a block
                 */
                switch(dvp->v_type) {
                case V_BLOCK:
                case V_NBLOCK:
                        return copyblk2blk(sblk, ssi, num, dblk, dsi, noreloc);
                case V_NUM:
                    {
                        NUMBER *n;      /* modified number */
                        int rt;         /* return code */

                        /* copy to a number */
                        rt = copyblk2num(sblk, ssi, num, dvp->v_num, dsi, &n);
                        if (rt == 0) {
                                qfree(dvp->v_num);
                                dvp->v_num = n;
                        }
                        return rt;
                    }
                case V_FILE:
                        return copyblk2file(sblk, ssi, num, dvp->v_file, dsi);
                case V_MAT:
                        return copyblk2mat(sblk, ssi, num, dvp->v_mat, dsi);
                case V_STR:
                        return copyblk2str(sblk, ssi, num, dvp->v_str, dsi);
                }
                return E_COPY_12;

        case V_STR:
                switch(dvp->v_type) {
                case V_BLOCK:
                case V_NBLOCK:
                        /* copy to a block */
                        return copystr2blk(svp->v_str, ssi, num, dblk, dsi,
                                noreloc);
                case V_FILE:
                        return copystr2file(svp->v_str, ssi, num,
                                dvp->v_file, dsi);
                case V_STR:
                        return copystr2str(svp->v_str, ssi, num, dvp->v_str,
                                dsi);
                }
                return E_COPY_12;

        case V_OCTET:
                switch(dvp->v_type) {
                case V_BLOCK:
                case V_NBLOCK:
                        return copyostr2blk((char *) svp->v_octet, ssi, num,
                                dblk, dsi, noreloc);
                case V_STR:
                        return copyostr2str((char *) svp->v_octet, ssi, num,
                                dvp->v_str, dsi);
                }
                return E_COPY_12;

        case V_NUM:

                /*
                 * copy from a number
                 */
                if (dblk != NULL) {
                        /* copy to a block */
                        return copynum2blk(svp->v_num, ssi, num, dblk,
                                dsi, noreloc);
                }
                switch (dvp->v_type) {
                case V_MAT:
                        /* copy to a matrix */
                        return E_COPY_12;       /* not yet - XXX */
                case V_LIST:
                        /* copy to a list */
                        return E_COPY_12;       /* not yet - XXX */
                }
                break;
        case V_FILE:

                /*
                 * copy from a file
                 */
                if (dblk != NULL) {
                        /* copy to a block */
                        return copyfile2blk(svp->v_file, ssi, num,
                                            dblk, dsi, noreloc);
                }
                switch (dvp->v_type) {
                case V_NUM:
                        /* copy to a number */
                        return E_COPY_12;       /* not yet - XXX */
                }
                break;

        case V_MAT:

                /*
                 * copy from a matrix
                 */
                if (dblk != NULL) {
                        /* copy to a block */
                        return copymat2blk(svp->v_mat, ssi, num, dblk,
                                dsi, noreloc);
                }
                switch (dvp->v_type) {
                case V_MAT:
                        /* copy to a matrix */
                        return copymat2mat(svp->v_mat, ssi, num,
                                           dvp->v_mat, dsi);
                case V_LIST:
                        /* copy to a list */
                        return copymat2list(svp->v_mat, ssi, num,
                                            dvp->v_list, dsi);
                }
                break;

        case V_LIST:

                /*
                 * copy from a list
                 */
                if (dblk != NULL) {
                        /* copy to a block */
                        return E_COPY_12;       /* not yet - XXX */
                }
                switch (dvp->v_type) {
                case V_MAT:
                        /* copy to a matrix */
                        return copylist2mat(svp->v_list, ssi, num,
                                            dvp->v_mat, dsi);
                case V_LIST:
                        /* copy to a list */
                        return copylist2list(svp->v_list, ssi, num,
                                             dvp->v_list, dsi);
                }
                break;
        }

        /*
         * unsupported copy combination
         */
        return E_COPY_12;
}


/*
 * copymat2mat - copy matrix to matrix
 */
int
copymat2mat(MATRIX *smat, long ssi, long num, MATRIX *dmat, long dsi)
{
        long i;
        VALUE *vp;
        VALUE *vq;
        VALUE *vtemp;
        unsigned short subtype;

        if (ssi > smat->m_size)
                return E_COPY_02;

        if (num < 0)
                num = smat->m_size - ssi;
        if (ssi + num > smat->m_size)
                return E_COPY_05;
        if (num == 0)
                return 0;
        if (dsi < 0)
                dsi = 0;
        if (dsi + num > dmat->m_size)
                return E_COPY_07;
        vtemp = (VALUE *) malloc(num * sizeof(VALUE));
        if (vtemp == NULL) {
                math_error("Out of memory for mat-to-mat copy");
                not_reached();
        }
        vp = smat->m_table + ssi;
        vq = vtemp;
        i = num;
        while (i-- > 0)
                copyvalue(vp++, vq++);
        vp = vtemp;
        vq = dmat->m_table + dsi;
        for (i = num; i > 0; i--, vp++, vq++) {
                subtype = vq->v_subtype;
                freevalue(vq);
                *vq = *vp;
                vq->v_subtype |= subtype;
        }
        free(vtemp);
        return 0;
}


/*
 * copyblk2mat - copy block to matrix
 */
int
copyblk2mat(BLOCK *blk, long ssi, long num, MATRIX *dmat, long dsi)
{
        OCTET *op;
        VALUE *vp;
        VALUE *vq;
        VALUE *vtemp;
        long i;
        unsigned short subtype;

        if (ssi > blk->datalen)
                return E_COPY_02;
        if (num < 0)
                num = blk->datalen - ssi;
        if (ssi + num > blk->datalen)
                return E_COPY_05;
        if (num == 0)
                return 0;
        if (dsi < 0)
                dsi = 0;
        if (dsi + num > dmat->m_size)
                return E_COPY_07;
        op = blk->data + ssi;
        vtemp = (VALUE *) malloc(num * sizeof(VALUE));
        if (vtemp == NULL) {
                math_error("Out of memory for block-to-matrix copy");
                not_reached();
        }
        vp = vtemp;
        i = num;
        while (i-- > 0) {
                vp->v_type = V_NUM;
                vp->v_subtype = V_NOSUBTYPE;
                vp->v_num = itoq((long) *op++);
                vp++;
        }
        vp = vtemp;
        vq = dmat->m_table + dsi;
        for (i = num; i > 0; i--, vp++, vq++) {
                subtype = vq->v_subtype;
                freevalue(vq);
                *vq = *vp;
                vq->v_subtype |= subtype;
        }
        free(vtemp);
        return 0;
}


/*
 * copymat2blk - copy matrix to block
 */
int
copymat2blk(MATRIX *smat, long ssi, long num, BLOCK *dblk, long dsi,
            bool noreloc)
{
        long i;
        long newlen;
        long newsize;
        USB8 *newdata;
        VALUE *vp;
        OCTET *op;

        if (ssi > smat->m_size)
                return E_COPY_02;
        if (num < 0)
                num = smat->m_size - ssi;
        if (num == 0)
                return 0;
        if (ssi + num > smat->m_size)
                return E_COPY_05;
        if (dsi < 0)
                dsi = dblk->datalen;
        newlen = dsi + num;
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for matrix-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
        vp = smat->m_table + ssi;
        op = dblk->data + dsi;
        for (i = num; i > 0; i--)
                copy2octet(vp++, op++);
        if (newlen > dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}


/*
 * copymat2list - copy matrix to list
 */
int
copymat2list(MATRIX *smat, long ssi, long num, LIST *lp, long dsi)
{
        VALUE *vp;
        VALUE *vq;
        LISTELEM *ep;
        VALUE *vtemp;
        long i;
        unsigned short subtype;

        if (ssi > smat->m_size)
                return E_COPY_02;
        if (num < 0)
                num = smat->m_size - ssi;
        if (num == 0)
                return 0;
        if (ssi + num > smat->m_size)
                return E_COPY_05;
        if (dsi < 0)
                dsi = 0;
        if (dsi + num > lp->l_count)
                return E_COPY_07;
        vtemp = (VALUE *) malloc(num * sizeof(VALUE));
        if (vtemp == NULL) {
                math_error("Out of memory for matrix-to-list copy");
                not_reached();
        }
        vp = smat->m_table + ssi;
        vq = vtemp;
        i = num;
        while (i-- > 0)
                copyvalue(vp++, vq++);
        vq = vtemp;
        ep = listelement(lp, (long) dsi);
        i = num;
        while (i-- > 0) {
                subtype = ep->e_value.v_subtype;
                freevalue(&ep->e_value);
                ep->e_value = *vq++;
                ep->e_value.v_subtype |= subtype;
                ep = ep->e_next;
        }
        free(vtemp);
        return 0;
}


/*
 * copymat2list - copy list to matrix
 */
int
copylist2mat(LIST *lp, long ssi, long num, MATRIX *dmat, long dsi)
{
        VALUE *vp;
        VALUE *vq;
        LISTELEM *ep;
        VALUE *vtemp;
        long i;
        unsigned short subtype;

        if (ssi > lp->l_count)
                return E_COPY_02;
        if (num < 0)
                num = lp->l_count - ssi;
        if (num == 0)
                return 0;
        if (ssi + num > lp->l_count)
                return E_COPY_05;
        if (dsi < 0)
                dsi = 0;
        if (dsi + num > dmat->m_size)
                return E_COPY_07;
        vtemp = (VALUE *) malloc(num * sizeof(VALUE));
        if (vtemp == NULL) {
                math_error("Out of memory for list-to-matrix copy");
                not_reached();
        }
        ep = listelement(lp, (long) ssi);
        vp = vtemp;
        i = num;
        while (i-- > 0) {
                copyvalue(&ep->e_value, vp++);
                ep = ep->e_next;
        }
        vp = vtemp;
        vq = dmat->m_table + dsi;
        for (i = num; i > 0; i--, vp++, vq++) {
                subtype = vq->v_subtype;
                freevalue(vq);
                *vq = *vp;
                vq->v_subtype |= subtype;
        }
        free(vtemp);
        return 0;
}


/*
 * copylist2list - copy list to list
 */
int
copylist2list(LIST *slp, long ssi, long num, LIST *dlp, long dsi)
{
        long i;
        LISTELEM *sep;
        LISTELEM *dep;
        VALUE *vtemp;
        VALUE *vp;
        unsigned short subtype;

        if (ssi > slp->l_count)
                return E_COPY_02;
        if (num < 0)
                num = slp->l_count - ssi;
        if (num == 0)
                return 0;
        if (ssi + num > slp->l_count)
                return E_COPY_05;
        if (dsi < 0)
                dsi = 0;
        if (dsi + num > dlp->l_count)
                return E_COPY_07;
        vtemp = (VALUE *) malloc(num * sizeof(VALUE));
        if (vtemp == NULL) {
                math_error("Out of memory for list-to-list copy");
                not_reached();
        }
        sep = listelement(slp, (long) ssi);
        vp = vtemp;
        i = num;
        while (i-- > 0) {
                copyvalue(&sep->e_value, vp++);
                sep = sep->e_next;
        }
        dep = listelement(dlp, (long) dsi);
        vp = vtemp;
        i = num;
        while (i-- > 0) {
                subtype = dep->e_value.v_subtype;
                freevalue(&dep->e_value);
                dep->e_value = *vp++;
                dep->e_value.v_subtype |= subtype;
                dep = dep->e_next;
        }
        free(vtemp);
        return 0;
}


/*
 * copyblk2file - copy block to file
 */
int
copyblk2file(BLOCK *sblk, long ssi, long num, FILEID id, long dsi)
{
        FILEIO  *fiop;
        FILE    *fp;
        long    numw;

        if (ssi > sblk->datalen)
                return E_COPY_02;
        if (num < 0)
                num = sblk->datalen - ssi;
        if (num == 0)
                return 0;

        fiop = findid(id, true);
        if (fiop == NULL)
                return E_COPYF_1;
        fp = fiop->fp;
        if (id == 1 || id == 2) {
                numw = idfputstr(id, (char *)sblk->data + ssi);  /* XXX */
                return 0;
        }
        if (dsi >= 0) {
                if (fseek(fp, dsi, 0))
                        return E_COPYF_2;
        }
        numw = fwrite(sblk->data + ssi, 1, num, fp);
        if (numw < num)
                return E_COPYF_3;
        fflush(fp);
        return 0;
}


/*
 * copyfile2blk - copy file to block
 */
int
copyfile2blk(FILEID id, long ssi, long num, BLOCK *dblk, long dsi, bool noreloc)
{
        FILEIO  *fiop;
        FILE    *fp;
        long    numw;
        ZVALUE  fsize;
        long    filelen;
        long    newlen;
        long    newsize;
        OCTET   *newdata;

        if (id < 3)                     /* excludes copying from stdin */
                return E_COPYF_1;
        fiop = findid(id, false);
        if (fiop == NULL)
                return E_COPYF_1;

        fp = fiop->fp;

        if (get_open_siz(fp, &fsize))
                return E_COPYF_2;
        if (zge31b(fsize)) {
                zfree(fsize);
                return E_COPY_05;
        }
        filelen = ztoi(fsize);
        zfree(fsize);

        if (ssi > filelen)
                return E_COPY_02;
        if (num < 0)
                num = filelen - ssi;
        if (num == 0)
                return 0;
        if (ssi + num > filelen)
                return E_COPY_05;
        if (fseek(fp, ssi, 0))          /* using system fseek  XXX */
                return E_COPYF_2;
        if (dsi < 0)
                dsi = dblk->datalen;
        newlen = dsi + num;
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for block-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
        numw = fread(dblk->data + dsi, 1, num, fp);
        if (numw < num)
                return E_COPYF_4;
        if (newlen > dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}


/*
 * copystr2file - copy string to file
 */
int
copystr2file(STRING *str, long ssi, long num, FILEID id, long dsi)
{
        long len;
        FILEIO *fiop;
        long numw;
        FILE *fp;

        len = str->s_len;

        if (ssi >= len)
                return E_COPY_02;
        if (num < 0)
                num = len - ssi;
        if (num <= 0)                   /* Nothing to be copied */
                return 0;
        if (ssi + num > len)
                return E_COPY_05;               /* Insufficient memory in str */
        fiop = findid(id, true);
        if (fiop == NULL)
                return E_COPYF_1;
        fp = fiop->fp;
        if (id == 1 || id == 2) {
                numw = idfputstr(id, str->s_str + ssi);  /* XXX */
                return 0;
        }
        if (dsi >= 0) {
                if (fseek(fp, dsi, 0))
                        return E_COPYF_2;
        }
        numw = fwrite(str->s_str + ssi, 1, num, fp);
        if (numw < num)
                return E_COPYF_3;
        fflush(fp);
        return 0;
}


/*
 * copyblk2blk - copy block to block
 */
int
copyblk2blk(BLOCK *sblk, long ssi, long num, BLOCK *dblk, long dsi,
            bool noreloc)
{
        long    newlen;
        long    newsize;
        USB8    *newdata;

        if (ssi > sblk->datalen)
                return E_COPY_02;
        if (num < 0)
                num = sblk->datalen - ssi;
        if (num == 0)                   /* Nothing to be copied */
                return 0;
        if (ssi + num > sblk->datalen)
                return E_COPY_05;
        if (dsi < 0)
                dsi = dblk->datalen;
        newlen = dsi + num;
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for block-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
        memmove(dblk->data + dsi, sblk->data + ssi, num);
        if (newlen > dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}


/*
 * copystr2blk - copy string to block
 */
int
copystr2blk(STRING *str, long ssi, long num, BLOCK *dblk, long dsi,
            bool noreloc)
{
        long    len;
        long    newlen;
        long    newsize;
        USB8    *newdata;

        len = str->s_len;

        if (ssi >= len)
                return E_COPY_02;
        if (num < 0)
                num = len - ssi;
        if (num <= 0)                   /* Nothing to be copied */
                return 0;
        if (dsi < 0)
                dsi = dblk->datalen;
        newlen = dsi + num + 1;
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for string-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
        memmove(dblk->data + dsi, str->s_str + ssi, num);
        dblk->data[dsi + num] = '\0';
        if (newlen > dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}


/*
 * copystr2str - copy up to num characters from sstr (starting at
 * index ssi) to dstr (starting at index dsi); num is reduced if there
 * are insufficient characters in sstr or insufficient space in dstr.
 */
int
copystr2str(STRING *sstr, long ssi, long num, STRING *dstr, long dsi)
{
        char *c, *c1;

        if (num < 0 || (size_t)(ssi + num) > sstr->s_len)
                num = sstr->s_len - ssi;
        if (num <= 0)
                return 0;               /* Nothing to be copied */
        if (dsi < 0)                    /* default destination index */
                dsi = 0;
        if ((size_t)(dsi + num) > dstr->s_len)
                num = dstr->s_len - dsi;
        c1 = sstr->s_str + ssi;
        c = dstr->s_str + dsi;
        while (num-- > 0)
                *c++ = *c1++;
        return 0;
}


/*
 * copyblk2str - copy up to num characters from sblk (starting at
 * index ssi) to str (starting at index dsi); num is reduced if there
 * is insufficient data in blk or insufficient space in str
 */
int
copyblk2str(BLOCK *sblk, long ssi, long num, STRING *dstr, long dsi)
{
        USB8 *c, *c1;

        if (num < 0 || ssi + num > sblk->datalen)
                num = sblk->datalen - ssi;
        if (num <= 0)
                return 0;               /* Nothing to be copied */
        if (dsi < 0)                    /* default destination index */
                dsi = 0;
        if ((size_t)(dsi + num) > dstr->s_len)
                num = dstr->s_len - dsi;
        c1 = sblk->data + ssi;
        c = (USB8 *)dstr->s_str + dsi;
        while (num-- > 0)
                *c++ = *c1++;
        return 0;
}
/*
 * copyostr2str - copy octet-specified string to string
 */
int
copyostr2str(char *sstr, long ssi, long num, STRING *dstr, long dsi)
{
        size_t len;
        char    *c, *c1;

        len = strlen(sstr);

        if (num < 0 || (size_t)(ssi + num) > len)
                num = len - ssi;
        if (num <= 0)                   /* Nothing to be copied */
                return 0;
        if (dsi < 0)
                dsi = 0;                /* Default destination index */
        if ((size_t)(dsi + num) > dstr->s_len)
                num = dstr->s_len - dsi;
        c1 = sstr + ssi;
        c = dstr->s_str + dsi;
        while (num-- > 0)
                *c++ = *c1++;
        return 0;
}


/*
 * copyostr2blk - copy octet-specified string to block
 */
int
copyostr2blk(char *str,long ssi,long num,BLOCK *dblk,long dsi,bool noreloc)
{
        size_t  len;
        size_t  newlen;
        size_t  newsize;
        USB8    *newdata;

        len = strlen(str) + 1;

        if (ssi > 0 && (size_t)ssi > len)
                return E_COPY_02;
        if (num < 0 || (size_t)(ssi + num) > len)
                num = len - ssi;
        if (num <= 0)                   /* Nothing to be copied */
                return 0;
        if (dsi < 0)
                dsi = dblk->datalen;    /* Default destination index */
        newlen = dsi + num;
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= (size_t)dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for string-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
        memmove(dblk->data + dsi, str + ssi, num);
        if (newlen > (size_t)dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}
#if !defined(HAVE_MEMMOVE)
/*
 * memmove - simulate the memory move function that deals with overlap
 *
 * Copying between objects that overlap will take place correctly.
 *
 * given:
 *      s1      destination
 *      s2      source
 *      n       octet count
 *
 * returns:
 *      s1
 */
void *
memmove(void *s1, CONST void *s2, MEMMOVE_SIZE_T n)
{
        /*
         * firewall
         */
        if (s1 == NULL || s2 == NULL) {
                math_error("bogus memmove NULL ptr");
                not_reached();
        }
        if (n <= 0) {
                /* neg or 0 count does nothing */
                return s1;
        }
        if ((char *)s1 == (char *)s2) {
                /* copy to same location does nothing */
                return s1;
        }

        /*
         * determine if we need to deal with overlap copy
         */
        if ((char *)s1 > (char *)s2 && (char *)s1 < (char *)s2+n) {

                /*
                 * we have to copy backwards ... slowly
                 */
                while (n-- > 0) {
                        ((char *)s1)[n] = ((char *)s2)[n];
                }

        } else {

                /*
                 * safe ... no overlap problems
                 */
                (void) memcpy(s1, s2, n);

        }
        return s1;
}
#endif


/*
 * copynum2blk - copy number numerator to block
 */
int
copynum2blk(NUMBER *snum, long ssi, long num, BLOCK *dblk, long dsi,
            bool noreloc)
{
        size_t  newlen;
        size_t  newsize;
        USB8    *newdata;
#if CALC_BYTE_ORDER == BIG_ENDIAN
        ZVALUE  *swnum; /* byte swapped numerator */
#endif

        if (ssi > snum->num.len)
                return E_COPY_02;
        if (num < 0)
                num = snum->num.len - ssi;
        if (num == 0)                   /* Nothing to be copied */
                return 0;
        if (ssi + num > snum->num.len)
                return E_COPY_05;
        if (dsi < 0)
                dsi = dblk->datalen;
        newlen = dsi + (num*sizeof(HALF));
        if (newlen <= 0)
                return E_COPY_07;
        if (newlen >= (size_t)dblk->maxsize) {
                if (noreloc)
                        return E_COPY_17;
                newsize = (1 + newlen/dblk->blkchunk) * dblk->blkchunk;
                newdata = (USB8*) realloc(dblk->data, newsize);
                if (newdata == NULL) {
                        math_error("Out of memory for num-to-block copy");
                        not_reached();
                }
                dblk->data = newdata;
                dblk->maxsize = newsize;
        }
#if CALC_BYTE_ORDER == LITTLE_ENDIAN
        memmove(dblk->data+dsi, (char *)(snum->num.v+ssi), num*sizeof(HALF));
#else
        swnum = swap_b8_in_ZVALUE(NULL, &(snum->num), false);
        memmove(dblk->data+dsi, (char *)(swnum->v+ssi), num*sizeof(HALF));
        zfree(*swnum);
#endif
        if (newlen > (size_t)dblk->datalen)
                dblk->datalen = newlen;
        return 0;
}


/*
 * copyblk2num - copy block to number
 */
int
copyblk2num(BLOCK *sblk, long ssi, long num, NUMBER *dnum, long dsi,
            NUMBER **res)
{
        size_t  newlen;
        NUMBER *ret;            /* cloned and modified numerator */
#if CALC_BYTE_ORDER == BIG_ENDIAN
        HALF *swapped;          /* byte swapped input data */
        unsigned long halflen;  /* length of the input rounded up to HALFs */
        HALF *h;                /* copy byteswap pointer */
        unsigned long i;
#endif

        if (ssi > sblk->datalen)
                return E_COPY_02;
        if (num < 0)
                num = sblk->datalen - ssi;
        if (num == 0)                   /* Nothing to be copied */
                return 0;
        if (ssi + num > sblk->datalen)
                return E_COPY_05;
        if (dsi < 0)
                dsi = dnum->num.len;
        newlen = dsi + ((num+sizeof(HALF)-1)/sizeof(HALF));
        if (newlen <= 0)
                return E_COPY_07;

        /* quasi-clone the numerator to the new size */
        ret = qalloc();
        ret->num.sign = dnum->num.sign;
        ret->num.v = alloc(newlen);
        ret->num.len = newlen;
        /* ensure that any trailing octets will be zero filled */
        ret->num.v[newlen-1] = 0;
        zcopyval(dnum->num, ret->num);
        if (!zisunit(ret->den)) {
                ret->den.len = dnum->den.len;
                ret->den.v = alloc(dnum->den.len);
                zcopyval(dnum->den, ret->den);
        }

        /* move the data */
#if CALC_BYTE_ORDER == LITTLE_ENDIAN
        memmove((char *)(ret->num.v + dsi), sblk->data + ssi, num);
#else
        /* form a HALF aligned copy of the input */
        halflen = (num+sizeof(HALF)-1) / sizeof(HALF);
        swapped = (HALF *)malloc(halflen * sizeof(HALF));
        if (swapped == NULL) {
                math_error("Out of memory for block-to-num copy");
                not_reached();
        }
        /* ensure that any trailing octets will be zero filled */
        swapped[halflen-1] = 0;
        memcpy(swapped, sblk->data + ssi, num);
        /* byte swap the copy of the input */
        for (i=0, h=swapped; i < halflen; ++i, ++h) {
                SWAP_B8_IN_HALF(h, h);
        }
        /* copy over whole byte-swapped HALFs */
        memcpy((char *)(ret->num.v + dsi), swapped,
               (num/sizeof(HALF))*sizeof(HALF));
        /* copy over any octets in the last partial HALF */
        i = num % sizeof(HALF);
        if (i != 0) {
                memcpy((char *)(ret->num.v + dsi)+num-i,
                       (char *)swapped + num-i, i);
        }
        free(swapped);
#endif
        /* save new number */
        *res = ret;
        return 0;
}
