/* 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
 *
 * afattrs.c - reading and writing attributes
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afattrs.c[7.0] Fri Jun 25 14:31:41 1993 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

/*===========================================================
 *  af_retattr -- return attribute in string representation
 *===========================================================*/

static char attrBuf[PATH_MAX];

EXPORT int af_isstdval (attrVal)
     char *attrVal;
{
  return (attrBuf == attrVal);
}

EXPORT void af_freeattr (attrVal)
     char *attrVal;
{
  if (attrVal != attrBuf)
    free (attrVal);
}

EXPORT void af_freeattrbuf (attrBuf)
     Af_attrs *attrBuf;
{
  int i=0;
  while (attrBuf->af_udattrs[i])
    free (attrBuf->af_udattrs[i++]);
}

EXPORT char *af_retattr (aso, attr)
     Af_key *aso;
     char   *attr;
{
  char *udattr, *entry, *valptr;

  if (afAccessAso (aso, AF_ATTRS))
    FAIL ("retattr", "", AF_EINVKEY, NULL);

  if (!attr)
    FAIL ("retattr", "no attribute name given", AF_EMISC, NULL);

  attrBuf[0] = '\0';

  if (!strncmp (attr, "af_", 3)) {
    /* presumably a standard attribute name */
    char *aNamPtr = &attr[3];

    if (!afAttrNameCmp (&AF_ATTHOST[3], aNamPtr)) {
      if (CATTR(aso).af_host)
	strcpy (attrBuf, CATTR(aso).af_host);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTSPATH[3], aNamPtr)) {
      if (CATTR(aso).af_syspath)
	strcpy (attrBuf, CATTR(aso).af_syspath);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTUNIXNAME[3], aNamPtr)) {
      strcpy (attrBuf, VATTR(aso).af_name);
      if (VATTR(aso).af_type) {
	strcat (attrBuf, ".");
	strcat (attrBuf, VATTR(aso).af_type);
      }
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTUNIXPATH[3], aNamPtr)) {
      strcpy (attrBuf, af_gbusname (CATTR(aso).af_syspath, VATTR(aso).af_name, VATTR(aso).af_type));
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTBOUND[3], aNamPtr)) {
      char tmpBuf[16];
      strcpy (attrBuf, VATTR(aso).af_name);
      if (VATTR(aso).af_type) {
	strcat (attrBuf, ".");
	strcat (attrBuf, VATTR(aso).af_type);
      }
      if (VATTR(aso).af_gen == AF_BUSYVERS)
	strcat (attrBuf, "[busy]");
      else {
	sprintf (tmpBuf, "[%d.%d]", VATTR(aso).af_gen, VATTR(aso).af_rev);
	strcat (attrBuf, tmpBuf);
      }
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTBOUNDPATH[3], aNamPtr)) {
      char tmpBuf[16];
      strcpy (attrBuf, af_gbusname (CATTR(aso).af_syspath, VATTR(aso).af_name, VATTR(aso).af_type));
      if (VATTR(aso).af_gen == AF_BUSYVERS)
	strcat (attrBuf, "[busy]");
      else {
	sprintf (tmpBuf, "[%d.%d]", VATTR(aso).af_gen, VATTR(aso).af_rev);
	strcat (attrBuf, tmpBuf);
      }
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTNAME[3], aNamPtr)) {
      if (VATTR(aso).af_name)
	strcpy (attrBuf, VATTR(aso).af_name);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTTYPE[3], aNamPtr)) {
      if (VATTR(aso).af_type)
	strcpy (attrBuf, VATTR(aso).af_type);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTGEN[3], aNamPtr)) {
      if (VATTR(aso).af_gen == AF_BUSYVERS)
	strcpy (attrBuf, "busy");
      else
	sprintf (attrBuf, "%d", VATTR(aso).af_gen);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTREV[3], aNamPtr)) {
      if (VATTR(aso).af_rev == AF_BUSYVERS)
	strcpy (attrBuf, "busy");
      else
	sprintf (attrBuf, "%d", VATTR(aso).af_rev);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTVERSION[3], aNamPtr)) {
      if (VATTR(aso).af_gen == AF_BUSYVERS)
	strcpy (attrBuf, "busy");
      else
	sprintf (attrBuf, "%d.%d", VATTR(aso).af_gen, VATTR(aso).af_rev);
      return (attrBuf);
    }

    if (!afAttrNameCmp (&AF_ATTSTATE[3], aNamPtr)) {
      switch (VATTR(aso).af_state) {
      case AF_BUSY:
	strcpy (attrBuf, "busy"); break;
      case AF_SAVED:
	strcpy (attrBuf, "saved"); break;
      case AF_PROPOSED:
	strcpy (attrBuf, "proposed"); break;
      case AF_PUBLISHED:
	strcpy (attrBuf, "published"); break;
      case AF_ACCESSED:
	strcpy (attrBuf, "accessed"); break;
      case AF_FROZEN:
	strcpy (attrBuf, "frozen"); break;
      }
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTOWNER[3], aNamPtr)) {
      sprintf (attrBuf, "%s@%s", CATTR(aso).af_ownname, CATTR(aso).af_owndomain);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTAUTHOR[3], aNamPtr)) {
      sprintf (attrBuf, "%s@%s", VATTR(aso).af_auname, VATTR(aso).af_audomain);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTDSIZE[3], aNamPtr)) {
      if (VATTR(aso).af_repr == AF_DELTA)
	sprintf (attrBuf, "%lu", VATTR(aso).af_dsize);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTSIZE[3], aNamPtr)) {
      sprintf (attrBuf, "%lu", VATTR(aso).af_fsize);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTMODE[3], aNamPtr)) {
      sprintf (attrBuf, "%o", VATTR(aso).af_mode);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTLOCKER[3], aNamPtr)) {
      if (VATTR(aso).af_lckname)
	sprintf (attrBuf, "%s@%s", VATTR(aso).af_lckname, VATTR(aso).af_lckdomain);
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTMTIME[3], aNamPtr)) {
      strcpy (attrBuf, asctime (localtime (&VATTR(aso).af_mtime)));
      attrBuf[strlen(attrBuf)-1] = '\0';
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTATIME[3], aNamPtr)) {
      strcpy (attrBuf, asctime (localtime (&VATTR(aso).af_atime)));
      attrBuf[strlen(attrBuf)-1] = '\0';
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTCTIME[3], aNamPtr)) {
      strcpy (attrBuf, asctime (localtime (&VATTR(aso).af_ctime)));
      attrBuf[strlen(attrBuf)-1] = '\0';
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTSTIME[3], aNamPtr)) {
      if (VATTR(aso).af_stime != AF_NOTIME) {
	strcpy (attrBuf, asctime (localtime (&VATTR(aso).af_stime)));
	attrBuf[strlen(attrBuf)-1] = '\0';
      }
      return (attrBuf);
    }
    
    if (!afAttrNameCmp (&AF_ATTLTIME[3], aNamPtr)) {
      if (VATTR(aso).af_ltime != AF_NOTIME) {
	strcpy (attrBuf, asctime (localtime (&VATTR(aso).af_ltime)));
	attrBuf[strlen(attrBuf)-1] = '\0';
      }
      return (attrBuf);
    }
  }

  /* assume user defined attribute */

  if ((entry = afLookupUda (aso, attr)) == NULL)
    return (NULL);

  if ((valptr = strchr (entry, AF_UDANAMDEL))) {
    if ((udattr = malloc ((unsigned) strlen(valptr) + sizeof(char))) == NULL)
      FAIL ("rudattr", "malloc", AF_ESYSERR, NULL);
    /* replace delimiters by '\n' */
    strcpy (udattr, valptr+1);
    valptr = udattr;
    while ((valptr = strchr (valptr, AF_UDAVALDEL)))
      *valptr = '\n';
    return (udattr);
  }
  return (attrBuf);
}

/*=============================================
 *  af_retnumattr -- return numeric attribute
 *=============================================*/

EXPORT int af_retnumattr (aso, attr)
     Af_key *aso;
     char   *attr;
{
  if (afAccessAso (aso, AF_ATTRS))
    FAIL ("retnumattr", "", AF_EINVKEY, ERROR);

  if (!attr)
    FAIL ("retnumattr", "no attribute name given", AF_EMISC, 0);

  if (!afAttrNameCmp (AF_ATTGEN, attr))
    return (VATTR(aso).af_gen);

  if (!afAttrNameCmp (AF_ATTREV, attr))
    return (VATTR(aso).af_rev);

  if (!afAttrNameCmp (AF_ATTSTATE, attr))
    return (VATTR(aso).af_state);

  if (!afAttrNameCmp (AF_ATTDSIZE, attr)) {
    if (VATTR(aso).af_repr != AF_DELTA)
      return (ERROR);
    return (VATTR(aso).af_dsize);
  }

  if (!afAttrNameCmp (AF_ATTSIZE, attr))
    return (VATTR(aso).af_fsize);

  if (!afAttrNameCmp (AF_ATTMODE, attr))
    return (VATTR(aso).af_mode);

  return (ERROR);
}

/*===========================================
 *  af_retuserattr -- return user attribute
 *===========================================*/

EXPORT Af_user *af_retuserattr (aso, attr)
     Af_key *aso;
     char   *attr;
{
  static Af_user userBuf;

  if (afAccessAso (aso, AF_ATTRS))
    FAIL ("retuserattr", "", AF_EINVKEY, NULL);

  if (!attr)
    FAIL ("retuserattr", "empty attribute name", AF_EMISC, NULL);

  if (!afAttrNameCmp (AF_ATTOWNER, attr)) {
    strcpy (userBuf.af_username, CATTR(aso).af_ownname);
    strcpy (userBuf.af_userhost, CATTR(aso).af_ownhost);
    strcpy (userBuf.af_userdomain, CATTR(aso).af_owndomain);
    return (&userBuf);
  }

  if (!afAttrNameCmp (AF_ATTAUTHOR, attr)) {
    strcpy (userBuf.af_username, VATTR(aso).af_auname);
    strcpy (userBuf.af_userhost, VATTR(aso).af_auhost);
    strcpy (userBuf.af_userdomain, VATTR(aso).af_audomain);
    return (&userBuf);
  }

  if (!afAttrNameCmp (AF_ATTLOCKER, attr)) {
    if (VATTR(aso).af_lckname) {
      strcpy (userBuf.af_username, VATTR(aso).af_lckname);
      strcpy (userBuf.af_userhost, VATTR(aso).af_lckhost);
      strcpy (userBuf.af_userdomain, VATTR(aso).af_lckdomain);
    }
    else {
      userBuf.af_username[0] = '\0';
      userBuf.af_userhost[0] = '\0';
      userBuf.af_userdomain[0] = '\0';
    }
    return (&userBuf);
  }
  return (NULL);
}

/*===========================================
 *  af_rettimeattr -- return time attribute
 *===========================================*/

EXPORT time_t af_rettimeattr (aso, attr)
     Af_key *aso;
     char   *attr;
{
  if (afAccessAso (aso, AF_ATTRS))
    FAIL ("rettimeattr", "", AF_EINVKEY, AF_NOTIME);

  if (!attr)
    FAIL ("rettimeattr", "empty attribute name", AF_EMISC, 0);

  if (!afAttrNameCmp (AF_ATTMTIME, attr))
    return (VATTR(aso).af_mtime);

  if (!afAttrNameCmp (AF_ATTATIME, attr))
    return (VATTR(aso).af_atime);

  if (!afAttrNameCmp (AF_ATTCTIME, attr))
    return (VATTR(aso).af_ctime);

  if (!afAttrNameCmp (AF_ATTSTIME, attr))
    return (VATTR(aso).af_stime);

  if (!afAttrNameCmp (AF_ATTLTIME, attr))
    return (VATTR(aso).af_ltime);

  return (AF_NOTIME);
}

/*===============================================
 *  af_setattr -- set user defined attribute
 *===============================================*/

EXPORT int af_setattr (key, mode, attr)
     Af_key *key;
     int    mode;
     char   *attr;
{
  register char *udaptr, *tmpuda, *valptr;
  register int  tail;

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

  if (!attr)
    FAIL ("setattr", "empty attribute name", AF_EMISC, 0);

  if ((VATTR(key).af_state == AF_FROZEN) && (mode == AF_REMOVE))
    FAIL ("setattr", "", AF_EWRONGSTATE, ERROR);

  /* look for delimiter character in attribute string */
  if (!attr || strchr (attr, AF_UDAVALDEL))
    FAIL ("setattr", "illegal format of attribute string", AF_EMISC, ERROR);

  if (af_checkperm (key, AF_WORLD) == ERROR)
    return (ERROR);

  /* search entry */
  udaptr = afLookupUda (key, attr); 

  if (!udaptr && (mode == AF_REPLACE))
    mode = AF_ADD;

  switch (mode) {
  case AF_ADD:
    if (udaptr) {
      /* build new entry and replace old one */
      if (!(valptr = strchr (attr, AF_UDANAMDEL))) {
	/* no new value -- nothing to do */
	break;
      }
      valptr += sizeof (char);
      if ((tmpuda = malloc ((unsigned) ((strlen (udaptr) + strlen (valptr) +2) * sizeof (char)))) == NULL)
	FAIL ("setattr", "malloc", AF_ESYSERR, ERROR);

      strcpy (tmpuda, udaptr);
      tail = strlen (tmpuda);
      tmpuda[tail] = AF_UDAVALDEL;
      tmpuda[tail+1] = '\0';
      strcat (tmpuda, valptr);
      afReplUda (key, tmpuda);
      free (tmpuda);
    }    
    else {
      /* add new entry */
      if (VATTR(key).af_udanum == AF_MAXUDAS)
	FAIL ("setattr", "too many user defined attributes", AF_EMISC, ERROR);
      afEnterUda (key, attr);
    }
    break;
  case AF_REMOVE:
    if (udaptr == NULL)
      FAIL ("setattr", "", AF_ENOUDA, ERROR);
    afDelUda (key, udaptr);
    break;
  case AF_REPLACE:
    if (udaptr == NULL)
      FAIL ("setattr", "", AF_ENOUDA, ERROR);
    afReplUda (key, attr);
    break;
  default: FAIL ("setattr", "", AF_EMODE, ERROR);
  }

  if (afUpdateAso (key, AF_CHANGE) == ERROR)
    return (ERROR);
  return (AF_OK);
}

/*=========================================
 *  af_allattrs -- return all attributes
 *=========================================*/

EXPORT int af_allattrs (key, attrbuf)
     Af_key   *key;
     Af_attrs *attrbuf;
{
  register int  i;
  register char *valptr;
  char          *udalist[AF_MAXUDAS+1];

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

  strcpy (attrbuf->af_host, CATTR(key).af_host);
  strcpy (attrbuf->af_name, VATTR(key).af_name);
  strcpy (attrbuf->af_type, NOTNIL(VATTR(key).af_type));
  strcpy (attrbuf->af_syspath, CATTR(key).af_syspath);

  attrbuf->af_gen = VATTR(key).af_gen;
  attrbuf->af_rev = VATTR(key).af_rev;
  attrbuf->af_state = (int) VATTR(key).af_state;
  strcpy (attrbuf->af_owner.af_username, CATTR(key).af_ownname);
  strcpy (attrbuf->af_owner.af_userhost, CATTR(key).af_ownhost);
  strcpy (attrbuf->af_owner.af_userdomain, CATTR(key).af_owndomain);
  strcpy (attrbuf->af_author.af_username, VATTR(key).af_auname);
  strcpy (attrbuf->af_author.af_userhost, VATTR(key).af_auhost);
  strcpy (attrbuf->af_author.af_userdomain, VATTR(key).af_audomain);
  attrbuf->af_size = VATTR(key).af_fsize;
  attrbuf->af_mode = VATTR(key).af_mode;
  strcpy (attrbuf->af_locker.af_username, NOTNIL(VATTR(key).af_lckname));
  strcpy (attrbuf->af_locker.af_userhost, NOTNIL(VATTR(key).af_lckhost));
  strcpy (attrbuf->af_locker.af_userdomain, NOTNIL(VATTR(key).af_lckdomain));
  attrbuf->af_mtime = VATTR(key).af_mtime;
  attrbuf->af_atime = VATTR(key).af_atime;
  attrbuf->af_ctime = VATTR(key).af_ctime;
  attrbuf->af_stime = VATTR(key).af_stime;
  attrbuf->af_ltime = VATTR(key).af_ltime;

  /* copy user defined attributes */
  if (VATTR(key).af_udanum > 0) {
    afListUdas (key, udalist);
    i=0;
    while (udalist[i]) {
      if ((attrbuf->af_udattrs[i] = malloc (strlen(udalist[i]) + sizeof(char))) == NULL)
	FAIL ("allattrs", "malloc", AF_ESYSERR, ERROR);
      strcpy (attrbuf->af_udattrs[i], udalist[i]);
      /* replace delimiters by '\n' */
      if ((valptr = strchr (attrbuf->af_udattrs[i], AF_UDANAMDEL))) {
	while ((valptr = strchr (valptr, AF_UDAVALDEL)))
	  valptr[0] = '\n';
      }
      i++;
    }
    attrbuf->af_udattrs[i] = NULL; /* finish list */
  }
  else
    attrbuf->af_udattrs[0] = NULL;

  return (AF_OK);
}
