/* 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
 *
 * afsets.c -- Operations on keysets
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afsets.c[7.0] Fri Dec 17 17:00:04 1993 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

/*================================================================
 *	af_settest -- test plausibility of set descriptor
 *                    returnes TRUE (ERROR) if set descr. is invalid,
 *                    otherwise FALSE (AF_OK).
 *
 *================================================================*/

LOCAL int af_settest (set)
     Af_set *set;
{
  if ((set->af_nkeys < 0) || (set->af_setlen < 0))
    return (ERROR);
  /* if set is not empty, test if first key is valid */
  if (set->af_nkeys >= 1)
    if (afAccessAso (&(set->af_klist[0]), 0))
      return (ERROR);

  return (AF_OK);
}

/*================================================================
 *	af_initset -- initialize set descriptor
 *================================================================*/

EXPORT void af_initset (set)
     Af_set *set;
{
  set->af_nkeys = 0;
  set->af_setlen = 0;
  set->af_klist = NULL;
}

/*================================================================
 *	af_copyset -- copy sets
 *================================================================*/

EXPORT int af_copyset (set1, set2)
     Af_set *set1, *set2;
{
  register int i;

  if (af_settest (set1))
    FAIL ("copyset", "", AF_EINVSET, ERROR);

  /* allocate memory for new set (incl. space for additional element) */
  set2->af_setlen = set1->af_nkeys + 1;
  if ((set2->af_klist = (Af_key *)malloc ((unsigned) (set2->af_setlen * sizeof(Af_key)))) == NULL)
    FAIL ("copyset", "malloc", AF_ESYSERR, ERROR);

  /* do copying */
  for (i=0; i<set1->af_nkeys; i++) {
    set2->af_klist[i] = set1->af_klist[i];
    afIncreaseAccess (&(set2->af_klist[i]));
  }
  
  set2->af_nkeys = set1->af_nkeys;
  return (set2->af_nkeys);
}

/*================================================================
 *	af_nrofkeys -- return number of keys in set
 *================================================================*/

EXPORT int af_nrofkeys (set)
     Af_set *set;
{
  if (af_settest (set))
    FAIL ("nrofkeys", "", AF_EINVSET, ERROR);
  return (set->af_nkeys);
}

/*================================================================
 *	af_sortset -- sort set by attribute
 *================================================================*/

struct cmpfun { int (*func) (); };

extern struct cmpfun af_cmpfuncts[];

extern char *af_attrlist[], af_udaname[];

EXPORT int af_sortset (set, attr)
     Af_set *set;
     char   *attr;
{
  register int hi = AF_ATTNUM-1, lo=0, anum, res=0;

  if (af_settest (set))
    FAIL ("sortset", "", AF_EINVSET, ERROR);
  if (!attr)
    FAIL ("sortset", "invalid attribute name", AF_EMISC, ERROR);

  /* get attribute number (binary search) */
  anum = (hi+lo)/2;
  while (hi >= lo) {
    res = strcmp (attr, af_attrlist[anum]);
    if (res == 0)
      break;
    if (res < 0)
      hi = anum - 1;
    else lo = anum + 1;
    anum = (hi+lo)/2;
  }
  /* attribute names, that are not in the list of standard attributes */
  /* are assumed to be "user defined attributes" */
  if (res != 0) {
    strcpy (af_udaname, attr);
    qsort ((char *)set->af_klist, set->af_nkeys, sizeof(Af_key), af_cmpuda); 
  }
  else {
    strcpy (af_udaname, attr); 
    qsort ((char *)set->af_klist, set->af_nkeys, sizeof(Af_key), af_cmpfuncts[anum].func); 
  }
  return (AF_OK);
}

/*================================================================
 *	af_dropset -- drop set
 *================================================================*/

EXPORT int af_dropset (set)
     Af_set *set;
{
  register int i;

  if (af_settest (set))
    FAIL ("dropset", "", AF_EINVSET, ERROR);

  for (i=0; i<set->af_nkeys; i++) {
    /* decrease reference count in corresponding archive */
    afDecreaseAccess (&(set->af_klist[i]));
  }
  
  set->af_nkeys = 0;
  if (set->af_setlen > 0) {
    free ((char *)set->af_klist);
    set->af_setlen = 0;
  }
  return (AF_OK);
}

/*================================================================
 *	af_setgkey -- return key from set
 *================================================================*/

EXPORT int af_setgkey (set, pos, key)
     Af_set *set;
     int    pos;
     Af_key *key; /* out */
{
  if (af_settest (set))
    FAIL ("setgkey", "", AF_EINVSET, ERROR);

  if ((pos >= set->af_nkeys) || (pos < 0))
    FAIL ("setgkey", "", AF_ENOPOS, ERROR);

  afIncreaseAccess (&(set->af_klist[pos]));
  key->af_ldes = set->af_klist[pos].af_ldes;
  key->af_lpos = set->af_klist[pos].af_lpos;
  return (AF_OK);
}

/*================================================================
 *	af_setaddkey -- add key to set
 *================================================================*/

EXPORT int af_setaddkey (set, pos, key)
     Af_set *set;
     int    pos;
     Af_key *key;
{
  register int i;

  if (af_settest (set))
    FAIL ("setaddkey", "", AF_EINVSET, ERROR);
  if (afAccessAso (key, 0))
    FAIL ("setaddkey", "", AF_EINVKEY, ERROR);
  if (((pos > set->af_nkeys) || (pos < 0)) && (pos != AF_LASTPOS))
    FAIL ("setaddkey", "", AF_ENOPOS, ERROR);

  if (pos == AF_LASTPOS)
    pos = set->af_nkeys;

  /* if set is full, enlarge it */
  if (set->af_nkeys == set->af_setlen) {
    if (set->af_setlen == 0) {
      if ((set->af_klist = (Af_key *)malloc ((unsigned) (sizeof(Af_key) * AF_SEGLEN))) == NULL)
	FAIL ("setaddkey", "malloc", AF_ESYSERR, ERROR);
    }
    else {
      if ((set->af_klist = (Af_key *)realloc ((char *)set->af_klist, (unsigned) (sizeof(Af_key) * (set->af_setlen + AF_SEGLEN)))) == NULL)
	FAIL ("setaddkey", "realloc", AF_ESYSERR, ERROR);
    }
    set->af_setlen += AF_SEGLEN;
  }
  
  for ( i=set->af_nkeys; i>pos; i--)
    set->af_klist[i] = set->af_klist[i-1];
  set->af_klist[pos] = *key;
  set->af_nkeys++;

  afIncreaseAccess (key);
  return (AF_OK);
}

/*================================================================
 *	af_setrmkey -- remove key from set
 *================================================================*/

EXPORT int af_setrmkey (set, key)
     Af_set *set;
     Af_key *key;
{
  register int i;
  bool     found = FALSE;

  if (af_settest (set))
    FAIL ("setrmkey", "", AF_EINVSET, ERROR);
  if (afAccessAso (key, 0))
    FAIL ("setrmkey", "", AF_EINVKEY, ERROR);

  for (i=0; i<set->af_nkeys; i++) {
    if (!found) {
      if (!af_keycmp (&(set->af_klist[i]), key))
	found = TRUE;
    }
    else /* compress list */
      set->af_klist[i-1] = set->af_klist[i];
  }
  if (!found)
    FAIL ("setrmkey", "", AF_ENOKEY, ERROR);
  set->af_nkeys--;
  afDecreaseAccess (key);
  return (AF_OK);
}

/*================================================================
 *	af_setposrmkey -- remove key (identified by position) from set
 *================================================================*/

EXPORT int af_setposrmkey (set, pos)
     Af_set *set;
     int    pos;
{
  register int i;

  if (af_settest (set))
    FAIL ("setposrmkey", "", AF_EINVSET, ERROR);
  if ((pos >= set->af_nkeys) || (pos < 0))
    FAIL ("setposrmkey", "", AF_ENOPOS, ERROR);

  afDecreaseAccess (&(set->af_klist[pos]));

  for (i=pos; i<set->af_nkeys-1; i++)
    set->af_klist[i] = set->af_klist[i+1];

  set->af_nkeys--;

  return (AF_OK);
}

/*================================================================
 *	af_subset -- build subset
 *================================================================*/

EXPORT int af_subset (set, attrbuf, newset)
     Af_set   *set, *newset;
     Af_attrs *attrbuf;
{
  register int i, j=0, k=0;
  int match;
  Af_set tmpset;

  if (af_settest (set))
    FAIL ("subset", "", AF_EINVSET, ERROR);

  /* allocate memory for new set */
  if ((tmpset.af_setlen = set->af_nkeys)) {
    if ((tmpset.af_klist = (Af_key *)malloc ((unsigned) (tmpset.af_setlen * sizeof(Af_key)))) == NULL)
      FAIL ("subset", "malloc", AF_ESYSERR, ERROR);
  }
  else
    tmpset.af_klist = NULL;

  /* if there are no disqualifying attributes, add key to new set */
  for (i = 0; i < set->af_nkeys; i++) {
    if (afAccessAso (&(set->af_klist[i]), AF_ATTRS))
      FAIL ("subset", "", AF_EINVKEY, ERROR);
    /* if an attribute is set an does not match, continue with next key */
    /*** name ***/
    if ( strcmp (attrbuf->af_name, AF_PATTERN_ALL) &&
	strcmp (attrbuf->af_name, VATTR((&(set->af_klist[i]))).af_name) )
      continue;
    
    /*** type ***/
    if ( strcmp (attrbuf->af_type, AF_PATTERN_ALL) &&
	strcmp (attrbuf->af_type, NOTNIL(VATTR((&(set->af_klist[i]))).af_type)) )
      continue;
    
    /*** syspath ***/
    if ( attrbuf->af_syspath[0] &&
	strcmp (attrbuf->af_syspath, CATTR((&(set->af_klist[i]))).af_syspath) )
      continue;

    /*** generation number ***/
    if ( (attrbuf->af_gen != AF_NOVNUM) &&
	(attrbuf->af_gen != VATTR((&(set->af_klist[i]))).af_gen) )
      continue;
  
    /*** revision number ***/
    if ( (attrbuf->af_rev != AF_NOVNUM) &&
	(attrbuf->af_rev != VATTR((&(set->af_klist[i]))).af_rev) )
      continue;

    /*** state ***/
    if ( (attrbuf->af_state != AF_NOSTATE) &&
	(attrbuf->af_state != VATTR((&(set->af_klist[i]))).af_state) )
      continue;

    /*** owner ***/
    if ( (attrbuf->af_owner.af_username[0]) &&
	(strcmp (attrbuf->af_owner.af_username, CATTR((&(set->af_klist[i]))).af_ownname)) )
      continue;
    if ( (attrbuf->af_owner.af_userhost[0]) &&
	(strcmp (attrbuf->af_owner.af_userhost, CATTR((&(set->af_klist[i]))).af_ownhost)) )
      continue;
    if ( (attrbuf->af_owner.af_userdomain[0]) &&
	(strcmp (attrbuf->af_owner.af_userdomain, CATTR((&(set->af_klist[i]))).af_owndomain)) )
      continue;
    
    /*** author ***/
    if ( (attrbuf->af_author.af_username[0]) &&
	(strcmp (attrbuf->af_author.af_username, VATTR((&(set->af_klist[i]))).af_auname)) )
      continue;
    if ( (attrbuf->af_author.af_userhost[0]) &&
	(strcmp (attrbuf->af_author.af_userhost, VATTR((&(set->af_klist[i]))).af_auhost)) )
      continue;
    if ( (attrbuf->af_author.af_userdomain[0]) &&
	(strcmp (attrbuf->af_author.af_userdomain, VATTR((&(set->af_klist[i]))).af_audomain)) )
      continue;

    /*** size ***/
    if ( (attrbuf->af_size != AF_NOSIZE) &&
	(attrbuf->af_size != VATTR((&(set->af_klist[i]))).af_fsize) )
      continue;

    /*** mode ***/
    if ( (attrbuf->af_mode != AF_NOMODE) &&
	(attrbuf->af_mode != VATTR((&(set->af_klist[i]))).af_mode) )
      continue;

    /*** locker ***/
    if ( (attrbuf->af_locker.af_username[0]) &&
	(strcmp (attrbuf->af_locker.af_username, NOTNIL(VATTR((&(set->af_klist[i]))).af_lckname))) )
      continue;
    if ( (attrbuf->af_locker.af_userhost[0]) &&
	(strcmp (attrbuf->af_locker.af_userhost, NOTNIL(VATTR((&(set->af_klist[i]))).af_lckhost))) )
      continue;
    if ( (attrbuf->af_locker.af_userdomain[0]) &&
	(strcmp (attrbuf->af_locker.af_userdomain, NOTNIL(VATTR((&(set->af_klist[i]))).af_lckdomain))) )
      continue;

    /*** date of last modification ***/
    if ( (attrbuf->af_mtime != AF_NOTIME) &&
	(attrbuf->af_mtime != VATTR((&(set->af_klist[i]))).af_mtime) )
      continue;
    
    /*** date of last access ***/
    if ( (attrbuf->af_atime != AF_NOTIME) &&
	(attrbuf->af_atime != VATTR((&(set->af_klist[i]))).af_atime) )
      continue;

    /*** date of last status change ***/
    if ( (attrbuf->af_ctime != AF_NOTIME) &&
	(attrbuf->af_ctime != VATTR((&(set->af_klist[i]))).af_ctime) )
      continue;

    /*** saving date ***/
    if ( (attrbuf->af_stime != AF_NOTIME) &&
	(attrbuf->af_stime != VATTR((&(set->af_klist[i]))).af_stime) )
      continue;

    /*** date of last lock change ***/
    if ( (attrbuf->af_ltime != AF_NOTIME) &&
	(attrbuf->af_ltime != VATTR((&(set->af_klist[i]))).af_ltime) )
      continue;

    /*** user defined attributes ***/
    if (attrbuf->af_udattrs[0]) {
      /* if list of user defined attributes is not empty or there are */
      /* attributes */
      match = TRUE;
      if ((attrbuf->af_udattrs[0][0] != '\0') ||
	  (VATTR((&(set->af_klist[i]))).af_udanum != 0)) {
	/* test all given entries */
	j=0;
	while (attrbuf->af_udattrs[j]
	       && (match = !afMatchUda (&(set->af_klist[i]),
					attrbuf->af_udattrs[j]))) {
	  j++;
	}
      } /* if */
      if (match == FALSE)
	continue;
    } /* if */
    
    
    /* add key to new set */
    tmpset.af_klist[k] = set->af_klist[i];
    afIncreaseAccess (&(tmpset.af_klist[k]));
    k++;
    
  } /* for */
  
  tmpset.af_nkeys = k;

  /* if the resulting set shall be stored in the old set */
  if (set == newset) {
    af_dropset (set);
    *set = tmpset;
  }
  else
    *newset = tmpset;
  
  return (k);
}


/*================================================================
 *	af_intersect -- build intersection of two sets
 *================================================================*/

EXPORT int af_intersect (set1, set2, newset)
     Af_set *set1, *set2;
     Af_set *newset;
{
  register int i, j, k;
  Af_set tmpset;

  if (af_settest (set1) || af_settest (set2))
    FAIL ("intersect", "", AF_EINVSET, ERROR);

  /* allocate enough memory */
  if (set1->af_nkeys >= set2->af_nkeys)
    tmpset.af_setlen = set1->af_nkeys;
  else
    tmpset.af_setlen = set2->af_nkeys;

  if (tmpset.af_setlen) {
    if ((tmpset.af_klist = (Af_key *)malloc ((unsigned) (tmpset.af_setlen * sizeof(Af_key)))) == NULL)
      FAIL ("intersect", "malloc", AF_ESYSERR, ERROR);
  }
  else
    tmpset.af_klist = NULL;

  /* build new set */
  k=0;
  for (i=0; i<set1->af_nkeys; i++)
    for (j=0; j<set2->af_nkeys; j++) {
      if (!af_keycmp (&(set1->af_klist[i]), &(set2->af_klist[j]))) {
	tmpset.af_klist[k] = set1->af_klist[i];
	afIncreaseAccess (&(tmpset.af_klist[k]));
	k++;
      }
    }
  tmpset.af_nkeys = k;
  
  /* if the new set shall be stored in one of the old ones */
  if (newset == set1) {
    af_dropset (set1);
    *set1 = tmpset;
  }
  else {
    if (newset == set2) {
      af_dropset (set2);
      *set2 = tmpset;
    }
    else
      *newset = tmpset;
  }
  
  return (k);
}

/*================================================================
 *	af_union -- build union of two sets
 *================================================================*/

EXPORT int af_union (set1, set2, newset, checkDouble)
     Af_set *set1, *set2;
     Af_set *newset;
     int checkDouble;
{
  int    newLen = set1->af_nkeys + set2->af_nkeys, i, j, k;
  bool   dbl=FALSE;
  Af_set tmpSet, *resultSetPtr, *secondSetPtr;

  if (af_settest (set1) || af_settest (set2))
    FAIL ("union", "", AF_EINVSET, ERROR);

  if (newLen == 0) {
    af_initset (&tmpSet);
    *newset = tmpSet;
    return (0);
  }

  /* allocate memory and set restultSetPtr */
  if ((newset == set1) && (set1->af_setlen > 0)) {
    if ((set1->af_klist = (Af_key *)realloc (set1->af_klist, (size_t) (newLen * sizeof(Af_key)))) == NULL)
      FAIL ("union", "malloc", AF_ESYSERR, ERROR);
    set1->af_setlen = newLen;
    resultSetPtr = set1;
    secondSetPtr = set2;
  }
  else if ((newset == set2) && (set2->af_setlen > 0)) {
    if ((set2->af_klist = (Af_key *)realloc (set2->af_klist, (size_t) (newLen * sizeof(Af_key)))) == NULL)
      FAIL ("union", "malloc", AF_ESYSERR, ERROR);
    set2->af_setlen = newLen;
    resultSetPtr = set2;
    secondSetPtr = set1;
  }
  else {
    if ((tmpSet.af_klist = (Af_key *)malloc ((size_t) (newLen * sizeof(Af_key)))) == NULL)
      FAIL ("union", "malloc", AF_ESYSERR, ERROR);
    tmpSet.af_setlen = newLen;
    /* copy set1 to result set */
    for (i=0; i<set1->af_nkeys; i++) {
      tmpSet.af_klist[i] = set1->af_klist[i];
      afIncreaseAccess (&(tmpSet.af_klist[i]));
    }
    tmpSet.af_nkeys = i;
    resultSetPtr = &tmpSet;
    secondSetPtr = set2;
  }

  /* add second set to result set */
  k = resultSetPtr->af_nkeys;
  for (j=0; j<secondSetPtr->af_nkeys; j++) {
    if (checkDouble) {
      for (i=0; i<resultSetPtr->af_nkeys; i++) {
	if (!af_keycmp (&(resultSetPtr->af_klist[i]), &(secondSetPtr->af_klist[j])))
	  dbl = TRUE;
      }
    }
    if (dbl)
      dbl = FALSE;
    else {
      resultSetPtr->af_klist[k] = secondSetPtr->af_klist[j];
      afIncreaseAccess (&(resultSetPtr->af_klist[k]));
      k++;
    }
  }
  resultSetPtr->af_nkeys = k;
  *newset = *resultSetPtr;
  return (k);
}

/*================================================================
 *	af_diff -- build difference between two sets
 *================================================================*/

EXPORT int af_diff (set1, set2, newset)
     Af_set *set1, *set2;
     Af_set *newset;
{
  register int i, j, k;
  bool     found = FALSE;
  Af_set   tmpset;

  if (af_settest (set1) || af_settest (set2))
    FAIL ("diff", "", AF_EINVSET, ERROR);

  /* allocate enough memory */
  if ((tmpset.af_setlen = set1->af_nkeys)) {
    if ((tmpset.af_klist = (Af_key *)malloc ((unsigned) (tmpset.af_setlen * sizeof(Af_key)))) == NULL)
      FAIL ("diff", "malloc", AF_ESYSERR, ERROR);
  }
  else
    tmpset.af_klist = NULL;

  /* build new set */
  k=0;
  for (i=0; i<set1->af_nkeys; i++) {
    for (j=0; j<set2->af_nkeys; j++) {
      if (!af_keycmp (&(set1->af_klist[i]), &(set2->af_klist[j])))
	found = TRUE;
    }
    if (found)
      found = FALSE;
    else {
      tmpset.af_klist[k] = set1->af_klist[i];
      afIncreaseAccess (&(tmpset.af_klist[k]));
      k++;
    }
  }
  tmpset.af_nkeys = k;
  
  /* if the new set shall be stored in one of the old ones */
  if (newset == set1) {
    af_dropset (set1);
    *set1 = tmpset;
  }
  else {
    if (newset == set2) {
      af_dropset (set2);
      *set2 = tmpset;
    }
    else
      *newset = tmpset;
  }
  
  return (k);
}
