/* 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
 *
 * afstore.c -- interface to archives and derived object caches
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afstore.c[7.1] Thu Aug  4 16:02:58 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

/*================================================================
 * afAccessAso -- prepare ASO for access (read Data if necessary)
 *================================================================*/

EXPORT int afAccessAso (key, mode)
     Af_key *key;
     int    mode;
{
  if (key == NULL)
    return (ERROR);
  if (key->af_ldes == NULL)
    return (ERROR);
  if ((key->af_lpos < 0) || (key->af_lpos >= key->af_ldes->af_listlen))
    return (ERROR);

  if (mode == 0)
    return (AF_OK);

  return (afRefreshList (key->af_ldes, mode));

}

/*====================================
 * afUpdateAso -- Update existing ASO
 *====================================*/

extern int afCurTransaction;

EXPORT int afUpdateAso (key, mode)
     Af_key *key;
     int    mode;
{
  int retVal;
  /* if (mode == AF_ALLVERS | AF_CHANGE) */
  /* update time of last status change (ctime) for all versions */
  /* not yet implemented (update ctime) */

  if (mode & AF_CHANGE) /* update ctime only for this version */
    VATTR(key).af_ctime = af_acttime ();

  if (afCurTransaction)
    return (afAddToTransactionList (key->af_ldes));

  retVal = AF_OK;
  if (VATTR(key).af_state == AF_DERIVED) 
    retVal = afObjCacheWrite (key->af_ldes);
  else
    retVal = afWriteArchive (key->af_ldes);
  return (retVal);
}

/*==============================
 * afNewAso -- generate new ASO
 *==============================*/

EXPORT int afNewAso (list, key, mode, generation)
     Af_revlist *list;
     Af_key     *key;
     int        mode;
     int        generation;
{
  int freepos, insertpos, i;

  if (mode != AF_CLASS_SOURCE)
    FAIL ("newAso", "invalid mode", AF_EINTERNAL, ERROR);

  key->af_ldes = list;

  /* get data from archive */
  if (afRefreshList (list, AF_DATA) == ERROR)
    return (ERROR);

  if ((freepos = af_gfreepos (list)) == ERROR)
    FAIL ("newAso", "too many new revisions", AF_EINTERNAL, ERROR);

  if (generation == AF_LASTVERS)
    key->af_lpos = freepos;
  else {
    /* create a hole in the list */
    if ((insertpos = af_glastgenpos (list, generation)) == ERROR)
      insertpos = freepos;
    insertpos++;
    for (i=freepos; i>insertpos; i--)
      if (list->af_list[i-1].af_nrefs > 0)
	FAIL ("newAso", "cannot create hole in history (reference count not zero)", AF_EMISC, ERROR);
    for (i=freepos; i>insertpos; i--)
      list->af_list[i] = list->af_list[i-1];
    key->af_lpos = insertpos;
  }

  VATTR(key).af_nrefs = 0;
  afIncreaseAccess (key);

  return (AF_OK);
}


/*=====================
 * afAddAso -- add ASO
 *=====================*/

EXPORT int afAddAso (key)
     Af_key *key;
{
  /* cannot be applied to derived object caches */
  return (afWriteArchive (key->af_ldes));
}

/*========================================
 * afDeleteAso -- delete ASO from archive
 *========================================*/

EXPORT int afDeleteAso (key)
     Af_key *key;
{
  int retVal;

  /* if key points to a file in a derived object cache */
  switch (VATTR(key).af_state) {
  case AF_DERIVED:
    if (unlink (afCacheFileName (key->af_ldes->af_arpath, VATTR(key).af_hashname)) == -1)
      FAIL ("DeleteAso", "unlink", AF_ESYSERR, ERROR);
    break;
  case AF_BUSY:
    /* try to remove busy file */
    if (unlink (af_gbusname (CATTR(key).af_syspath, VATTR(key).af_name, VATTR(key).af_type)) == -1)
      FAIL ("DeleteAso", "unlink", AF_ESYSERR, ERROR);
    VATTR(key).af_predgen = AF_NOVNUM;
    VATTR(key).af_predrev = AF_NOVNUM;
    VATTR(key).af_lckname = NULL;
    VATTR(key).af_lckhost = NULL;
    VATTR(key).af_lckdomain = NULL;
    VATTR(key).af_ltime = AF_NOTIME;
    break;
  default:
    /* remove delta */
    /* read data section of archive */
    if (afRefreshList (key->af_ldes, AF_DATA) == ERROR)
      return (ERROR);

    key->af_ldes->af_datasize -= VATTR(key).af_notesize;
    if (VATTR(key).af_repr == AF_DELTA)
      key->af_ldes->af_datasize -= VATTR(key).af_dsize;
    else
      key->af_ldes->af_datasize -= VATTR(key).af_fsize;

    if (af_rmdelta (key) != AF_OK)
      return (ERROR);
  }
  afDropUdas (key);
  /* clear "valid" bit and decrease refcount for corresponding archive */
  VATTR(key).af_class &= ~AF_VALID;
  key->af_ldes->af_access -= VATTR(key).af_nrefs;
  VATTR(key).af_nrefs = 0;

  key->af_ldes->af_nrevs--;

  retVal = AF_OK;
  if (VATTR(key).af_state == AF_DERIVED) 
    retVal = afObjCacheWrite (key->af_ldes);
  else
    retVal = afWriteArchive (key->af_ldes);
  return (retVal);
}

/*=============================
 * afGetAso -- get ASO by name
 *=============================*/

EXPORT int afGetAso (syspath, name, type, gen, rev, key)
     char   *syspath, *name, *type;
     int    gen, rev;
     Af_key *key;
     /* syspath must be a canonical filename */
{
  register Af_revlist *list;
  register char       *pathsym, *namesym, *typesym;
  Af_key              *keyptr;

  /* build pathname (default is current directory) */
  pathsym = af_entersym (syspath);
  namesym = af_entersym (name);
  typesym = af_entersym (type);

  if ((list = afInitList (pathsym, namesym, typesym)) == NULL)
    FAIL ("getkey", "", AF_ENOREV, ERROR);

  key->af_ldes = list;
 
  /* handle special cases */
  if ((gen < 0) || (rev < 0)) {
    switch (rev) {
    case AF_BUSYVERS:
      if (af_buildkey (list, gen, rev, key) == ERROR)
	FAIL ("getkey", "", AF_ENOREV, ERROR);
      break;
    case AF_LASTVERS:
      if (gen == AF_LASTVERS) {
	if ((keyptr = af_glastkey (list)) == NULL)
	  FAIL ("getkey", "", AF_ENOREV, ERROR);
	key->af_lpos = keyptr->af_lpos;
      }
      else {
	if ((keyptr = af_glastgenkey (list, gen)) == NULL)
	  FAIL ("getkey", "", AF_ENOREV, ERROR);
	key->af_lpos = keyptr->af_lpos;
      }
      break;
    case AF_FIRSTVERS:
      if (list->af_nrevs == 0)
	FAIL ("getkey", "", AF_ENOREV, ERROR);
      if (gen == AF_FIRSTVERS) {
	key->af_lpos = 0;
	while ((VATTR(key).af_state == AF_BUSY) || (!(VATTR(key).af_class & AF_VALID))) {
	  if (key->af_lpos++ == list->af_listlen-1)
	    FAIL ("getkey", "", AF_ENOREV, ERROR);
	}
      }
      else {  
	key->af_lpos = 1;
	while ((!(VATTR(key).af_class & AF_VALID)) || (VATTR(key).af_gen != gen)) {
	  if (key->af_lpos++ == list->af_listlen-1)
	    FAIL ("getkey", "", AF_ENOREV, ERROR);
	}
      }
      break;
    default: FAIL ("getkey", "", AF_ENOREV, ERROR);
    }
  }
  else {
    if (af_buildkey (list, gen, rev, key) == ERROR)
      FAIL ("getkey", "", AF_ENOREV, ERROR);
  }

  afIncreaseAccess (key);
  return (AF_OK);
}

/*==============================================
 * afTestAso -- test existence of ASO (by name)
 *==============================================*/

EXPORT int afTestAso (path, name, type, mode)
     char *path, *name, *type;
     int  mode;
{
  register Af_revlist *list;
  register int        i, maxindex;
  char     *pathSym = af_entersym (path);
  char     *nameSym = af_entersym (name);
  char     *typeSym = af_entersym (type);

  if (mode & AF_CLASS_DERIVED) { /* look in derived object cache */
    if ((list = afInitObjCache (pathSym)) == NULL)
      return (ERROR);
      
    maxindex = list->af_nrevs-1;
    for (i = 0; i <= maxindex; i++) {
      /* skip holes in the list */
      if (!(list->af_list[i].af_class & AF_VALID)) {
	if (++maxindex == list->af_listlen)
	  FATAL ("TestAso", "bad revision count", AF_EINCONSIST, ERROR);
	continue;
      }
      if ((nameSym == list->af_list[i].af_name) && (typeSym == list->af_list[i].af_type))
	return (AF_OK);
    }
  }
  else { /* look in directory */
    if ((list = afTestList (pathSym, nameSym, typeSym)) && list->af_nrevs > 0)
      return (AF_OK);
    if ((list = afInitList (pathSym, nameSym, typeSym)) && list->af_nrevs > 0)
      return (AF_OK);
  }
  return (ERROR);
}

/*============================================================
 * afBuildFile -- write content data of ASO to temporary file
 *============================================================*/

EXPORT int afBuildFile (key, name)
     Af_key *key;
     char   *name;
{
  struct utimbuf utbuf;

  if (VATTR(key).af_state == AF_DERIVED) {
    /* get file from derived object cache */
    if (afObjCacheRestore (key, name) == ERROR)
      return (ERROR);
  }
  else {
    /* get data from archive file */
    if (afRefreshList (key->af_ldes, AF_DATA) == ERROR)
      return (ERROR);
      
    if (af_undodelta (key, name) == ERROR)
      return (ERROR);
  }

  /*** set modification and access date ***/
  utbuf.actime = VATTR(key).af_atime;
  utbuf.modtime = VATTR(key).af_mtime;
  if (utime (name, &utbuf) == ERROR)
    af_wng ("BuildFile", "cannot set UNIX access and modification date");
  chmod (name, (int) VATTR(key).af_mode);
  return (AF_OK);
}
