/* 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
 *
 * afudattrs.c -- manage user defined attributes
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afudattrs.c[7.0] Fri Jan 14 13:35:27 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"

LOCAL int af_fhash (htabsiz, hstr) /* from Aho/Ullman */
     int  htabsiz; 
     char *hstr;
{
  register char *p;
  register unsigned long hval, g;

  hval = 0;
  for (p = hstr; (*p!=AF_UDANAMDEL) && (*p!='\0'); p++) {
    hval = (hval << 4) ^ (*p);
    if ((g = hval & 0xf0000000)) {
      hval = hval ^ (g >> 24);
      hval = hval ^ g;
    }
  }
  return (hval % htabsiz);
}

/*===============================================================
 * afAttrValCmp -- compare single values in a string of the form
 *                          name=[value1 value2 ...]
 *           Return values like strcmp.
 *==============================================================*/

LOCAL int afAttrValCmp (str1, str2)
     char *str1, *str2;
{
  while (*str1 == *str2) {
    if ((*str1 == AF_UDAVALDEL) || (*str1 == '\0'))
      return (0);
    str1++;
    str2++;
  }
  if (((*str1 == AF_UDAVALDEL) && (*str2 =='\0')) || ((*str1 =='\0') && (*str2 == AF_UDAVALDEL)))
    return (0);
  else
    return (*str1 - *str2);
}


/*=====================================================================
 * afValLookup -- look for single value
 *=====================================================================*/ 

LOCAL char *afValLookup (entry, value)
     char *entry, *value; 
{
  register char *valptr;

  if ((valptr = strchr (entry, AF_UDANAMDEL)))
    valptr += sizeof (char);

  /* search until value is found */
  while (afAttrValCmp (valptr, value)) {
    if ((valptr = strchr (valptr, AF_UDAVALDEL)) == NULL) 
      return NULL;
    valptr += sizeof (char);
  }

  return (valptr);
}

/*=====================================================================
 * afInitUdas -- Initialize Uda part in revision list
 *=====================================================================*/ 

EXPORT void afInitUdas (key)
     Af_key *key;
{
  int i;

  VATTR(key).af_udanum = 0;
  VATTR(key).af_uhtab = NULL;
  for (i=0; i<AF_UDANUM; i++)
    VATTR(key).af_udalist[i] = NULL;
}

/*=====================================================================
 * afEnterUda -- enter user defined attribute
 *=====================================================================*/ 

EXPORT int afEnterUda (key, symbol)
     Af_key *key;
     char   *symbol;
{
  int  totalSize, i, symindex;
  char *symptr;
  Af_hashent *new, *curptr;

  /* allocate memory */
  if ((symptr = af_malloc (key->af_ldes, (unsigned) (strlen (symbol) + sizeof (char)))) == NULL)
    return (ERROR);
  strcpy (symptr, symbol);
  
  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    symindex = af_fhash (AF_UDAHASHSIZE, symbol);
    if (!VATTR(key).af_uhtab[symindex].symbol)  /* entry is not used yet */ {
      VATTR(key).af_uhtab[symindex].symbol = symptr;
      VATTR(key).af_uhtab[symindex].next = NULL;
    }
    else { /* collision! */
      if ((new = (Af_hashent*)af_malloc (key->af_ldes, (unsigned) sizeof (Af_hashent))) == NULL)
	return (ERROR);
      new->symbol = symptr;
      new->next = NULL;
      curptr = &(VATTR(key).af_uhtab[symindex]);
      while (curptr->next)
	curptr = curptr->next;
      curptr->next = new;
    }
  }
  else {
    /* if linear list of user defined attributes is full */
    if (VATTR(key).af_udanum >= AF_UDANUM) {
      /* initialize hashtable */
      totalSize = AF_UDAHASHSIZE * sizeof(Af_hashent);
      if ((VATTR(key).af_uhtab = (Af_hashent *)af_malloc (key->af_ldes, (unsigned) totalSize)) == NULL)
	return (ERROR);
      memset ((char *)VATTR(key).af_uhtab, 0, totalSize);
      for (i=0; i<AF_UDANUM; i++) {
	if (VATTR(key).af_udalist[i] == NULL)
	  continue;
	afEnterUda (key, VATTR(key).af_udalist[i]);
      }
      afEnterUda (key, symbol);
    }
    else { /* linear list is not full */
      i=0;
      while (VATTR(key).af_udalist[i]) i++;
      VATTR(key).af_udalist[i] = symptr;
      VATTR(key).af_udalist[i+1] = NULL;
    }
  }
  VATTR(key).af_udanum += 1;
  return (AF_OK);
}

/*=====================================================================
 * afReplUda  -- replace user defined attribute
 *=====================================================================*/ 

EXPORT int afReplUda (key, symbol)
     Af_key *key;
     char   *symbol;
{
  int i, symindex;
  Af_hashent *entry;

  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    symindex = af_fhash (AF_UDAHASHSIZE, symbol);
    if (VATTR(key).af_uhtab[symindex].symbol)  /* found something */ {
      if (!afAttrNameCmp (VATTR(key).af_uhtab[symindex].symbol, symbol)) { /* found it ? */
	if ((VATTR(key).af_uhtab[symindex].symbol = af_realloc (key->af_ldes, VATTR(key).af_uhtab[symindex].symbol, (unsigned) (strlen (symbol) + sizeof (char)))) == NULL)
	  return (ERROR);
	strcpy (VATTR(key).af_uhtab[symindex].symbol, symbol);
	return (AF_OK);
      }
      else {
	entry = &(VATTR(key).af_uhtab[symindex]);
	while (entry->next) {
	  entry = entry->next;
	  if (!afAttrNameCmp (entry->symbol, symbol)) {
	    if ((entry->symbol = af_realloc (key->af_ldes, entry->symbol, (unsigned) (strlen (symbol) + sizeof (char)))) == NULL)
	      return (ERROR);
	    strcpy (entry->symbol, symbol);
	    return (AF_OK);
	  } /* if */
	} /* while */
      } /* else */
    } /* if */
  }
  else {
    for (i=0; i<AF_UDANUM; i++) {
      if (VATTR(key).af_udalist[i] == NULL)
	continue;
      if (!afAttrNameCmp (VATTR(key).af_udalist[i], symbol))  {
	if ((VATTR(key).af_udalist[i] = af_realloc (key->af_ldes, VATTR(key).af_udalist[i], (unsigned) (strlen (symbol) + sizeof (char)))) == NULL)
	  return (ERROR);
	strcpy (VATTR(key).af_udalist[i], symbol);
	return (AF_OK);
      }
    }
  }
  FATAL ("ReplUda", "user defined attribute not found", AF_EINTERNAL, ERROR);
}

/*=====================================================================
 * afDelUda   -- delete user defined attribute
 *=====================================================================*/ 

EXPORT int afDelUda (key, symbol)
     Af_key *key;
     char   *symbol;
{
  int i, symindex;
  Af_hashent *del, *succ;

  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    symindex = af_fhash (AF_UDAHASHSIZE, symbol);
    if (!VATTR(key).af_uhtab[symindex].symbol)
      FATAL ("DelUda", "symbol not found", AF_EINTERNAL, ERROR); 
    del = &(VATTR(key).af_uhtab[symindex]);
    if (!afAttrNameCmp (del->symbol, symbol)) { /* found it ? */
      af_free (key->af_ldes, del->symbol);
      if (del->next) { /* if there are more entries */
	del->symbol = del->next->symbol;
	succ = del->next->next;
	af_free (key->af_ldes, (char *)del->next);
	del->next = succ;
      }
      else {
	del->symbol = NULL;
	del->next = NULL;
      }
      VATTR(key).af_udanum -= 1;
      return (AF_OK);
    }
    else {
      while (del->next) {
	if (!afAttrNameCmp (del->next->symbol, symbol)) {	  
	  af_free (key->af_ldes, del->next->symbol);
	  succ = del->next->next;
	  af_free (key->af_ldes, (char *)del->next);
	  del->next = succ;
	  VATTR(key).af_udanum -= 1;
	  return (AF_OK);
	}
	del = del->next;
      }
    }
  }
  else {
    for (i=0; i<AF_UDANUM; i++) {
      if (VATTR(key).af_udalist[i] == NULL)
	continue;
      if (!afAttrNameCmp (VATTR(key).af_udalist[i], symbol)) {
	af_free (key->af_ldes, VATTR(key).af_udalist[i]);
	VATTR(key).af_udalist[i] = NULL;
	VATTR(key).af_udanum -= 1;
	return (AF_OK);
      }
    }
  }
  FATAL ("DelUda", "user defined attribute not found", AF_EINTERNAL, ERROR);
}

/*=====================================================================
 * afLookupUda -- search user defined attribute
 *=====================================================================*/ 

EXPORT char *afLookupUda (key, symbol)
     Af_key *key; 
     char   *symbol; 
{
  int i, symindex;
  Af_hashent *targ;

  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    symindex = af_fhash (AF_UDAHASHSIZE, symbol);
    if (VATTR(key).af_uhtab[symindex].symbol) {
      if (!afAttrNameCmp (VATTR(key).af_uhtab[symindex].symbol, symbol))
	return (VATTR(key).af_uhtab[symindex].symbol);
      else { 
	targ = &(VATTR(key).af_uhtab[symindex]);
	while (targ->next) {
	  targ = targ->next;
	  if (!afAttrNameCmp (targ->symbol, symbol))
	    return (targ->symbol);
	}
      }
    }
  }
  else {
    for (i=0; i<AF_UDANUM; i++) {
      if (VATTR(key).af_udalist[i] == NULL)
	continue;
      if (!afAttrNameCmp (VATTR(key).af_udalist[i], symbol))
	return (VATTR(key).af_udalist[i]);
    }
  }
  return (NULL);
}

/*====================================================================
 *    afMatchUda
 *    returnes FALSE if entry matches, else TRUE
 *====================================================================*/

EXPORT int afMatchUda (key, entry)
     Af_key *key;
     char   *entry;
{
  char *udaptr, *valptr;

  /* if user defined attribute does not exist */
  if ((udaptr = afLookupUda (key, entry)) == NULL)
    return (ERROR);

  /* else compare values */
  if ((valptr = strchr (entry, AF_UDANAMDEL))) {
    do {
      valptr = valptr + sizeof (char);
      if ((afValLookup (udaptr, valptr) == NULL) && (*valptr !='\0'))
	return (ERROR);
    }
    while ((valptr = strchr (valptr, AF_UDAVALDEL)));
  }
  return (AF_OK);
}

/*=====================================================================
 * afListUdas -- create list of user defined attributes
 *               --> Pointerlist terminated by NULL
 *=====================================================================*/ 

EXPORT int afListUdas (key, symbollist)
     Af_key *key;
     char   **symbollist;
{
  int i, j=0;
  Af_hashent *h;

  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    for (i = 0; i < AF_UDAHASHSIZE; i++)
      if (VATTR(key).af_uhtab[i].symbol) {
	h = &(VATTR(key).af_uhtab[i]);
	while (h) {
	  symbollist[j] = h->symbol;
	  j++;
	  h = h->next;
	}
      }
  }
  else {
    for (i=0; i<AF_UDANUM; i++) {
      if (VATTR(key).af_udalist[i] == NULL)
	continue;
      symbollist[j] = VATTR(key).af_udalist[i];
      j++;
    }
  }
  symbollist[j] = NULL;
  if (j==0)
    return (j);
  else
    return (j-1);
}

/*=====================================================================
 * afDropUdas -- Drop list of user defined attributes
 *=====================================================================*/ 

EXPORT int afDropUdas (key)
     Af_key *key;
{
  int i;
  Af_hashent *entry;

  /* if there is a hashtable */
  if (VATTR(key).af_uhtab) {
    for (i=0; i<AF_UDAHASHSIZE; i++) {
      if (VATTR(key).af_uhtab[i].next) {
	entry = VATTR(key).af_uhtab[i].next;
	do {
	  af_free (key->af_ldes, entry->symbol);
	  af_free (key->af_ldes, (char *)entry); 
	}
	while ((entry = entry->next));
      }
      if (VATTR(key).af_uhtab[i].symbol)
	af_free (key->af_ldes, VATTR(key).af_uhtab[i].symbol);
    }
    af_free (key->af_ldes, (char *)VATTR(key).af_uhtab);
    VATTR(key).af_uhtab = NULL;
  }
  else {
    for (i=0; i<AF_UDANUM; i++) {
      if (VATTR(key).af_udalist[i] == NULL)
	continue;
      af_free (key->af_ldes, VATTR(key).af_udalist[i]);
      VATTR(key).af_udalist[i] = NULL;
    }
  }
  VATTR(key).af_udanum = 0;
  return (AF_OK);
}

/*=====================================================================
 * afCopyUdas -- Copy list of user defined attributes
 *=====================================================================*/ 

EXPORT int afCopyUdas (srckey, destkey)
     Af_key *srckey, *destkey;
{
  int i;
  char *udalist[AF_MAXUDAS+1];

  /* copy hashtable */
  afListUdas (srckey, udalist);
  i=0;
  while (udalist[i]) {
    afEnterUda (destkey, udalist[i]);
    i++;
  }
  VATTR(destkey).af_udanum = VATTR(srckey).af_udanum;
  return (AF_OK);
}

/*===============================================================
 * afAttrNameCmp -- check two attributes for name equality
 *           Return values like strcmp.
 *==============================================================*/

EXPORT int afAttrNameCmp (str1, str)
     char *str1, *str;
{
  while (*str1 == *str) {
    if ((*str1 == AF_UDANAMDEL) || (*str1 == '\0'))
      return (0);
    str1++;
    str++;
  }
  if (((*str1 == AF_UDANAMDEL) && (*str == '\0')) || ((*str1 == '\0') && (*str == AF_UDANAMDEL)))
    return (0);
  else
    return (*str1 - *str);
}

/**** DEBUG **** DEBUG **** DEBUG **** DEBUG **** DEBUG **** DEBUG ****/

/*=====================================================================
 * af_dumphtb -- dump hashtable
 *=====================================================================*/ 

EXPORT void af_dumphtb (htab) /* for debug purposes */
     Af_hashent *htab;
{
  register int i;
  register Af_hashent *h;

  for (i = 0; i < AF_UDAHASHSIZE; i++) {
    if (htab[i].symbol[0]) {
      h = &htab[i];
      while (h) {
	fprintf (stderr, "\nsymbol: (%d) %s", i, h->symbol);
	h = h->next;
      }
    }
    else fprintf (stderr, ".");
  }
}


