/* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Davide Libenzi * */ #include "xinclude.h" #define XDL_MOBF_MINALLOC 128 typedef struct s_mmoffbuffer { long off, size; char *ptr; } mmoffbuffer_t; static int xdl_copy_range(mmfile_t *mmf, long off, long size, xdemitcb_t *ecb) { if (xdl_seek_mmfile(mmf, off) < 0) { return -1; } if (xdl_copy_mmfile(mmf, size, ecb) != size) { return -1; } return 0; } int xdl_bpatch(mmfile_t *mmf, mmfile_t *mmfp, xdemitcb_t *ecb) { long size, off, csize, osize; unsigned long fp, ofp; char const *blk; unsigned char const *data, *top; mmbuffer_t mb; if ((blk = (char const *) xdl_mmfile_first(mmfp, &size)) == NULL || size < XDL_BPATCH_HDR_SIZE) { return -1; } ofp = xdl_mmf_adler32(mmf); osize = xdl_mmfile_size(mmf); XDL_LE32_GET(blk, fp); XDL_LE32_GET(blk + 4, csize); if (fp != ofp || csize != osize) { return -1; } blk += XDL_BPATCH_HDR_SIZE; size -= XDL_BPATCH_HDR_SIZE; do { for (data = (unsigned char const *) blk, top = data + size; data < top;) { if (*data == XDL_BDOP_INS) { data++; mb.size = (long) *data++; mb.ptr = (char *) data; data += mb.size; if (ecb->outf(ecb->priv, &mb, 1) < 0) { return -1; } } else if (*data == XDL_BDOP_INSB) { data++; XDL_LE32_GET(data, csize); data += 4; mb.size = csize; mb.ptr = (char *) data; data += mb.size; if (ecb->outf(ecb->priv, &mb, 1) < 0) { return -1; } } else if (*data == XDL_BDOP_CPY) { data++; XDL_LE32_GET(data, off); data += 4; XDL_LE32_GET(data, csize); data += 4; if (xdl_copy_range(mmf, off, csize, ecb) < 0) { return -1; } } else { return -1; } } } while ((blk = (char const *) xdl_mmfile_next(mmfp, &size)) != NULL); return 0; } static unsigned long xdl_mmob_adler32(mmoffbuffer_t *obf, int n) { unsigned long ha; for (ha = 0; n > 0; n--, obf++) ha = xdl_adler32(ha, (unsigned char const *) obf->ptr, obf->size); return ha; } static long xdl_mmob_size(mmoffbuffer_t *obf, int n) { return n > 0 ? obf[n - 1].off + obf[n - 1].size: 0; } static mmoffbuffer_t *xdl_mmob_new(mmoffbuffer_t **probf, int *pnobf, int *paobf) { int aobf; mmoffbuffer_t *cobf, *rrobf; if (*pnobf >= *paobf) { aobf = 2 * (*paobf) + 1; if ((rrobf = (mmoffbuffer_t *) xdl_realloc(*probf, aobf * sizeof(mmoffbuffer_t))) == NULL) { return NULL; } *probf = rrobf; *paobf = aobf; } cobf = (*probf) + (*pnobf); (*pnobf)++; return cobf; } static int xdl_mmob_find_cntr(mmoffbuffer_t *obf, int n, long off) { int i, lo, hi; for (lo = -1, hi = n; hi - lo > 1;) { i = (hi + lo) / 2; if (off < obf[i].off) hi = i; else lo = i; } return (lo >= 0 && off >= obf[lo].off && off < obf[lo].off + obf[lo].size) ? lo: -1; } static int xdl_bmerge(mmoffbuffer_t *obf, int n, mmbuffer_t *mbfp, mmoffbuffer_t **probf, int *pnobf) { int i, aobf, nobf; long ooff, off, csize; unsigned long fp, ofp; unsigned char const *data, *top; mmoffbuffer_t *robf, *cobf; if (mbfp->size < XDL_BPATCH_HDR_SIZE) { return -1; } data = (unsigned char const *) mbfp->ptr; top = data + mbfp->size; ofp = xdl_mmob_adler32(obf, n); XDL_LE32_GET(data, fp); data += 4; XDL_LE32_GET(data, csize); data += 4; if (fp != ofp || csize != xdl_mmob_size(obf, n)) { return -1; } aobf = XDL_MOBF_MINALLOC; nobf = 0; if ((robf = (mmoffbuffer_t *) xdl_malloc(aobf * sizeof(mmoffbuffer_t))) == NULL) { return -1; } for (ooff = 0; data < top;) { if (*data == XDL_BDOP_INS) { data++; if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) { xdl_free(robf); return -1; } cobf->off = ooff; cobf->size = (long) *data++; cobf->ptr = (char *) data; data += cobf->size; ooff += cobf->size; } else if (*data == XDL_BDOP_INSB) { data++; XDL_LE32_GET(data, csize); data += 4; if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) { xdl_free(robf); return -1; } cobf->off = ooff; cobf->size = csize; cobf->ptr = (char *) data; data += cobf->size; ooff += cobf->size; } else if (*data == XDL_BDOP_CPY) { data++; XDL_LE32_GET(data, off); data += 4; XDL_LE32_GET(data, csize); data += 4; if ((i = xdl_mmob_find_cntr(obf, n, off)) < 0) { xdl_free(robf); return -1; } off -= obf[i].off; for (; i < n && csize > 0; i++, off = 0) { if ((cobf = xdl_mmob_new(&robf, &nobf, &aobf)) == NULL) { xdl_free(robf); return -1; } cobf->off = ooff; cobf->size = XDL_MIN(csize, obf[i].size - off); cobf->ptr = obf[i].ptr + off; ooff += cobf->size; csize -= cobf->size; } if (csize > 0) { xdl_free(robf); return -1; } } else { xdl_free(robf); return -1; } } *probf = robf; *pnobf = nobf; return 0; } static int xdl_bmerge_synt(mmoffbuffer_t *obf, int n, xdemitcb_t *ecb) { int i; mmbuffer_t *mb; if ((mb = (mmbuffer_t *) xdl_malloc(n * sizeof(mmbuffer_t))) == NULL) { return -1; } for (i = 0; i < n; i++) { mb[i].ptr = obf[i].ptr; mb[i].size = obf[i].size; } if (ecb->outf(ecb->priv, mb, n) < 0) { xdl_free(mb); return -1; } xdl_free(mb); return 0; } int xdl_bpatch_multi(mmbuffer_t *base, mmbuffer_t *mbpch, int n, xdemitcb_t *ecb) { int i, nobf, fnobf; mmoffbuffer_t *obf, *fobf; nobf = 1; if ((obf = (mmoffbuffer_t *) xdl_malloc(nobf * sizeof(mmoffbuffer_t))) == NULL) { return -1; } obf->off = 0; obf->ptr = base->ptr; obf->size = base->size; for (i = 0; i < n; i++) { if (xdl_bmerge(obf, nobf, &mbpch[i], &fobf, &fnobf) < 0) { xdl_free(obf); return -1; } xdl_free(obf); obf = fobf; nobf = fnobf; } if (xdl_bmerge_synt(obf, nobf, ecb) < 0) { xdl_free(obf); return -1; } xdl_free(obf); return 0; }