text: /*
   * Copyright (c) 2005 Michael Schroeder (mls@suse.de)
   *
   * This program is licensed under the BSD license, read LICENSE.BSD
   * for further information
   */
  
  #define _LARGEFILE64_SOURCE
  
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  #include <stdint.h>
  
  #include <zlib.h>
  #include <bzlib.h>
  #include <lzma.h>
  
  #include "rpmoffs.h"
  #include "delta.h"
  #include "util.h"
  #include "md5.h"
  #include "cfile.h"
  
  double targetsize;
  double writtensize;
  
  unsigned int readiso(FILE *fp, struct rpmpay *pay, int payn, unsigned char **isop)
  {
    off64_t size;
    off64_t skipsum;
    off64_t last;
    int i;
    unsigned char *iso;
    unsigned int l, p, lastl;
    char *lastn;
  
    last = 0;
    lastl = 0;
    skipsum = 0;
    lastn = "<start>";
    for (i = 0; i < payn; i++)
      {
        if (pay[i].o == last)
  	{
  	  if (pay[i].l != lastl)
  	    {
  	      fprintf(stderr, "files at same pos with different sizes\n");
  	      exit(1);
  	    }
  	  continue;
  	}
        if (last + lastl > pay[i].o)
  	{
  	  fprintf(stderr, "files overlap %s %s %lld %d %lld\n", lastn, pay[i].name, (long long int)last, lastl, (long long int)pay[i].o);
  	  exit(1);
  	}
        last = pay[i].o;
        lastl = pay[i].l;
        lastn = pay[i].name;
        skipsum += pay[i].l;
      }
    if (fseeko64(fp, (off64_t)0, SEEK_END) != 0)
      {
        perror("fseeko64");
        exit(1);
      }
    size = ftello64(fp);
    if (size < skipsum)
      {
        fprintf(stderr, "size < skipsum\n");
        exit(1);
      }
    if (fseeko64(fp, (off64_t)0, SEEK_SET) != 0)
      {
        perror("fseeko64");
        exit(1);
      }
    if (size - skipsum >= 0x80000000)
      {
        fprintf(stderr, "remaining size too big: %lld\n", (long long int)(size - skipsum));
        exit(1);
      }
    l = size - skipsum;
    if ((iso = malloc(l)) == 0)
      {
        fprintf(stderr, "out of mem for iso (%d bytes)", l);
        exit(1);
      }
    last = 0;
    lastl = 0;
    p = 0;
    pay[payn].o = size;
    for (i = 0; i <= payn; i++)
      {
        if (pay[i].o == last)
  	continue;
        if (fread(iso + p, pay[i].o - (last + lastl), 1, fp) != 1)
  	{
  	  fprintf(stderr, "read error\n");
  	  exit(1);
  	}
        pay[i].x = p;
        pay[i].lx = pay[i].o - (last + lastl);
        p += pay[i].o - (last + lastl);
        last = pay[i].o;
        lastl = pay[i].l;
        if (fseeko64(fp, pay[i].o + pay[i].l, SEEK_SET))
  	{
  	  perror("fseeko64");
  	  exit(1);
  	}
      }
    if (p != l)
      {
        fprintf(stderr, "internal error %d %d\n", p, l);
        exit(1);
      }
    *isop = iso;
    return l;
  }
  
  void
  recode_instr(struct instr *instr, int instrlen, unsigned int **b1p, int *nb1p, unsigned int **b2p, int *nb2p, struct rpmpay *pay, int payn)
  {
    unsigned int *b1;
    int nb1;
    unsigned int *b2;
    int nb2;
    int i, j;
    int lastoff;
    unsigned int x1, x2, x3, x4;
    unsigned int left;
    int payp;
  
    b1 = b2 = 0; 
    nb1 = nb2 = 0;
    j = 0;  
    lastoff = 0;
    left = pay && payn ? pay[0].lx : 0;
    payp = 0;
    for (i = 0; i < instrlen; i++)
      {
        x1 = instr[i].copyout;
        x2 = instr[i].copyin;
        x3 = instr[i].copyoutoff;
        x4 = instr[i].copyinoff;
  retry:
        if (!x1)
          {
            if (!x2)
              continue;
          }
        if (x1)
          {
            if ((nb2 & 15) == 0)
              b2 = xrealloc2(b2, nb2 + 16, 2 * sizeof(unsigned int));
            if (lastoff <= x3)
              b2[2 * nb2] = x3 - lastoff;
            else
              b2[2 * nb2] = (lastoff - x3) | 0x80000000;
            if (left && x1 >= left)
  	    {
  	      b2[2 * nb2 + 1] = left;
  	      nb2++;
  	      j++;
  	      x3 = lastoff = x3 + left;
  	      x1 -= left;
  	      if ((nb1 & 15) == 0)
  		b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int));
  	      b1[3 * nb1] = j;
  	      b1[3 * nb1 + 1] = 0xffffffff;
  	      b1[3 * nb1 + 2] = payp;
  	      nb1++;
  	      j = 0;
  	      left = 0;
  	      while (payp < payn && pay[++payp].x == 0)
  		;
  	      if (payp < payn)
  		left = pay[payp].lx;
  	      goto retry;
  	    }
            else
  	    {
  	      b2[2 * nb2 + 1] = x1;
  	      nb2++;
  	      j++;
  	      x3 = lastoff = x3 + x1;
  	      if (left)
  	        left -= x1;
  	    }
          }
        if (x2)
          {
            if (left && x2 >= left)
  	    {
  	      if ((nb1 & 15) == 0)
  		b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int));
  	      b1[3 * nb1] = j;
  	      b1[3 * nb1 + 1] = left;
  	      b1[3 * nb1 + 2] = x4;
  	      nb1++;
  	      j = 0;
  	      x2 -= left;
  	      x4 += left;
  	      if ((nb1 & 15) == 0)
  		b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int));
  	      b1[3 * nb1] = 0;
  	      b1[3 * nb1 + 1] = 0xffffffff;
  	      b1[3 * nb1 + 2] = payp;
  	      nb1++;
  	      left = 0;
  	      while (payp < payn && pay[++payp].x == 0)
  		;
  	      if (payp < payn)
  		left = pay[payp].lx;
  	      x1 = 0;
  	      goto retry;
  	    }
            if ((nb1 & 15) == 0)
              b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int));
            b1[3 * nb1] = j;
            b1[3 * nb1 + 1] = x2;
            b1[3 * nb1 + 2] = x4;
            nb1++;
            j = 0;
  	  if (left)
  	    left -= x2;
          }
      }
    if (left || payp != payn)
      {
        fprintf(stderr, "oops, left = %d %d %d\n", left, payp, payn);
        exit(1);
      }
    if (j)
      {
        if ((nb1 & 15) == 0)
          b1 = xrealloc2(b1, nb1 + 16, 3 * sizeof(unsigned int));
        b1[3 * nb1] = j;
        b1[3 * nb1 + 1] = 0;
        b1[3 * nb1 + 2] = 0;
        nb1++;
      }
    *b1p = b1;
    *nb1p = nb1;
    *b2p = b2;
    *nb2p = nb2;
  }
  
  void
  bzput4(struct cfile *fp, unsigned int d)
  {
    unsigned char dd[4];
    dd[0] = d >> 24;
    dd[1] = d >> 16;
    dd[2] = d >> 8;
    dd[3] = d;
    if (fp->write(fp, dd, 4) != 4)
      {
        perror("bzwrite");
        exit(1);
      }
  }
  
  void
  put4(FILE *fp, unsigned int d)
  {
    unsigned char dd[4];
    dd[0] = d >> 24;
    dd[1] = d >> 16;
    dd[2] = d >> 8;
    dd[3] = d;
    if (fwrite(dd, 4, 1, fp) != 1)
      {
        perror("fwrite");
        exit(1);
      }
  }
  
  void diffit(struct cfile *fpout, FILE *fpold, FILE *fpnew, unsigned char *old, unsigned int oldl, unsigned char *new, int newl, struct rpmpay *newpays, int newpayn, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx);
  
  unsigned int payrawread(FILE *fp, off64_t off, unsigned int len, unsigned char **pp)
  {
    int l, r;
    struct cfile *cfile;
  
    if (fseeko64(fp, off, SEEK_SET) != 0)
      {
        perror("fseeko");
        exit(1);
      }
    cfile = cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fp, CFILE_COMP_UN, len, 0, 0);
    if (!cfile)
      {
        fprintf(stderr, "cfile open failed\n");
        exit(1);
      }
    l = cfile_copy(cfile, cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, pp, CFILE_COMP_UN, CFILE_LEN_UNLIMITED, 0, 0), CFILE_COPY_CLOSE_OUT);
    if (l == -1)
      {
        fprintf(stderr, "cfile_copy failed\n");
        exit(1);
      }
    r = cfile->close(cfile);
    if (r)
      {
        fprintf(stderr, "cfile not used up (%d bytes left)\n", r);
        exit(1);
      }
    return l;
  }
  
  unsigned int payread(FILE *fp, off64_t off, unsigned int len, unsigned char **pp, MD5_CTX *ctx, unsigned char *namebuf)
  {
    int l, r;
    struct cfile *cfile;
  
    if (fseeko64(fp, off, SEEK_SET) != 0)
      {
        perror("fseeko");
        exit(1);
      }
    cfile = cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fp, CFILE_COMP_XX, len, ctx ? (cfile_ctxup)rpmMD5Update : 0, ctx);
    if (!cfile)
      {
        fprintf(stderr, "cfile open failed\n");
        exit(1);
      }
    if (namebuf)
      {
        cfile_detect_rsync(cfile);
        namebuf[0] = cfile->comp;
      }
    l = cfile_copy(cfile, cfile_open(CFILE_OPEN_WR, CFILE_IO_ALLOC, pp, CFILE_COMP_UN, CFILE_LEN_UNLIMITED, 0, 0), CFILE_COPY_CLOSE_OUT);
    if (l == -1)
      {
        fprintf(stderr, "cfile_copy failed\n");
        exit(1);
      }
    r = cfile->close(cfile);
    if (r)
      {
        fprintf(stderr, "cfile not used up (%d bytes left)\n", r);
        exit(1);
      }
    return l;
  }
  
  void
  processrpm(struct cfile *fpout, FILE *fpold, FILE *fpnew, struct rpmpay *pay, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx)
  {
    struct rpmpay *oldpay;
    int i, n, payrawcmp;
    unsigned int l, newl, oldl;
    unsigned char *new, *old;
    unsigned char namebuf[258];
  
    oldpay = 0;
    for (i = 0; i < oldpayn; i++)
      if (!strcmp(oldpays[i].name, pay->name))
        {
          oldpay = oldpays + i;
          break;
        }
    l = strlen(pay->name);
    if (l > 255)
      l = 255;
    namebuf[0] = 255;
    namebuf[1] = l;
    memcpy(namebuf + 2, pay->name, l);
    namebuf[l + 2] = 0;
    targetsize += pay->l;
    if (!oldpay)
      {
        printf("%s: not found in old iso...", pay->name);
        if (fpout->write(fpout, namebuf, l + 2) != l + 2)
  	{
  	  perror("namebuf write");
  	  exit(1);
  	}
        bzput4(fpout, pay->l);
        if (fseeko64(fpnew, pay->o, SEEK_SET) != 0)
  	{
  	  perror("fseeko");
  	  exit(1);
  	}
        if (cfile_copy(cfile_open(CFILE_OPEN_RD, CFILE_IO_FILE, fpnew, CFILE_COMP_UN, pay->l, (cfile_ctxup)rpmMD5Update, ctx), fpout, CFILE_COPY_CLOSE_IN) != 0)
  	{
  	  fprintf(stderr, "cfile_copy failed\n");
  	  exit(1);
  	}
      }
    else
      {
        n = 0;
        for (i = 0; i < oldpayn; i++)
  	if (i == 0 || oldpays[i].x != 0)
  	  {
  	    if (oldpays[i].o == oldpay->o)
  	      break;
  	    else
  	      n++;
  	  }
        if (i == oldpayn)
  	{
  	  fprintf(stderr, "internal error\n");
  	  exit(1);
  	}
        if (oldpay->l != oldpays[i].l)
  	{
  	  fprintf(stderr, "internal error, length mismatch %d %d\n", oldpay->l, oldpays[i].l);
  	  exit(1);
  	}
        /* compare the raw payloads */
        newl = payrawread(fpnew, pay->o, pay->l, &new);
        oldl = payrawread(fpold, oldpay->o, oldpay->l, &old);
        if (newl == oldl && pay->l == oldpay->l)
  	{
  	  payrawcmp = memcmp(new, old, newl);
  	}
        else
  	{
  	  payrawcmp = 1;
  	}
        free(old);
        old = 0;
        oldl = 0;
        free(new);
        new = 0;
        newl = 0;
  	
        /* payread will fix namebuf[0] */
        newl = payread(fpnew, pay->o, pay->l, &new, ctx, namebuf);
  
        if (!payrawcmp)
  	{
  	  printf("%s: unchanged...", namebuf + 2);
  	  namebuf[0] = 254;
  	}
        else
  	{
  	  oldl = payread(fpold, oldpay->o, oldpay->l, &old, 0, 0);
  	  int comp = cfile_setlevel(namebuf[0], pay->level);
  	  printf("%s (%s): creating delta...", namebuf + 2, cfile_comp2str(comp));
  	  namebuf[0] = CFILE_COMPALGO(comp) | (CFILE_COMPLEVEL(comp) << 4);	/* argh! */
  	}
        fflush(stdout);
        if (fpout->write(fpout, namebuf, l + 2) != l + 2)
  	{
  	  perror("namebuf write");
  	  exit(1);
  	}
        bzput4(fpout, n);		/* offset id */
        if (namebuf[0] != 254)
          diffit(fpout, 0, 0, old, oldl, new, newl, 0, 0, 0, 0, 0);
        old = 0;
        oldl = 0;
        free(new);
        new = 0;
        newl = 0;
      }
    writtensize += fpout->bytes;
    fpout->bytes = 0;
    printf("%4.1f%%\n", writtensize * 100 / targetsize);
  }
  
  /* frees old! */
  void diffit(struct cfile *fpout, FILE *fpold, FILE *fpnew, unsigned char *old, unsigned int oldl, unsigned char *new, int newl, struct rpmpay *newpays, int newpayn, struct rpmpay *oldpays, int oldpayn, MD5_CTX *ctx)
  {
    unsigned int *b1;
    int nb1;
    unsigned int *b2;
    int nb2;
    struct instr *instr = 0;
    int instrlen = 0;
    unsigned char *addblk = 0;
    unsigned int addblklen = 0;
    int i, j, b2i;
    unsigned int off;
  
    mkdiff(DELTAMODE_HASH, old, oldl, new, newl, &instr, &instrlen, 0, 0, &addblk, &addblklen, 0, 0);
    free(old);
    old = 0;
    recode_instr(instr, instrlen, &b1, &nb1, &b2, &nb2, newpays, newpayn);
    free(instr);
    instr = 0;
    instrlen = 0;
    bzput4(fpout, oldl);
    bzput4(fpout, newl);
    bzput4(fpout, nb1);
    bzput4(fpout, nb2);
    for (i = 0; i < nb1; i++)
      bzput4(fpout, b1[3 * i]);
    for (i = 0; i < nb1; i++)
      bzput4(fpout, b1[3 * i + 1]);
    for (i = 0; i < nb2; i++)
      bzput4(fpout, b2[2 * i]);
    for (i = 0; i < nb2; i++)
      bzput4(fpout, b2[2 * i + 1]);
  
    /* write add section */
    bzput4(fpout, addblklen);
    if (addblklen && fpout->write(fpout, addblk, addblklen) != addblklen)
      {
        perror("bzwrite");
        exit(1);
      }
    if (addblk)
      free(addblk);
  
    /* write data section */
    b2i = 0;
    off = 0;
    for (i = 0; i < nb1; i++)
      {
        if (ctx)
  	{
  	  for (j = 0; j < b1[3 * i]; j++, b2i++)
  	    {
  	      if (b2[2 * b2i + 1])
  	        rpmMD5Update(ctx, new + off, b2[2 * b2i + 1]);
  	      off += b2[2 * b2i + 1];
  	    }
  	}
        if (b1[3 * i + 1] == 0xffffffff)
  	{
  	  processrpm(fpout, fpold, fpnew, newpays + b1[3 * i + 2], oldpays, oldpayn, ctx);
  	}
        else if (b1[3 * i + 1])
  	{
  	  if (ctx)
  	    {
  	      if (off != b1[3 * i + 2])
  		{
  		  fprintf(stderr, "internal error: off mismatch %d %d\n", off, b1[3 * i + 2]);
  		  exit(1);
  		}
  	      rpmMD5Update(ctx, new + off, b1[3 * i + 1]);
  	      off += b1[3 * i + 1];
  	    }
  	  if (fpout->write(fpout, new + b1[3 * i + 2], b1[3 * i + 1]) != b1[3 * i + 1])
  	    {
  	      perror("bzwrite");
  	      exit(1);
  	    }
  	}
      }
    free(b1);
    free(b2);
  }
  
  int
  main(int argc, char **argv)
  {
    FILE *fpold, *fpnew, *fpout;
    unsigned int oldisolen;
    unsigned int newisolen;
    unsigned char *oldiso;
    unsigned char *newiso;
    struct rpmpay *oldpays = 0;
    int oldpayn = 0;
    struct rpmpay *newpays = 0;
    int newpayn = 0;
    int i, n;
    struct cfile *bf;
    MD5_CTX targetmd5;
    unsigned char targetmd5res[16];
  
    if (argc != 4)
      {
        fprintf(stderr, "usage: makedeltaiso <oldiso> <newiso> <deltaiso>\n");
        exit(1);
      }
    if ((fpold = fopen64(argv[1], "r")) == 0)
      {
        perror(argv[1]);
        exit(1);
      }
    if ((fpnew = fopen64(argv[2], "r")) == 0)
      {
        perror(argv[2]);
        exit(1);
      }
    oldpayn = rpmoffs(fpold, argv[1], &oldpays);
    printf("%s: %d rpms\n", argv[1], oldpayn);
    newpayn = rpmoffs(fpnew, argv[2], &newpays);
    printf("%s: %d rpms\n", argv[2], newpayn);
    if ((fpout = fopen64(argv[3], "w")) == 0)
      {
        perror(argv[3]);
        exit(1);
      }
    printf("reading old iso (omitting payloads)\n");
    oldisolen = readiso(fpold, oldpays, oldpayn, &oldiso);
    printf("size without payloads is %d bytes\n", oldisolen);
    printf("reading new iso (omitting payloads)\n");
    newisolen = readiso(fpnew, newpays, newpayn, &newiso);
    printf("size without payloads is %d bytes\n", newisolen);
    targetsize = newisolen;
  
    putc('D', fpout);
    putc('I', fpout);
    putc('S', fpout);
    putc('O', fpout);
    put4(fpout, 2);
    if ((bf = cfile_open(CFILE_OPEN_WR, CFILE_IO_FILE, fpout, CFILE_COMP_BZ, CFILE_LEN_UNLIMITED, 0, 0)) == 0)
      {
        fprintf(stderr, "cfile wopen failed\n");
        exit(1);
      }
    /* write old map */
    n = 0;
    for (i = 0; i < oldpayn; i++)
      if (i == 0 || oldpays[i].x)
        n++;
    bzput4(bf, n);
    for (i = 0; i < oldpayn; i++)
      if (i == 0 || oldpays[i].x)
        {
          bzput4(bf, oldpays[i].lx);
          bzput4(bf, oldpays[i].l);
        }
    bzput4(bf, oldpays[i].lx);
  
    printf("creating iso diff\n");
    rpmMD5Init(&targetmd5);
    diffit(bf, fpold, fpnew, oldiso, oldisolen, newiso, newisolen, newpays, newpayn, oldpays, oldpayn, &targetmd5);
    rpmMD5Final(targetmd5res, &targetmd5);
    oldiso = 0;
    oldisolen = 0;
    free(newiso);
    newiso = 0;
    newisolen = 0;
    writtensize += bf->bytes;
    bf->bytes = 0;
    printf("iso diff done, final compression: %4.1f%%\n", writtensize * 100 / targetsize);
    if (bf->write(bf, targetmd5res, 16) != 16)
      {
        perror("md5sum write");
        exit(1);
      }
    if (bf->close(bf) == -1)
      {
        perror("cfile close");
        exit(1);
      }
    if (fclose(fpout))
      {
        perror("fclose");
        exit(1);
      }
    printf("iso md5sum is: ");
    for (i = 0; i < 16; i++)
      printf("%02x", targetmd5res[i]);
    printf("\n");
    for (i = 0; i < oldpayn; i++)
      free(oldpays[i].name);
    free(oldpays);
    for (i = 0; i < newpayn; i++)
      free(newpays[i].name);
    free(newpays);
    exit(0);
  }
  