/*
 * blkcpy - general values and related routines used by the calculator
 *
 * Copyright (C) 1999-2007,2021-2023,2025-2026  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/
 */

/*
 * important <system> header includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>

/*
 * calc local src includes
 */
#include "value.h"
#include "calc.h"
#include "file.h"
#include "blkcpy.h"
#include "attribute.h"
#include "errtbl.h"

#include "banned.h" /* include after all other 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, LEN ssi, LEN num, VALUE *dvp, LEN 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, LEN ssi, LEN num, MATRIX *dmat, LEN dsi)
{
    LEN 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 *)calloc(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, LEN ssi, LEN num, MATRIX *dmat, LEN dsi)
{
    OCTET *op;
    VALUE *vp;
    VALUE *vq;
    VALUE *vtemp;
    LEN 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 *)calloc(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((LEN)*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, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    LEN i;
    LEN newlen;
    LEN newsize;
    uint8_t *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 = (uint8_t *)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, LEN ssi, LEN num, LIST *lp, LEN dsi)
{
    VALUE *vp;
    VALUE *vq;
    LISTELEM *ep;
    VALUE *vtemp;
    LEN 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 *)calloc(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, (LEN)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, LEN ssi, LEN num, MATRIX *dmat, LEN dsi)
{
    VALUE *vp;
    VALUE *vq;
    LISTELEM *ep;
    VALUE *vtemp;
    LEN 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 *)calloc(num, sizeof(VALUE));
    if (vtemp == NULL) {
        math_error("Out of memory for list-to-matrix copy");
        not_reached();
    }
    ep = listelement(lp, (LEN)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, LEN ssi, LEN num, LIST *dlp, LEN dsi)
{
    LEN 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 *)calloc(num, sizeof(VALUE));
    if (vtemp == NULL) {
        math_error("Out of memory for list-to-list copy");
        not_reached();
    }
    sep = listelement(slp, (LEN)ssi);
    vp = vtemp;
    i = num;
    while (i-- > 0) {
        copyvalue(&sep->e_value, vp++);
        sep = sep->e_next;
    }
    dep = listelement(dlp, (LEN)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, LEN ssi, LEN num, FILEID id, LEN dsi)
{
    FILEIO *fiop;
    FILE *fp;
    LEN 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, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    FILEIO *fiop;
    FILE *fp;
    LEN numw;
    ZVALUE fsize;
    LEN filelen;
    LEN newlen;
    LEN 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)) {
        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 = (uint8_t *)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, LEN ssi, LEN num, FILEID id, LEN dsi)
{
    LEN len;
    FILEIO *fiop;
    LEN 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, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    LEN newlen;
    LEN newsize;
    uint8_t *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 = (uint8_t *)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, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    LEN len;
    LEN newlen;
    LEN newsize;
    uint8_t *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 = (uint8_t *)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, LEN ssi, LEN num, STRING *dstr, LEN 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, LEN ssi, LEN num, STRING *dstr, LEN dsi)
{
    uint8_t *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 = (uint8_t *)dstr->s_str + dsi;
    while (num-- > 0) {
        *c++ = *c1++;
    }
    return 0;
}
/*
 * copyostr2str - copy octet-specified string to string
 */
int
copyostr2str(char *sstr, LEN ssi, LEN num, STRING *dstr, LEN 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, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    size_t len;
    size_t newlen;
    size_t newsize;
    uint8_t *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 = (uint8_t *)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;
}

/*
 * copynum2blk - copy number numerator to block
 */
int
copynum2blk(NUMBER *snum, LEN ssi, LEN num, BLOCK *dblk, LEN dsi, bool noreloc)
{
    size_t newlen;
    size_t newsize;
    uint8_t *newdata;
#if CALC_BYTE_ORDER == CALC_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 = (uint8_t *)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 == CALC_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, LEN ssi, LEN num, NUMBER *dnum, LEN dsi, NUMBER **res)
{
    size_t newlen;
    NUMBER *ret; /* cloned and modified numerator */
#if CALC_BYTE_ORDER == CALC_BIG_ENDIAN
    HALF *swapped; /* byte swapped input data */
    LEN halflen;   /* length of the input rounded up to HALFs */
    HALF *h;       /* copy byteswap pointer */
    LEN 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 == CALC_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 *)calloc(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;
}
