/*
** mg_dict_str.c for  in 
** 
** Made by vianney rancurel
** Login   <vianney@epita.fr>
** 
** Started on  Wed Aug 25 12:16:07 1999 vianney rancurel
** Last update Thu Oct 28 20:16:18 1999 
*/
#include "mg.h"

/* adds-or-overrides a record associated to a string object.
   Old value is freed and new value is strduped. There is no check
   if old value and new value are identical.
   Returns 0 if OK. Might returns various errors */
t_status		dict_str_override(dict,key,value)
t_dict			*dict;
char			*key;
char			*value;
{
  t_hash_elt		*he;
  char			*nvalue;
  t_status		status;
  
  if ((nvalue = strdup_alloc(value,
			     dict->ht->alloc_proc,
			     dict->ht->comment,
			     "dict_str_override:value",
			     &status)) == NULL)
    return (status);
  if (he = dict_get(dict,key))
    {
      dict->ht->free_proc(he->value,
			  dict->ht->comment,
			  "*:value");
      he->value = nvalue;
      return (0);
    }
  else
    if ((status = dict_add(dict,key,nvalue)) != 0)
      {
	dict->ht->free_proc(nvalue,
			    dict->ht->comment,
			    "dict_str_override:value");
	return (status);
      }
  return (0);
}

/* adds-or-overrides a record associated to a string object.
   Old value is freed and new value is strduped. There is no check
   if old value and new value are identical. Note that len bytes will be
   bcopied (there is no check for a zero in str).
   Returns 0 if OK. Might returns various errors */
t_status		dict_str_override_with_len(dict,key,str,len)
t_dict			*dict;
char			*key;
char			*str;/* Might not be null-terminated because of len*/
int			len;
{
  t_hash_elt		*he;
  char			*nvalue;
  t_status		status;
  
  if ((nvalue = dict->ht->alloc_proc(len + 1,
				     dict->ht->comment,
				     "dict_str_override:value",
				     &status)) == NULL)
    return (status);
  bcopy(str,nvalue,len);
  if (he = dict_get(dict,key))
    {
      dict->ht->free_proc(he->value,
			  dict->ht->comment,
			  "*:value");
      he->value = nvalue;
      return (0);
    }
  else
    if ((status = dict_add(dict,key,nvalue)) != 0)
      {
	dict->ht->free_proc(nvalue,
			    dict->ht->comment,
			    "dict_str_override:value");
	return (status);
      }
  return (0);
}

/* adds a record associated to a string object.
   New value is strduped.
   Returns 0 if OK. Returns ERR_MG_EXIST if the key already exists.
   Might also returns various errors */
t_status		dict_str_add(dict,key,value)
t_dict			*dict;
char			*key;
char			*value;
{
  t_hash_elt		*he;
  t_status		status;
  
  if (he = dict_get(dict,key))
    return (ERR_MG_EXIST);
  else
    {
      char	*nvalue;
      
      if ((nvalue = strdup_alloc(value,
				 dict->ht->alloc_proc,
				 dict->ht->comment,
				 "dict_str_add:value",
				 &status)) == NULL)
	return (status);
      if ((status = dict_add(dict,key,nvalue)) != 0)
	{
	  dict->ht->free_proc(nvalue,
			      dict->ht->comment,
			      "dict_str_add:value");
	  return (status);
	}
    }
  return (0);
}

/* removes a record associated to key and also frees the value.
   Returns 0 if OK, ERR_MG_NOENT if key doesn't exist */
t_status		dict_str_rm(dict,key)
t_dict			*dict;
char			*key;
{
  t_hash_elt		*he;

  if ((he = dict_get(dict,key)) == NULL)
    return (ERR_MG_NOENT);
  dict->ht->free_proc(he->value,
		      dict->ht->comment,
		      "*:value");
  return (dict_rm(dict,key));
}

/* is a t_hash_destroy_proc.
   It is used internally by dict_str_delete(3). */
VOID_FUNC		dict_str_destroy_elt(he,dict)
t_hash_elt		*he;
t_dict			*dict;
{
  dict->ht->free_proc(he->key,
		      dict->ht->comment,
		      "*:key");
  dict->ht->free_proc(he->value,
		      dict->ht->comment,
		      "*:value");
}

/* deletes the string objects, the hash table and the dictionary structure. */
VOID_FUNC		dict_str_delete(dict)
t_dict			*dict;
{
  t_free_proc		free_proc;
  char			*comment;

  free_proc = dict->ht->free_proc;
  comment = dict->ht->comment;
  hash_delete(dict->ht,
	      (t_hash_destroy_proc)dict_str_destroy_elt,
	      dict);
  free_proc(dict,
	    comment,
	    "*:dict");
}

/* gets the string object associated with key.
   Returns NULL if not found, assuming a string object is never NULL */
char			*dict_str_get(dict,key)
t_dict			*dict;
char			*key;
{
  t_hash_elt		*he;

  if ((he = dict_get(dict,key)) == NULL)
    return (NULL);
  return (he->value);
}

/* is a t_dict_walk_proc.
   It is used internally by dict_str_copy_override(3). */
t_status		dict_str_copy_override_walk(he,dst)
t_hash_elt		*he;
t_dict			*dst;
{
  return (dict_str_override(dst,he->key,he->value));
}

/* destructive-copy objects from dictionay src to dictionary dst.
   Note that dst must pre-exist and can contain objects before the call
   to this function.
   Returns 0 if OK. Might returns various errors */
t_status		dict_str_copy_override(src,dst)
t_dict			*src;
t_dict			*dst;
{
  return (dict_walk(src,
		    (t_dict_walk_proc)dict_str_copy_override_walk,
		    dst));
}

/* is a t_dict_walk_proc.
   It is used internally by dict_str_copy_silently(3). */
t_status		dict_str_copy_silently_walk(he,dst)
t_hash_elt		*he;
t_dict			*dst;
{
  t_status		status;

  if ((status = dict_str_add(dst,he->key,he->value)) != 0)
    {
      if (status == ERR_MG_EXIST)
	return (0);
      else
	return (status);
    }
}

/* silent-copy objects from dictionay src to dictionary dst.
   If a key from src pre-exists in dst, then it is not overrided and does
   not generate an error.
   Note that dst must pre-exist and can contain objects before the call
   to this function.
   Returns 0 if OK. Might returns various errors */
t_status		dict_str_copy_silently(src,dst)
t_dict			*src;
t_dict			*dst;
{
  return (dict_walk(src,
		    (t_dict_walk_proc)dict_str_copy_silently_walk,
		    dst));
}

/* is a t_dict_walk_proc.
   It is used internally by dict_str_copy_override_suffix(3). */
t_status		dict_str_copy_override_suffix_walk(he,dsd)
t_hash_elt		*he;
t_dict_str_data		*dsd;
{
  t_status		status;
  char			*new_key;
  int			size;
  int			suffix_len;

  suffix_len = strlen(dsd->str);
  size = suffix_len + strlen(he->key) + 1;
  if ((new_key = dsd->dict->ht->alloc_proc(size,
					   dsd->dict->ht->comment,
				    "dict_str_copy_override_suffix_walk:key",
					   &status)) == NULL)
    return (status);
  strcpy(new_key,dsd->str);
  strcpy(new_key + suffix_len,he->key);
  status = dict_str_override(dsd->dict,new_key,he->value);
  dsd->dict->ht->free_proc(new_key,
			   dsd->dict->ht->comment,
			   "dict_str_copy_override_suffix_walk:key");
  return (status);
}

/* destructive-copy from src to dst, appending suffix to keys.
   E.g. if src contains the keys {foo, bar}, and suffix is "boom", dst
   will contain keys {fooboom, barboom}.
   Returns 0 if OK, might return various errors */
t_status		dict_str_copy_override_suffix(src,dst,suffix)
t_dict			*src;
t_dict			*dst;
char			*suffix;
{
  t_dict_str_data	dsd;

  dsd.dict = dst;
  dsd.str = suffix;
  return (dict_walk(src,
		    (t_dict_walk_proc)dict_str_copy_override_suffix_walk,
		    &dsd));
}

/* is a t_dict_walk_proc.
   It is used internally by dict_str_get_sub_prefix(3) */
t_status		dict_str_get_sub_prefix_walk(he,dsd)
t_hash_elt		*he;
t_dict_str_data		*dsd;
{
  int			prefix_len;

  prefix_len = strlen(dsd->str);
  if (!strncmp(he->key,dsd->str,prefix_len))
    return (dict_str_override(dsd->dict,
			      (char *)(he->key) + prefix_len,
			      he->value));
  return (0);
}

/* collects records from src which have "prefix" to dst.
   Returns 0 if OK, might return various errors */
t_status		dict_str_get_sub_prefix(src,dst,prefix)
t_dict			*src;
t_dict			*dst;
char			*prefix;
{
  t_dict_str_data	dsd;

  dsd.dict = dst;
  dsd.str = prefix;
  return (dict_walk(src,
		    (t_dict_walk_proc)dict_str_get_sub_prefix_walk,
		    &dsd));
}

/* is a t_dict_walk_proc.
   It is used internally by dict_str_get_sub_prefix_to_vec_str(3). */
t_status		dict_str_get_sub_prefix_to_vec_str_walk(he,vsd)
t_hash_elt		*he;
t_vec_str_data		*vsd;
{
  int			prefix_len;

  prefix_len = strlen(vsd->str);
  if (!strncmp(he->key,vsd->str,prefix_len))
    return (vec_str_add(vsd->vec,
			he->value));
  return (0);
}

/* collects records from src which have "prefix" to a vec_str.
   It is the vector version of dict_str_get_sub_prefix(3).
   Returns 0 if OK, might return various errors. */
t_status		dict_str_get_sub_prefix_to_vec_str(src,dst,prefix)
t_dict			*src;
t_vec			*dst;		/* Vec_str */
char			*prefix;
{
  t_vec_str_data	vsd;

  vsd.vec = dst;
  vsd.str = prefix;
  return (dict_walk(src,
		    (t_dict_walk_proc)dict_str_get_sub_prefix_to_vec_str_walk,
		    &vsd));
}

/* collects-and-sorts records from src which have "prefix" to a vec_str.
   It is the sorted version of dict_str_get_sub_prefix_to_vec_str(3).
   Returns 0 if OK, might return various errors. */
t_status		dict_str_get_sub_prefix_to_vec_str_sorted(src,
								  dst,
								  prefix)
t_dict			*src;
t_vec			*dst;
char			*prefix;
{
  t_vec_str_data	vsd;

  vsd.vec = dst;
  vsd.str = prefix;
  return (dict_walk_sorted(src,
		   (t_dict_walk_proc)dict_str_get_sub_prefix_to_vec_str_walk,
			   &vsd));
}

/* splits a string into a dictionary of strings.
   Str is in the form key1[=str1],key2[=str2],...keyn[=strn] assuming
   sep is comma. If no equal sign is found, value is assumed a zero string.
   Returns 0 if OK, might return various errors. */
t_status		dict_str_split(dict,str,sep)
t_dict			*dict;
char			*str;
int			sep;	/* Separator, e.g. comma */
{
  t_vec			*vec_str;
  t_status		status;

  if ((vec_str = vec_new(VEC_BASE,
			 FALSE,
			 dict->ht->alloc_algorithm_proc,
			 dict->ht->alloc_proc,
			 dict->ht->realloc_proc,
			 dict->ht->free_proc,
			 dict->ht->comment,
			 &status)) == NULL)
    return (status);
  if ((status = vec_str_split(vec_str,str,sep)) != 0)
    {
      vec_str_delete(vec_str);
      return (status);
    }
  VEC_FOR(vec_str,char *str)
    {
      char		*value;

      if ((value = index(str,'=')) == NULL)
	value = "";
      else
	*value++ = 0;
      if ((status = dict_str_override(dict,str,value)) != 0)
	{
	  vec_str_delete(vec_str);
	  return (status);
	}
    }
  VEC_ENDFOR;
  vec_str_delete(vec_str);
  return (0);
}

#ifdef DEBUG
/* is a t_dict_walk_proc.
   It is used internally by dict_str_show(3). */
t_status		dict_str_show_walk(he)
t_hash_elt		*he;
{
  fprintf(stderr,"%s=%s\n",he->key,he->value);
  return (0);
}

/* shows a dictionary of strings.
   It is a debug function. */
VOID_FUNC		dict_str_show(dict)
t_dict			*dict;
{
  dict_walk(dict,
	    (t_dict_walk_proc)dict_str_show_walk,
	    NULL);
}
#endif

