/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
 */
/*
 * AtFS -- Attribute Filesystem
 *
 * afdelta.c -- Delta processing
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afdelta.c[7.0] Wed Jul  7 19:05:08 1993 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

/*====================================================================
 *    af_nodelta
 *
 * Store the busy version (busykey) without building a delta.
 * "nodelta" is necessary for saving the initial version (1.0)
 * or for storing versions without delta technique.
 *
 *====================================================================*/

EXPORT int af_nodelta (busykey, savekey)
     Af_key *busykey;
     Af_key *savekey;
{
  register FILE	*busyfile;
  
  VATTR(savekey).af_fsize = af_retfsize (busykey->af_ldes->af_busyfilename);
  VATTR(savekey).af_dsize = 0;
  VATTR(savekey).af_repr = AF_CHUNK;
  if ((VATTR(savekey).af_data = af_malloc (savekey->af_ldes, (unsigned) VATTR(savekey).af_fsize)) == NULL)
    return (ERROR);

  if ((busyfile = fopen (busykey->af_ldes->af_busyfilename, "r")) == NULL)
    FAIL ("nodelta", "fopen", AF_ESYSERR, ERROR);

  if (fread (VATTR(savekey).af_data, sizeof (char), (size_t) VATTR(savekey).af_fsize, busyfile) != VATTR(savekey).af_fsize)
    FAIL ("nodelta", "fread", AF_ESYSERR, ERROR);

  fclose (busyfile);
  savekey->af_ldes->af_datasize += VATTR(savekey).af_fsize;
  return (AF_OK);
} /* af_nodelta */



/*====================================================================
 *    af_dodelta
 *
 * Build a delta between a busy version (busykey) and its preceeding
 * version (predkey) and store it as a new version (savekey).
 * If no busykey is given , build a null delta and store
 * it as new version.
 * The latter case is needed, if a version shall be stored with different
 * attributes but unchanged contents. 
 *
 *====================================================================*/

EXPORT int af_dodelta (busykey, predkey, savekey)
     Af_key *busykey;
     Af_key *predkey;
     Af_key *savekey;
{
  register char	*delname;
  register FILE	*busyfile, *delfile;
  
  if (busykey) {
    /* read in busyfile for delta processing */
    VATTR(savekey).af_fsize = af_retfsize(busykey->af_ldes->af_busyfilename);
      
    if ((VATTR(savekey).af_data = af_malloc (savekey->af_ldes, (unsigned) VATTR(savekey).af_fsize)) == NULL)
      return (ERROR);

    if ((busyfile = fopen (busykey->af_ldes->af_busyfilename,"r")) == NULL)
      FAIL ("dodelta", "fopen", AF_ESYSERR, ERROR);

    if (fread (VATTR(savekey).af_data, sizeof (char), (size_t) VATTR(savekey).af_fsize, busyfile) != VATTR(savekey).af_fsize)
      FAIL ("dodelta", "fread", AF_ESYSERR, ERROR);

    fclose (busyfile);
    savekey->af_ldes->af_datasize += VATTR(savekey).af_fsize;
    VATTR(savekey).af_dsize = 0;
    VATTR(savekey).af_repr = AF_CHUNK;
  }
  else {
    /* produce null delta (new generation) */
    VATTR(savekey).af_data = VATTR(predkey).af_data;
    VATTR(savekey).af_fsize = VATTR(predkey).af_fsize;
    VATTR(savekey).af_dsize = 0;
    VATTR(savekey).af_repr = AF_CHUNK;
    savekey->af_ldes->af_datasize += VATTR(savekey).af_fsize;
  }

  /* if version opens a new branch, no delta processing is necessary */
  if ((predkey->af_ldes == NULL) || (VATTR(predkey).af_repr == AF_DELTA))
    return (AF_OK);

  /* else do delta processing */

  /* create filename for delta */
  delname = af_gtmpname ("/tmp");
  af_regtmpfile (delname);

  if (afMakeDelta (VATTR(savekey).af_data, VATTR(predkey).af_data,
      VATTR(savekey).af_fsize, VATTR(predkey).af_fsize, delname) == ERROR)
    FATAL ("dodelta", "", AF_EDELTA, ERROR);

  /* update predversion */
  /* read deltafile if present */
  if ((VATTR(predkey).af_dsize = af_retfsize (delname)) != (size_t)0) {
    if ((VATTR(predkey).af_dsize > VATTR(predkey).af_fsize) || (busykey == NULL))
      if ((VATTR(predkey).af_data = af_malloc (predkey->af_ldes, (unsigned) VATTR(predkey).af_dsize)) == NULL)
	return (ERROR);

    if ((delfile = fopen (delname, "r")) == NULL)
      FAIL ("dodelta", "fopen", AF_ESYSERR, ERROR);

    if ((fread (VATTR(predkey).af_data, sizeof (char), (size_t) VATTR(predkey).af_dsize, delfile)) != VATTR(predkey).af_dsize)
      FAIL ("dodelta", "fread", AF_ESYSERR, ERROR);
    fclose (delfile);
  }
  else {
    VATTR(predkey).af_data = NULL;
  }

  /* remove tmp-file */
  unlink (delname);
  af_unregtmpfile (delname);
  
  /* update list descriptor (file is replaced by delta) */
  savekey->af_ldes->af_datasize -= VATTR(predkey).af_fsize;
  savekey->af_ldes->af_datasize += VATTR(predkey).af_dsize;
  VATTR(predkey).af_repr = AF_DELTA;
  VATTR(predkey).af_succgen = VATTR(savekey).af_gen;
  VATTR(predkey).af_succrev = VATTR(savekey).af_rev;
  
  return (AF_OK);
} /* af_dodelta */



/*====================================================================
 *    af_undodelta
 *
 * The version pointed to by deltakey is supposed to be a (at least)
 * saved version represented by a chunk (AF_CHUNK) or a delta (AF_DELTA).
 * Passing a busy version causes an error (AF_NOVERS).
 * Filename has to be the name of an accessible UNIX-file,
 * where the rebuilt version will be stored.
 * The version pointed to by deltakey remains unchanged. 
 *
 *====================================================================*/

EXPORT int af_undodelta (deltakey, filename)
     Af_key *deltakey;
     char   *filename;
{
  register Af_key *deltachain, *keyptr;
  char            *tmpname, *data = NULL;
  register int    i;
  size_t           fsize;
  register FILE   *tmpfile;

  /* this error should never occur */
  if (VATTR(deltakey).af_repr == AF_FILE)
    FATAL ("undodelta", "wrong kind of representation", AF_EINTERNAL, ERROR);

  if ((deltachain = (Af_key *) malloc ((unsigned) (AF_SEGLEN * sizeof (Af_key)))) == NULL)
    FAIL ("undodelta", "malloc", AF_ESYSERR, ERROR);

  /* collect deltachain */
  i = 0;
  deltachain[0] = *deltakey;
  keyptr = deltakey;
  while (VATTR(keyptr).af_repr == AF_DELTA) {
    i++;
    if ((i & AF_SEGLEN) == AF_SEGLEN) /* if segment is full */
      if ((deltachain = (Af_key *) realloc ((char *) deltachain, (unsigned) ((i + AF_SEGLEN) * sizeof (Af_key)))) == NULL)
	FAIL ("undodelta", "realloc", AF_ESYSERR, ERROR);

    if (af_buildkey (deltakey->af_ldes, VATTR(keyptr).af_succgen,
		     VATTR(keyptr).af_succrev, &(deltachain[i])) == ERROR)
      FATAL ("undodelta", "delta chain broken", AF_EINTERNAL, ERROR);
    keyptr = &(deltachain[i]);
  }

  if ((fsize = VATTR(keyptr).af_fsize) > 0) {
    if ((data = malloc ((unsigned) fsize)) == NULL)
      FAIL ("undodelta", "malloc", AF_ESYSERR, ERROR);
    memcpy (data, VATTR(keyptr).af_data, (size_t) fsize);
  }
  /* first step is done */
  i--;

  /* process delta chain */
  tmpname = af_gtmpname ("/tmp");
  af_regtmpfile (tmpname);

  if (i < 0) { /* no delta chain */
    if ((tmpfile = fopen (tmpname, "w")) == NULL)
      FAIL ("undodelta", "fopen", AF_ESYSERR, ERROR);
    if (fsize > 0)
      fwrite (data, (size_t) fsize, sizeof (char), tmpfile);
    fclose (tmpfile);
  }
      
  /* else */
  for (; i >= 0; i--) {
    keyptr = &(deltachain[i]);
    if (afReconsData (data, VATTR(keyptr).af_data, fsize, VATTR(keyptr).af_dsize, tmpname) == ERROR)
      FATAL ("undodelta", "", AF_EDELTA, ERROR);
    if (i == 0) 
      continue;  /* increase performance */
    if ((fsize = af_retfsize (tmpname)) > 0) {
      if (data) {
	if ((data = realloc (data, (unsigned)(fsize * sizeof (char)))) == NULL)
	  FAIL ("undodelta", "realloc", AF_ESYSERR, ERROR);
      }
      else {
	if ((data = malloc ((unsigned)(fsize * sizeof (char)))) == NULL)
	  FAIL ("undodelta", "realloc", AF_ESYSERR, ERROR);
      }
      if ((tmpfile = fopen (tmpname, "r")) == NULL)
	FAIL ("undodelta", "fopen", AF_ESYSERR, ERROR);
      fread (data, (size_t) fsize, sizeof (char), tmpfile);
      fclose (tmpfile);
    }
    else {
      if (data)
	free (data);
      data = NULL;
    }
  }
  free ((char *) deltachain);
  if (data)
    free (data);

  unlink (filename);
  if (link (tmpname, filename) == ERROR) {
    /* if link fails, try to copy file */
    if (af_cpfile (tmpname, af_retfsize (tmpname), filename) == ERROR)
      FAIL ("undodelta", "link/copy", AF_ESYSERR, ERROR);
  }
  unlink (tmpname);
  af_unregtmpfile (tmpname);

  return (AF_OK);
}

/*====================================================================
 *    af_rmdelta
 *
 *====================================================================*/

EXPORT int af_rmdelta (deltakey)
     Af_key *deltakey;
{
  register int i;
  int      succsize, predsize;
  char     *succdata, *preddata, *tmpname;
  register FILE *tmpfile;
  Af_key   *deltachain, *keyptr = NULL, *predptr;
                    /* deltachain shall hold all keys from the physical
		     * predecessor to the end of the physical line of 
		     * descent (chunk). That is:
		     * deltachain[0] = predecessor
		     * deltachain[1] = deltakey
		     * deltachain[2] = successor ...
		     * ... deltachain[n] = chunk
		     */

  if ((deltachain = (Af_key *) malloc ((unsigned) (AF_SEGLEN * sizeof (Af_key)))) == NULL)
    FAIL ("rmdelta", "malloc", AF_ESYSERR, ERROR);

  /* update predecessor attributes of successor version */
  if ((VATTR(deltakey).af_succgen != AF_NOVNUM) && 
      (VATTR(deltakey).af_succrev != AF_NOVNUM)) {
    af_buildkey (deltakey->af_ldes, VATTR(deltakey).af_succgen, VATTR(deltakey).af_succrev, &(deltachain[2]));
    VATTR((&(deltachain[2]))).af_predgen = VATTR(deltakey).af_predgen;
    VATTR((&(deltachain[2]))).af_predrev = VATTR(deltakey).af_predrev;
  }

  /* *** new succ/pred chaining requires also search for other */
  /* *** "successors". This is a bit "hacked" !!! */
  for (i = 0; i < deltakey->af_ldes->af_listlen; i++) {
    if (!(deltakey->af_ldes->af_list[i].af_class & AF_VALID))
      continue;
    if ((deltakey->af_ldes->af_list[i].af_predgen == VATTR(deltakey).af_gen) &&
	(deltakey->af_ldes->af_list[i].af_predrev == VATTR(deltakey).af_rev)) {
      deltakey->af_ldes->af_list[i].af_predgen = AF_NOVNUM;
      deltakey->af_ldes->af_list[i].af_predrev = AF_NOVNUM;
    }
  }

  /* if deltakey points to the predecessor of the busy version,  */
  /* update busy version (should be optimized) */
  /* works only, if busy version is at position 0 */
  if ((deltakey->af_ldes->af_list[0].af_predgen == VATTR(deltakey).af_gen) &&
      (deltakey->af_ldes->af_list[0].af_predrev == VATTR(deltakey).af_rev)) {
    deltakey->af_ldes->af_list[0].af_predgen = AF_NOVNUM;
    deltakey->af_ldes->af_list[0].af_predrev = AF_NOVNUM;
  }
  
  /* if deltakey points to first element in delta chain */
  /*    no delta processing is necessary */
  /* *** delta chaining has been changed meanwhile. So this only */
  /* *** applies to old archives and to the very first revision. */
  if ((VATTR(deltakey).af_predgen == AF_NOVNUM) && 
      (VATTR(deltakey).af_predrev == AF_NOVNUM))
    return (AF_OK);

  /* else collect delta chain */
  af_buildkey (deltakey->af_ldes, VATTR(deltakey).af_predgen,
	       VATTR(deltakey).af_predrev, &(deltachain[0]));
  predptr = &(deltachain[0]);

  /* if predecessor's successor successor is different from own version */
  /* number, we're at the starting pint of a branch -- no delta processing */
  /* necessary. This applies to new style succ/pred chaining */
  if ((VATTR(predptr).af_succgen != VATTR(deltakey).af_gen) ||
      (VATTR(predptr).af_succgen != VATTR(deltakey).af_gen))
    return (AF_OK);

  deltachain[1] = *deltakey;
  /* if deltakey points to chunk do nothing else */
  if ((VATTR(deltakey).af_succgen == AF_NOVNUM) && 
      (VATTR(deltakey).af_succrev == AF_NOVNUM)) {
    i = 1;
  }
  else {
    i = 2;
    keyptr = &(deltachain[2]);
    while (VATTR(keyptr).af_repr == AF_DELTA) {
      i++;
      if ((i & AF_SEGLEN) == AF_SEGLEN) /* if segment is full */
	if ((deltachain = (Af_key *) realloc ((char *) deltachain, (unsigned) ((i+AF_SEGLEN) * sizeof(Af_key)))) == NULL)
	  FAIL ("rmdelta", "realloc", AF_ESYSERR, ERROR);
      
      af_buildkey (deltakey->af_ldes, VATTR(keyptr).af_succgen,
		   VATTR(keyptr).af_succrev, &(deltachain[i]));
      keyptr = &(deltachain[i]);
    }
  }

  tmpname = af_gtmpname ("/tmp");
  af_regtmpfile (tmpname);

  /* if deltakey points to chunk, only the predecessor version has to */
  /*    be rebuilt */
  if (i == 1) {
    /* generate chunk for predecessor */
    if (afReconsData (VATTR(deltakey).af_data, VATTR(predptr).af_data,
		      VATTR(deltakey).af_fsize, VATTR(predptr).af_dsize, tmpname)
	== ERROR)
      FATAL ("rmdelta", "", AF_EDELTA, ERROR);

    VATTR(predptr).af_repr = AF_CHUNK;
    /* update sizes in revision list descriptor and in attribute buffer */
    deltakey->af_ldes->af_datasize -= VATTR(predptr).af_dsize;
    VATTR(predptr).af_dsize = 0;
    VATTR(predptr).af_fsize = af_retfsize (tmpname);
    deltakey->af_ldes->af_datasize += VATTR(predptr).af_fsize;

    /* if VATTR(predptr).af_data points to memory allocated by an own
     * af_malloc (and not in af_readdata), this memory segment remains
     * "unfreed" (This is a BUG !).
     */
    if ((VATTR(predptr).af_data = af_malloc (predptr->af_ldes, (unsigned) (VATTR(predptr).af_fsize * sizeof(char)))) == NULL)
      return (ERROR);

    tmpfile = fopen (tmpname, "r");
    fread (VATTR(predptr).af_data, (size_t) VATTR(predptr).af_fsize, sizeof (char), tmpfile);
    fclose (tmpfile);
    unlink (tmpname);
    af_unregtmpfile (tmpname);
    VATTR(predptr).af_succgen = AF_NOVNUM;
    VATTR(predptr).af_succrev = AF_NOVNUM;
    return (AF_OK);
  }
      
  /* else process delta chain and build pred and succ version */
  succsize = VATTR(keyptr).af_fsize;
  if ((succdata = malloc ((unsigned) (VATTR(keyptr).af_fsize * sizeof (char)))) == NULL)
    FAIL ("rmdelta", "malloc", AF_ESYSERR, ERROR);
  memcpy (succdata, VATTR(keyptr).af_data, succsize);
  /* first step is done */
  i--;

  /* first regenerate sucessor version and leave it in the buffer "succdata" */
  for (; i >= 2; i--) {
    keyptr = &(deltachain[i]);
    if (afReconsData (succdata, VATTR(keyptr).af_data, (long) succsize,
		      VATTR(keyptr).af_dsize, tmpname) == ERROR)
      FATAL ("rmdelta", "", AF_EDELTA, ERROR);
    
    succsize = af_retfsize (tmpname);
    if ((succdata = realloc (succdata, (unsigned) (succsize * sizeof(char)))) == NULL)
      FAIL ("rmdelta", "realloc", AF_ESYSERR, ERROR);
    tmpfile = fopen (tmpname, "r");
    fread (succdata, (size_t)succsize, sizeof (char), tmpfile);
    fclose (tmpfile);
  }
  /* regenerate predecessor version in buffer "preddata" */
  if (afReconsData (succdata, VATTR(deltakey).af_data, (long) succsize,
		    VATTR(deltakey).af_dsize, tmpname) == ERROR)
    FATAL ("rmdelta", "", AF_EDELTA, ERROR);

  predsize = af_retfsize (tmpname);
  if ((preddata = malloc ((unsigned) (predsize * sizeof (char)))) == NULL)
    FAIL ("rmdelta", "malloc", AF_ESYSERR, ERROR);
  tmpfile = fopen (tmpname, "r");
  fread (preddata, predsize, sizeof (char), tmpfile);
  fclose (tmpfile);
  if (afReconsData (preddata, VATTR((&(deltachain[0]))).af_data, (long) predsize, VATTR((&(deltachain[0]))).af_dsize, tmpname) == ERROR)
    FATAL ("rmdelta", "", AF_EDELTA, ERROR);
  
  predsize = af_retfsize (tmpname);
  if ((preddata = realloc (preddata, (unsigned) (predsize * sizeof (char)))) == NULL)
    FAIL ("rmdelta", "realloc", AF_ESYSERR, ERROR);
  tmpfile = fopen (tmpname, "r");
  fread (preddata, predsize, sizeof (char), tmpfile);
  fclose (tmpfile);
  
  /* build new delta for predecessor version */
  if (afMakeDelta (succdata, preddata, (long) succsize, (long) predsize, tmpname) == ERROR)
    FATAL ("rmdelta", "", AF_EDELTA, ERROR);

  /* update sizes in revision list descriptor and in attribute buffer */
  deltakey->af_ldes->af_datasize -= VATTR(predptr).af_dsize;
  if ((VATTR(predptr).af_dsize = af_retfsize (tmpname)) != 0) {
    deltakey->af_ldes->af_datasize += VATTR(predptr).af_dsize;

    /* if VATTR(predptr).af_data points to memory allocated by an own
     * af_malloc (and not in af_readdata), this memory segment remains
     * "unfreed" (This is a BUG !).
     */
    if ((VATTR(predptr).af_data = af_malloc (predptr->af_ldes, (unsigned) (VATTR(predptr).af_dsize*sizeof(char)))) == NULL)
      return (ERROR);
    tmpfile = fopen (tmpname, "r");
    fread (VATTR(predptr).af_data, (size_t) VATTR(predptr).af_dsize, sizeof (char), tmpfile);
    fclose (tmpfile);
  }
  else {
    VATTR(predptr).af_data = NULL;
  }
  unlink (tmpname);
  af_unregtmpfile (tmpname);
  free (succdata);
  free (preddata);
  VATTR(predptr).af_succgen = VATTR((&(deltachain[2]))).af_gen;
  VATTR(predptr).af_succrev = VATTR((&(deltachain[2]))).af_rev;
  free ((char *) deltachain);
  return (AF_OK);
}

