/* 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.
*/
/*
 * AtFStk -- Attribute Filesystem Toolkit Library
 *
 * bind_attr.c -- AtFS toolkit library (Version Binding)
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: bind_attr.c[7.0] Fri Jan 28 19:37:39 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"
#include "sttk.h"
#include "atfstk.h"
#include "bind.h"

/*=========================
 *  getAttr
 *=========================*/

LOCAL int getAttr (attrName)
     char *attrName;
{
  int i;
  
  for (i = 0; atStdAttrTab[i].name; i++) {
    if (!(strcmp (attrName, atStdAttrTab[i].name)))
      return (atStdAttrTab[i].code);
  }
  return (-1);
}

/*=========================
 *  atBindAttrAbsolute
 *=========================*/

EXPORT int atBindAttrAbsolute (set, attrName, attrValue)
     Af_set *set;
     char *attrName;
     char *attrValue;
{
  int      attrNum = getAttr (attrName), hits, bindType;
  Af_attrs reqAttrs;
  char     *alias;
  
  af_initattrs (&reqAttrs);
  
  switch (attrNum) {
  case AT_CODE_ALIAS:
    if ((reqAttrs.af_udattrs[0] =
	 malloc ((size_t) strlen (AT_ATTALIAS) + strlen(attrValue) + 2)) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      af_dropset (set);
      return (0);
    }
    strcpy (reqAttrs.af_udattrs[0], AT_ATTALIAS);
    strcat (reqAttrs.af_udattrs[0], "=");
    if (attrValue)
      strcat (reqAttrs.af_udattrs[0], attrValue);
    reqAttrs.af_udattrs[1] = NULL;
    break;
  case AT_CODE_ATIME:
    if (attrValue && !(reqAttrs.af_atime = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher access date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_AUTHOR:
    if (attrValue)
      atScanUser (attrValue, &reqAttrs.af_author);
    break;
  case AT_CODE_CACHEKEY:
    if ((reqAttrs.af_udattrs[0] =
	 malloc ((size_t) strlen (AT_ATTCACHEKEY) + strlen(attrValue) + 2)) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      af_dropset (set);
      return (0);
    }
    strcpy (reqAttrs.af_udattrs[0], AT_ATTCACHEKEY);
    strcat (reqAttrs.af_udattrs[0], "=");
    if (attrValue)
      strcat (reqAttrs.af_udattrs[0], attrValue);
    reqAttrs.af_udattrs[1] = NULL;
    break;
    break;
  case AT_CODE_CTIME:
    if (attrValue && !(reqAttrs.af_ctime = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher create date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_GENERATION:
    if (attrValue) {
      if (!strcmp (attrValue, "busy"))
	reqAttrs.af_gen = AF_BUSYVERS;
      else
	reqAttrs.af_gen = atoi (attrValue);
    }
    break;
  case AT_CODE_HOST:
    if (attrValue)
      strcpy (reqAttrs.af_host, attrValue);
    break;
  case AT_CODE_LOCK:
    if (!attrValue) {
      Af_key tmpKey;
      int i;
      char *locker;
      for (i=af_nrofkeys(set)-1; i >= 0; i--) {
	af_setgkey (set, i, &tmpKey);
	locker = af_retattr (&tmpKey, AF_ATTLOCKER);
	if (!locker[0])
	  af_setposrmkey (set, i);
	af_dropkey (&tmpKey);
      }
      return (af_nrofkeys(set));
    }
    atScanUser (attrValue, &reqAttrs.af_locker);
    break;
  case AT_CODE_LTIME:
    if (!attrValue) {
      Af_key tmpKey;
      int i;
      for (i=af_nrofkeys(set)-1; i >= 0; i--) {
	af_setgkey (set, i, &tmpKey);
	if (af_rettimeattr (&tmpKey, AF_ATTLTIME) == AF_NOTIME)
	  af_setposrmkey (set, i);
	af_dropkey (&tmpKey);
      }
      return (af_nrofkeys(set));
    }
    if (!(reqAttrs.af_ltime = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher lock date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_MTIME:
    if (attrValue && !(reqAttrs.af_mtime = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher modification date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_NAME:
    if (attrValue)
      strcpy (reqAttrs.af_name, attrValue);
    break;
  case AT_CODE_OWNER:
    if (attrValue)
      atScanUser (attrValue, &reqAttrs.af_owner);
    break;
  case AT_CODE_REVISION:
    if (attrValue) {
      if (!strcmp (attrValue, "busy"))
	reqAttrs.af_rev = AF_BUSYVERS;
      else
	reqAttrs.af_rev = atoi (attrValue);
    }
    break;
  case AT_CODE_SIZE:
    if (attrValue)
      reqAttrs.af_size = atoi (attrValue);
    break;
  case AT_CODE_STATE:
    if (attrValue && (reqAttrs.af_state = atScanStatus (attrValue)) == AF_NOSTATE) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "unknown version state");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_STIME:
    if (attrValue && !(reqAttrs.af_stime = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher save date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_SYSPATH:
    if (attrValue)
      strcpy (reqAttrs.af_syspath, attrValue);
    break;
  case AT_CODE_TYPE:
    if (attrValue)
      strcpy (reqAttrs.af_type, attrValue);
    break;
  case AT_CODE_VERSION:
    if (attrValue && !strcmp (attrValue, "busy")) {
      reqAttrs.af_gen = AF_BUSYVERS;
      reqAttrs.af_rev = AF_BUSYVERS;
      break;
    }
    if ((bindType = atScanBinding (attrValue ? attrValue : "", &alias, &reqAttrs.af_gen,
				   &reqAttrs.af_rev, NULL)) == AT_BIND_DEFAULT)
      break;
    if (bindType == AT_BIND_VNUM)
      break;
    /* else assume version number alias (even if bindType is AT_BIND_RULE) */
    if ((reqAttrs.af_udattrs[0] =
	 malloc ((size_t) strlen (AT_ATTALIAS) + strlen(alias) + 2)) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      af_dropset (set);
      return (0);
    }
    strcpy (reqAttrs.af_udattrs[0], AT_ATTALIAS);
    strcat (reqAttrs.af_udattrs[0], "=");
    if (attrValue)
      strcat (reqAttrs.af_udattrs[0], attrValue);
    reqAttrs.af_udattrs[1] = NULL;
    break;
  case -1: /* no standard attribute -- assume user defined attr */
    if ((reqAttrs.af_udattrs[0] =
	 malloc ((size_t) strlen(attrName) + strlen(attrValue ? attrValue : "") + 2)) == NULL) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "not enough memory");
      af_dropset (set);
      return (0);
    }
    strcpy (reqAttrs.af_udattrs[0], attrName);
    strcat (reqAttrs.af_udattrs[0], "=");
    if (attrValue)
      strcat (reqAttrs.af_udattrs[0], attrValue);
    reqAttrs.af_udattrs[1] = NULL;
    break;
  }
  
  if ((hits = af_subset (set, &reqAttrs, set)) == -1) {
    atBindError = TRUE;
    strcpy (atBindErrorMsg, af_errmsg ("atBindAttrAbsolute"));
  }
  
  if (reqAttrs.af_udattrs[0])
    free (reqAttrs.af_udattrs[0]);
  
  return (hits);
}

/*=========================
 *  atBindAttrRelative
 *=========================*/

LOCAL int dateCmp (date1, date2, op)
     time_t date1;
     time_t date2;
     int op;
{
  switch (op) {
  case BIND_PRED_ATTRGE:
    return (date1 >= date2);
  case BIND_PRED_ATTRGT:
    return (date1 > date2);
  case BIND_PRED_ATTRLE:
    return (date1 <= date2);
  case BIND_PRED_ATTRLT:
    return (date1 < date2);
  case BIND_PRED_ATTRNOT:
    return (date1 != date2);
  }
  return (FALSE);
}

LOCAL int userCmp (user1, user2, op)
     Af_user *user1;
     Af_user *user2;
     int op;
{
  switch (op) {
  case BIND_PRED_ATTRGE:
    if (strcmp (user1->af_username, user2->af_username) >= 0)
      return (TRUE);
    if (strcmp (user1->af_userdomain, user2->af_userdomain) >= 0) 
      return (TRUE);
    break;
  case BIND_PRED_ATTRGT:
    if (strcmp (user1->af_username, user2->af_username) > 0)
      return (TRUE);
    if (strcmp (user1->af_userdomain, user2->af_userdomain) > 0) 
      return (TRUE);
    break;
  case BIND_PRED_ATTRLE:
    if (strcmp (user1->af_username, user2->af_username) <= 0)
      return (TRUE);
    if (strcmp (user1->af_userdomain, user2->af_userdomain) <= 0) 
      return (TRUE);
    break;
  case BIND_PRED_ATTRLT:
    if (strcmp (user1->af_username, user2->af_username) < 0)
      return (TRUE);
    if (strcmp (user1->af_userdomain, user2->af_userdomain) < 0) 
      return (TRUE);
    break;
  case BIND_PRED_ATTRNOT:
    if (strcmp (user1->af_username, user2->af_username))
      return (TRUE);
    if (strcmp (user1->af_userdomain, user2->af_userdomain)) 
      return (TRUE);
    break;
  }
  return (FALSE);
}

LOCAL int intCmp (val1, val2, op)
     int val1;
     int val2;
     int op;
{
  switch (op) {
  case BIND_PRED_ATTRGE:
    return (val1 >= val2);
  case BIND_PRED_ATTRGT:
    return (val1 > val2);
  case BIND_PRED_ATTRLE:
    return (val1 <= val2);
  case BIND_PRED_ATTRLT:
    return (val1 < val2);
  case BIND_PRED_ATTRNOT:
    return (val1 != val2);
  }
  return (FALSE);
}

LOCAL int versionCmp (gen1, rev1, gen2, rev2, op)
     int gen1;
     int rev1;
     int gen2;
     int rev2;
     int op;
{
  switch (op) {
  case BIND_PRED_ATTRGE:
    if (gen1 > gen2) return (TRUE);
    if (gen1 == gen2) return (rev1 >= rev2);
    break;
  case BIND_PRED_ATTRGT:
    if (gen1 > gen2) return (TRUE);
    return (rev1 > rev2);
  case BIND_PRED_ATTRLE:
    if (gen1 < gen2) return (TRUE);
    if (gen1 == gen2) return (rev1 <= rev2);
    break;
  case BIND_PRED_ATTRLT:
    if (gen1 < gen2) return (TRUE);
    return (rev1 < rev2);
  case BIND_PRED_ATTRNOT:
    if (gen1 != gen2) return (TRUE);
    return (rev1 != rev2);
  }
  return (FALSE);
}

LOCAL int strValCmp (udaVal1, udaVal2, op)
     char *udaVal1;
     char *udaVal2;
     int op;
{
  switch (op) {
  case BIND_PRED_ATTRGE:
    return (strcmp (udaVal1, udaVal2) >= 0);
  case BIND_PRED_ATTRGT:
    return (strcmp (udaVal1, udaVal2) > 0);
  case BIND_PRED_ATTRLE:
    return (strcmp (udaVal1, udaVal2) <= 0);
  case BIND_PRED_ATTRLT:
    return (strcmp (udaVal1, udaVal2) < 0);
  case BIND_PRED_ATTRNOT:
    return (strcmp (udaVal1, udaVal2) != 0);
  }
  return (FALSE);
}

EXPORT int atBindAttrRelative (set, attrName, attrValue, op)
     Af_set *set;
     char *attrName;
     char *attrValue;
     int op;
{
  int attrNum = getAttr (attrName), i;
  Af_key   tmpKey;
  time_t   dateVal;
  Af_user  userVal;
  int      numVal;
  int      bindType, genNo, revNo;
  char     *alias, *origUdaVal;
  
  if (!attrValue)
    attrValue = "";
  
  switch (attrNum) {
  case AT_CODE_ALIAS:
    alias = attrValue;
    bindType = AT_BIND_ALIAS;
    break;
  case AT_CODE_ATIME:
    if (!(dateVal = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher access date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_AUTHOR:
    atScanUser (attrValue, &userVal);
    break;
  case AT_CODE_CACHEKEY:
    break;
  case AT_CODE_CTIME:
    if (!(dateVal = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher create date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_GENERATION:
    if (attrValue) {
      if (!strcmp (attrValue, "busy"))
	numVal = AF_BUSYVERS;
      else
	numVal = atoi (attrValue);
    }
    else
      numVal = AF_NOVNUM;
    break;
  case AT_CODE_HOST:
    break;
  case AT_CODE_LOCK:
    atScanUser (attrValue, &userVal);
    break;
  case AT_CODE_LTIME:
    if (!(dateVal = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher lock date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_MTIME:
    if (!(dateVal = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher modification date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_NAME:
    break;
  case AT_CODE_OWNER:
    atScanUser (attrValue, &userVal);
    break;
  case AT_CODE_REVISION:
    if (attrValue) {
      if (!strcmp (attrValue, "busy"))
	numVal = AF_BUSYVERS;
      else
	numVal = atoi (attrValue);
    }
    else
      numVal = AF_NOVNUM;
    break;
  case AT_CODE_SIZE:
    if (attrValue)
      numVal = atoi (attrValue);
    else
      numVal = AF_NOSIZE;
    break;
  case AT_CODE_STATE:
    if ((numVal = atScanStatus (attrValue)) == AF_NOSTATE) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "unknown version state");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_STIME:
    if (!(dateVal = stMktime (attrValue))) {
      atBindError = TRUE;
      strcpy (atBindErrorMsg, "cannot decipher save date");
      af_dropset (set);
      return (0);
    }
    break;
  case AT_CODE_SYSPATH:
    break;
  case AT_CODE_TYPE:
    break;
  case AT_CODE_VERSION:
    if (!strcmp (attrValue, "busy")) {
      bindType = AT_BIND_VNUM;
      genNo = revNo = AF_BUSYVERS;
    }
    else
      bindType = atScanBinding (attrValue, &alias, &genNo, &revNo, NULL);
    break;
  case -1: /* no standard attribute -- assume user defined attr */
    break;
  }
  
  for (i=af_nrofkeys(set)-1; i >= 0; i--) {
    (void) af_setgkey (set, i, &tmpKey);
    switch (attrNum) {
    case AT_CODE_ATIME:
      if (!dateCmp (af_rettimeattr (&tmpKey, AF_ATTATIME), dateVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_AUTHOR:
      if (!userCmp (af_retuserattr (&tmpKey, AF_ATTAUTHOR), &userVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_CACHEKEY:
      if ((origUdaVal = af_retattr (&tmpKey, AT_ATTCACHEKEY))) {
	if (!strValCmp (origUdaVal, attrValue, op))
	  af_setposrmkey (set, i);
      }
      else
	if (op != BIND_PRED_ATTRNOT)	
	  af_setposrmkey (set, i);
      break;
    case AT_CODE_CTIME:
      if (!dateCmp (af_rettimeattr (&tmpKey, AF_ATTCTIME), dateVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_GENERATION:
      if (!intCmp (af_retnumattr (&tmpKey, AF_ATTGEN), numVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_HOST:
      if (!strValCmp (af_retattr (&tmpKey, AF_ATTHOST), attrValue, op));
	af_setposrmkey (set, i);
      break;
    case AT_CODE_LOCK:
      if (!userCmp (af_retuserattr (&tmpKey, AF_ATTLOCKER), &userVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_LTIME:
      if (!dateCmp (af_rettimeattr (&tmpKey, AF_ATTLTIME), dateVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_MTIME:
      if (!dateCmp (af_rettimeattr (&tmpKey, AF_ATTMTIME), dateVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_NAME:
      if (!strValCmp (af_retattr (&tmpKey, AF_ATTNAME), attrValue, op));
	af_setposrmkey (set, i);
      break;
    case AT_CODE_OWNER:
      if (!userCmp (af_retuserattr (&tmpKey, AF_ATTOWNER), &userVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_REVISION:
      if (!intCmp (af_retnumattr (&tmpKey, AF_ATTREV), numVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_SIZE:
      if (!intCmp (af_retnumattr (&tmpKey, AF_ATTSIZE), numVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_STATE:
      if (!intCmp (af_retnumattr (&tmpKey, AF_ATTSTATE), numVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_STIME:
      if (!dateCmp (af_rettimeattr (&tmpKey, AF_ATTSTIME), dateVal, op))
	af_setposrmkey (set, i);
      break;
    case AT_CODE_SYSPATH:
      if (!strValCmp (af_retattr (&tmpKey, AF_ATTSPATH), attrValue, op));
	af_setposrmkey (set, i);
      break;
    case AT_CODE_TYPE:
      if (!strValCmp (af_retattr (&tmpKey, AF_ATTTYPE), attrValue, op));
	af_setposrmkey (set, i);
      break;
    case AT_CODE_ALIAS:
    case AT_CODE_VERSION:
      switch (bindType) {
      case AT_BIND_DEFAULT:
      case AT_BIND_VNUM:
	if ((genNo == AF_NOVNUM) && (revNo == AF_NOVNUM))
	  break;
	if (genNo == AF_NOVNUM) {
	  if (!intCmp (af_retnumattr (&tmpKey, AF_ATTREV), revNo, op))
	    af_setposrmkey (set, i);
	  break;
	}
	if (revNo == AF_NOVNUM) {
	  if (!intCmp (af_retnumattr (&tmpKey, AF_ATTGEN), genNo, op))
	    af_setposrmkey (set, i);
	  break;
	}
	if (!versionCmp (af_retnumattr (&tmpKey, AF_ATTGEN),
			 af_retnumattr (&tmpKey, AF_ATTREV), genNo, revNo, op))
	  af_setposrmkey (set, i);
	break;
      case AT_BIND_RULE:
      case AT_BIND_ALIAS:
	if ((origUdaVal = af_retattr (&tmpKey, AT_ATTALIAS))) {
	  if (!strValCmp (origUdaVal, alias, op))
	    af_setposrmkey (set, i);
	}
	else
	  if (op != BIND_PRED_ATTRNOT)
	    af_setposrmkey (set, i);
	break;
      }
      break;
    case -1: /* no standard attribute -- assume user defined attr */
      if ((origUdaVal = af_retattr (&tmpKey, attrName))) {
	if (!strValCmp (origUdaVal, attrValue, op))
	  af_setposrmkey (set, i);
      }
      else
	if (op != BIND_PRED_ATTRNOT)	
	  af_setposrmkey (set, i);
      break;
    }
    af_dropkey (&tmpKey);
  }
  
  return (af_nrofkeys(set));
}

/*=========================
 *  atBindAttrMinMax
 *=========================*/

EXPORT int atBindAttrMinMax (set, attrName, op)
     Af_set *set;
     char *attrName;
     int op;
{
  int attrNum = getAttr (attrName);
  int maxSetPos = af_nrofkeys (set)-1, curSetPos = 0;
  int i, j, cmpCode;
  char *curUdaVal, *nextUdaVal;
  Af_key curKey, nextKey;
  
  Af_user curUserVal, *userPtr;
  
  if (maxSetPos < 1) /* set is empty or contains just one key */
    return (maxSetPos+1);
  
  (void) af_setgkey (set, curSetPos++, &curKey);
  
  for (i=1; i <= maxSetPos; i++) {
    (void) af_setgkey (set, curSetPos, &nextKey);
    
    switch (attrNum) {
    case AT_CODE_ATIME:
      cmpCode = af_rettimeattr (&curKey, AF_ATTATIME) - af_rettimeattr (&nextKey, AF_ATTATIME);
      break;
    case AT_CODE_AUTHOR:
      userPtr = af_retuserattr (&curKey, AF_ATTAUTHOR);
      curUserVal = *userPtr;
      userPtr = af_retuserattr (&nextKey, AF_ATTAUTHOR);
      if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRLT))
	cmpCode = -1;
      else if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRGT))
	cmpCode = 1;
      else
	cmpCode = 0;
      break;
    case AT_CODE_CACHEKEY:
      cmpCode = strcmp (af_retattr (&curKey, AT_ATTCACHEKEY), af_retattr (&nextKey, AT_ATTCACHEKEY));
      break;
    case AT_CODE_CTIME:
      cmpCode = af_rettimeattr (&curKey, AF_ATTCTIME) - af_rettimeattr (&nextKey, AF_ATTCTIME);
      break;
    case AT_CODE_GENERATION:
      cmpCode = af_retnumattr (&curKey, AF_ATTGEN) - af_retnumattr (&nextKey, AF_ATTGEN);
      break;
    case AT_CODE_HOST:
      cmpCode = 0;
      break;
    case AT_CODE_LOCK:
      userPtr = af_retuserattr (&curKey, AF_ATTLOCKER);
      curUserVal = *userPtr;
      userPtr = af_retuserattr (&nextKey, AF_ATTLOCKER);
      if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRLT))
	cmpCode = -1;
      else if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRGT))
	cmpCode = 1;
      else
	cmpCode = 0;
      break;
    case AT_CODE_LTIME:
      cmpCode = af_rettimeattr (&curKey, AF_ATTLTIME) - af_rettimeattr (&nextKey, AF_ATTLTIME);
      break;
    case AT_CODE_MTIME:
      cmpCode = af_rettimeattr (&curKey, AF_ATTMTIME) - af_rettimeattr (&nextKey, AF_ATTMTIME);
      break;
    case AT_CODE_NAME:
      cmpCode = 0;
      break;
    case AT_CODE_OWNER:
      userPtr = af_retuserattr (&curKey, AF_ATTOWNER);
      curUserVal = *userPtr;
      userPtr = af_retuserattr (&nextKey, AF_ATTOWNER);
      if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRLT))
	cmpCode = -1;
      else if (userCmp (&curUserVal, userPtr, BIND_PRED_ATTRGT))
	cmpCode = 1;
      else
	cmpCode = 0;
      break;
    case AT_CODE_REVISION:
      cmpCode = af_retnumattr (&curKey, AF_ATTREV) - af_retnumattr (&nextKey, AF_ATTREV);
      break;
    case AT_CODE_SIZE:
      cmpCode = af_retnumattr (&curKey, AF_ATTSIZE) - af_retnumattr (&nextKey, AF_ATTSIZE);
      break;
    case AT_CODE_STATE:
      cmpCode = af_retnumattr (&curKey, AF_ATTSTATE) - af_retnumattr (&nextKey, AF_ATTSTATE);
      break;
    case AT_CODE_STIME:
      cmpCode = af_rettimeattr (&curKey, AF_ATTSTIME) - af_rettimeattr (&nextKey, AF_ATTSTIME);
      break;
    case AT_CODE_SYSPATH:
      cmpCode = 0;
      break;
    case AT_CODE_TYPE:
      cmpCode = 0;
      break;
    case AT_CODE_ALIAS:
    case AT_CODE_VERSION:
      cmpCode = af_retnumattr (&curKey, AF_ATTGEN) - af_retnumattr (&nextKey, AF_ATTGEN);
      if (cmpCode == 0)
	cmpCode = af_retnumattr (&curKey, AF_ATTREV) - af_retnumattr (&nextKey, AF_ATTREV);
      break;
    case -1: /* no standard attribute -- assume user defined attr */
      if (!(curUdaVal = af_retattr (&curKey, attrName))) {
	/* Uda non existent for current version -> delete cur vers */
	af_setposrmkey (set, curSetPos-1);
	af_dropkey (&curKey);
	curKey = nextKey;
	if (i == maxSetPos) {
	  /* at the end of the set delete last key too, if necessary */
	  af_setposrmkey (set, curSetPos-1);
	  af_dropkey (&curKey);
	}
	continue;
      }
      if (!(nextUdaVal = af_retattr (&nextKey, attrName))) {
	/* Uda non existent for next version -> simulate smaller/bigger */
	free (curUdaVal);
	if (op == BIND_PRED_ATTRMAX) cmpCode = 1;
	else if (op == BIND_PRED_ATTRMIN) cmpCode = -1;
	break;
      }
      cmpCode = strcmp (curUdaVal, nextUdaVal);
      free (curUdaVal);
      free (nextUdaVal);
      break;
    }
    
    switch (op) {
    case BIND_PRED_ATTRMAX:
      if (cmpCode < 0) {
	for (j = curSetPos-1; j >= 0; j--)
	  af_setposrmkey (set, j);
	curSetPos = 1;
	af_dropkey (&curKey);
	curKey = nextKey;
      }
      else if (cmpCode > 0) {
	af_setposrmkey (set, curSetPos);
	af_dropkey (&nextKey);
      }
      else {
	curSetPos++;
	af_dropkey (&curKey);
	curKey = nextKey;
      }
      break;
    case BIND_PRED_ATTRMIN:
      if (cmpCode > 0) {
	for (j = curSetPos-1; j >= 0; j--)
	  af_setposrmkey (set, j);
	curSetPos = 1;
	af_dropkey (&curKey);
	curKey = nextKey;
      }
      else if (cmpCode < 0) {
	af_setposrmkey (set, curSetPos);
	af_dropkey (&nextKey);
      }
      else {
	curSetPos++;
	af_dropkey (&curKey);
	curKey = nextKey;
      }
      break;
    }
  }
  af_dropkey (&curKey);
  
  return (af_nrofkeys (set));
}
