/* 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
 *
 * affiles.c -- UNIX-files in AtFS
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: affiles.c[7.1] Thu Aug  4 16:02:54 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

extern int af_errno;

/*================================================================
 *	af_access -- see if any version of named file exists
 *================================================================*/

EXPORT int af_access (path, name, type, mode)
     char *path, *name, *type;
     int  mode;
{
  char *uniqPath = af_uniqpath (af_entersym(path));
  
  /* empty name argument */
  if (!name || !(*name)) {
    if (type && *type)
      FAIL ("access", "cannot handle empty name and non-empty type", AF_EMISC, ERROR);
    name = ".";
  }

  /* too much hassle to handle this case */
  if (!strcmp (name, "..") && (!type || !(*type)))
    FAIL ("access", "cannot handle name '..'", AF_EMISC, ERROR);

  /* when referring the current directory, busyname is just the pathname */
  if (!strcmp (name, ".") && (!type || !(*type)))
    return (afTestAso (af_afpath (uniqPath), af_afname (uniqPath), af_aftype (uniqPath), mode));
  else
    return (afTestAso (uniqPath, name, type, mode));
}

/*================================================================
 *	af_crkey
 *================================================================*/

EXPORT int af_crkey (path, name, type, key)
     char *path;
     char *name, *type;
     Af_key *key;
{
  char *busyname, *uniqpath = af_uniqpath (af_entersym(path));
  FILE *busyfile;
  struct stat bibuf;

  /* empty name argument */
  if (!name || !(*name)) {
    name = ".";
    if (type && *type)
      FAIL ("crkey", "cannot handle empty name and non-empty type", AF_EMISC, ERROR);
    name = ".";
  }

  /* too much hassle to handle this case */
  if (!strcmp (name, "..") && (!type || !(*type)))
    FAIL ("crkey", "cannot handle name '..'", AF_EMISC, ERROR);

  /* when referring the current directory, busyname is just the pathname */
  if (!strcmp (name, ".") && (!type || !(*type))) {
    busyname = uniqpath;
    uniqpath = af_afpath (busyname);
    name = af_afname (busyname);
    type = af_aftype (busyname);
  }
  else
    busyname = af_gbusname (uniqpath, name, type);
  
  /* if file does not exist -- create it */
  if ((stat (busyname, &bibuf)) == ERROR) {
    if ((busyfile = fopen (busyname, "w")) == NULL)
      FAIL ("crkey", "fopen", AF_ESYSERR, ERROR);
    fclose (busyfile);
    stat (busyname, &bibuf);
  }

  /* select busy version if present */
  if (afGetAso (uniqpath, name, type, AF_BUSYVERS, AF_BUSYVERS, key) == ERROR)
    return (ERROR);

  return (AF_OK);
}

/*================================================================
 *	af_open
 *================================================================*/

EXPORT FILE *af_open (key, mode)
     Af_key *key;
     char   *mode;
{
  FILE *file;
  char *tmpname;
  
  if (afAccessAso (key, AF_ATTRS))
    FAIL ("open", "", AF_EINVKEY, NULL);
  
  /* if file is present as busy version */
  if (VATTR(key).af_state == AF_BUSY) {
    if ((file = fopen (key->af_ldes->af_busyfilename, mode)) == NULL)
      FAIL ("open", "fopen", AF_ESYSERR, NULL);
    VATTR(key).af_atime = af_acttime ();
    return (file);
  }
  
  /* saved versions can be opened only for reading */
  if (mode[0] != 'r')
    FAIL ("open", "", AF_ESAVED, NULL);
  
  /* see if file is readable */
  if (af_checkread (key) == ERROR)
    FAIL ("open", "cannot read", AF_EACCES, NULL);

  /* build name for temporary file */
  tmpname = af_gtmpname ("/tmp");
  af_regtmpfile (tmpname);

  if (afBuildFile (key, tmpname) == ERROR)
    return (NULL);

  if ((file = fopen (tmpname, mode)) == NULL)
    FAIL ("open", "fopen", AF_ESYSERR, NULL);

  unlink (tmpname); /* this causes the tmp file to be removed on closing */
  af_unregtmpfile (tmpname);

  /* do *not* set access date for sake of efficiency
   * VATTR(key).af_atime = af_acttime ();
   *  * possibly the date of last access is *not* set properly
   *  * instead of
   * afUpdateAso (key, 0);
   *  * this should be
   *  * if (afUpdateAso (key, 0) == ERROR)
   *  *    return (NULL);
   */
  return (file);
}

/*================================================================
 *	af_establish
 *================================================================*/

EXPORT int af_establish (key, fileName)
     Af_key *key;
     char   *fileName;
{
  if (afAccessAso (key, AF_ATTRS))
    FAIL ("establish", "", AF_EINVKEY, ERROR);
  
  /* see if file is readable */
  if (af_checkread (key) == ERROR)
    FAIL ("establish", "cannot read", AF_EACCES, ERROR);

  /* if file is present as busy version */
  if (VATTR(key).af_state == AF_BUSY) {
    if (af_cpfile (key->af_ldes->af_busyfilename, VATTR(key).af_fsize, fileName) == ERROR)
    return (ERROR);
  }
  
  if (afBuildFile (key, fileName) == ERROR)
    return (ERROR);

  /* do *not* set access date for sake of efficiency
   * VATTR(key).af_atime = af_acttime ();
   * * possibly the date of last access is *not* set properly
   * * instead of
   * afUpdateAso (key, 0);
   * * this should be
   * * if (afUpdateAso (key, 0) == ERROR)
   * *    return (NULL);
   */
  return (AF_OK);
}


/*================================================================
 *	af_close
 *================================================================*/

EXPORT int af_close (file)
     FILE *file;
{
  return (fclose (file));
}

/*================================================================
 *	af_rm
 *================================================================*/

EXPORT int af_rm (key)
     Af_key *key;
{
  if (afAccessAso (key, AF_ATTRS))
    FAIL ("rm", "", AF_EINVKEY, ERROR);

  /* if object is not locked (derived objects need not to be locked) */
  if (VATTR(key).af_state != AF_DERIVED) {
    if (af_checkperm (key, AF_LOCKHOLDER) == ERROR)
      FAIL ("rm", "", AF_ENOTLOCKED, ERROR);
  }

  /*
   * if (VATTR(key).af_nrefs > 1)
   *   af_wng ("rm", "deleted object has more than one reference");
   */

  if (afDeleteAso (key) == ERROR)
    return (ERROR);

  return (AF_OK);
}

/*================================================================
 *	af_restore
 *================================================================*/

EXPORT int af_restore (key, restkey)
     Af_key *key, *restkey;
{
  char *busyname;

  if (afAccessAso (key, AF_ATTRS))
    FAIL ("restore", "", AF_EINVKEY, ERROR);

  if (VATTR(key).af_state != AF_DERIVED)
    FAIL ("restore", "", AF_ENOTDERIVED, ERROR);

  /* see if file is readable */
  if (af_checkread (key) == ERROR)
    FAIL ("restore", "cannot read cached file", AF_EACCES, ERROR);

  busyname = af_gbusname (CATTR(key).af_syspath, VATTR(key).af_name, VATTR(key).af_type);
  if (afBuildFile (key, busyname) == ERROR)
    return (ERROR);

  /* build key for restored file */
  if (afGetAso (CATTR(key).af_syspath, VATTR(key).af_name, VATTR(key).af_type,
		AF_BUSYVERS, AF_BUSYVERS, restkey) == ERROR)
    FATAL ("restore", "cannot access restored file", AF_EINTERNAL, ERROR);

  /* restore user defined attributes from binary pool */
  afDropUdas (restkey);
  afInitUdas (restkey);
  afCopyUdas (key, restkey);

  /* do *not* set access date for sake of efficiency
   * VATTR(key).af_atime = af_acttime ();
   * * possibly the date of last access is *not* set properly
   * * instead of
   * afUpdateAso (key, 0);
   * * this should be
   * * if (afUpdateAso (key, 0) == ERROR)
   * *    return (NULL);
   */
  afUpdateAso (restkey, 0);
  
  return (AF_OK);
}


