Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
/me is stupid. Making a directory called core breaks the make clean because it tries...
authormquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Thu, 29 Jan 2004 18:26:10 +0000 (18:26 +0000)
committermquinson <mquinson@48e7efb5-ca39-0410-a469-dd3cf9ba447f>
Thu, 29 Jan 2004 18:26:10 +0000 (18:26 +0000)
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/simgrid/simgrid/trunk@22 48e7efb5-ca39-0410-a469-dd3cf9ba447f

src/xbt/config.c [new file with mode: 0644]
src/xbt/dict.c [new file with mode: 0644]
src/xbt/dict_cursor.c [new file with mode: 0644]
src/xbt/dict_elm.c [new file with mode: 0644]
src/xbt/dict_multi.c [new file with mode: 0644]
src/xbt/dict_private.h [new file with mode: 0644]
src/xbt/dynar.c [new file with mode: 0644]
src/xbt/error.c [new file with mode: 0644]
src/xbt/log.c [new file with mode: 0644]
src/xbt/log_default_appender.c [new file with mode: 0644]
src/xbt/module.c [new file with mode: 0644]

diff --git a/src/xbt/config.c b/src/xbt/config.c
new file mode 100644 (file)
index 0000000..f74b848
--- /dev/null
@@ -0,0 +1,1088 @@
+/* $Id$ */
+
+/* config - Dictionnary where the type of each cell is provided.            */
+
+/* This is useful to build named structs, like option or property sets.     */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2001,2002,2003,2004 the OURAGAN project.                   */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h" /* prototypes of this module */
+
+GRAS_LOG_NEW_DEFAULT_CATEGORY(config);
+
+/* gras_cfgelm_t: the typedef corresponding to a config cell. 
+
+   Both data and DTD are mixed, but fixing it now would prevent me to ever
+   defend my thesis. */
+
+typedef struct {
+  /* Allowed type of the cell */
+  gras_cfgelm_type_t type;
+  int min,max;
+
+  /* actual content 
+     (cannot be an union because type host uses both str and i) */
+  gras_dynar_t *content;
+} gras_cfgelm_t;
+
+static const char *gras_cfgelm_type_name[gras_cfgelm_type_count]=
+  {"int","double","string","host"};
+
+/* Internal stuff used in cache to free a cell */
+static void gras_cfgelm_free(void *data);
+
+/* Retrieve the cell we'll modify */
+static gras_error_t gras_cfgelm_get(gras_cfg_t *cfg, const char *name,
+                                   gras_cfgelm_type_t type,
+                                   /* OUT */ gras_cfgelm_t **whereto);
+
+void gras_cfg_str_free(void *d);
+void gras_cfg_host_free(void *d);
+
+void gras_cfg_str_free(void *d){
+  free(*(void**)d);
+}
+void gras_cfg_host_free(void *d){
+  gras_host_t *h=(gras_host_t*) *(void**)d; 
+  if (h) {
+    if (h->name) free(h->name);
+    free(h);
+  }
+}
+
+/*----[ Memory management ]-----------------------------------------------*/
+
+/**
+ * gras_cfg_new:
+ *
+ * @whereto: 
+ *
+ * Initialise an config set
+ */
+
+
+gras_error_t gras_cfg_new(gras_cfg_t **whereto) {
+  return gras_dict_new((gras_dict_t**)whereto);
+}
+
+/**
+ * gras_cfg_cpy:
+ *
+ * @whereto: the config set to be created
+ * @tocopy: the source data
+ *
+ */
+
+gras_error_t
+gras_cfg_cpy(gras_cfg_t **whereto, gras_cfg_t *tocopy) {
+  gras_dict_cursor_t *cursor=NULL; 
+  gras_cfgelm_t *cell=NULL;
+  char *name=NULL;
+  int errcode=no_error;
+  
+  *whereto=NULL;
+  gras_assert0(tocopy,"cannot copy NULL config");
+
+  gras_dict_foreach((gras_dict_t*)tocopy,cursor,name,cell) {
+    if ((errcode=gras_cfg_register(*whereto, name, cell->type, 
+                                  cell->min, cell->max))         != no_error) {
+      if (cursor)   gras_dict_cursor_free(cursor);
+      if (*whereto) gras_cfg_free(whereto);
+      return errcode;
+    }
+  }
+
+  return errcode;
+}
+
+void gras_cfg_free(gras_cfg_t **cfg) {
+  gras_dict_free((gras_dict_t**)cfg);
+}
+
+/**
+ * gras_cfg_dump:
+ * @name: The name to give to this config set
+ * @indent: what to write at the begining of each line (right number of spaces)
+ * @cfg: the config set
+ *
+ * Dumps a config set for debuging purpose
+ */
+void
+gras_cfg_dump(const char *name,const char *indent,gras_cfg_t *cfg) {
+  gras_dict_t *dict = (gras_dict_t*) cfg;
+  gras_dict_cursor_t *cursor=NULL; 
+  gras_cfgelm_t *cell=NULL;
+  char *key=NULL;
+  int i; 
+  gras_error_t errcode;
+  int size;
+  int ival;
+  char *sval;
+  double dval;
+  gras_host_t hval;
+
+  if (name)
+    printf("%s>> Dumping of the config set '%s':\n",indent,name);
+  gras_dict_foreach(dict,cursor,key,cell) {
+    if ((errcode=gras_dict_cursor_get_key(cursor,(char**)&name))  != no_error ||
+       (errcode=gras_dict_cursor_get_data(cursor,(void**)&cell)) != no_error) {
+      gras_dict_cursor_free(cursor);
+      return;
+    }
+
+    printf("%s  %s:",indent,key);
+    fflush(stdout);
+
+    size = gras_dynar_length(cell->content);
+    printf("%d_to_%d_%s. Actual size=%d. List of values:\n",
+          cell->min,cell->max,gras_cfgelm_type_name[cell->type],
+          size);
+
+    switch (cell->type) {
+    case gras_cfgelm_int:
+      for (i=0; i<size; i++) {
+       gras_dynar_get(cell->content,i,&ival);
+       printf ("%s    %d\n",indent,ival);
+      }
+      break;
+
+    case gras_cfgelm_double:
+      for (i=0; i<size; i++) {
+       gras_dynar_get(cell->content,i,&dval);
+       printf ("%s    %f\n",indent,dval);
+      }
+      break;
+
+    case gras_cfgelm_string:
+      for (i=0; i<size; i++) {
+       gras_dynar_get(cell->content,i,&sval);
+       printf ("%s    %s\n",indent,sval);
+      }
+      break;
+
+    case gras_cfgelm_host:
+      for (i=0; i<size; i++) {
+       gras_dynar_get(cell->content,i,&hval);
+       printf ("%s    %s:%d\n",indent,hval.name,hval.port);
+      }
+      break;
+
+    default:
+      printf("%s    Invalid type!!\n",indent);
+    }
+
+  }
+
+  if (name) printf("%s<< End of the config set '%s'\n",indent,name);
+
+  gras_dict_cursor_free(cursor);
+  return;
+}
+
+/**
+ * gras_cfgelm_free:
+ *
+ * @data: the data to be freed (typed as void* to be usable as free funct in dict)
+ *
+ * free an config element
+ */
+
+void gras_cfgelm_free(void *data) {
+  gras_cfgelm_t *c=(gras_cfgelm_t *)data;
+
+  if (!c) return;
+  gras_dynar_free(c->content);
+  free(c);
+}
+
+/*----[ Registering stuff ]-----------------------------------------------*/
+
+/**
+ * gras_cfg_register:
+ * @cfg: the config set
+ * @type: the type of the config element
+ * @min: the minimum
+ * @max: the maximum
+ *
+ * register an element within a config set
+ */
+
+gras_error_t
+gras_cfg_register(gras_cfg_t *cfg,
+                 const char *name, gras_cfgelm_type_t type,
+                 int min, int max){
+  gras_cfgelm_t *res;
+  gras_error_t errcode;
+
+  TRYCATCH(mismatch_error,gras_dict_retrieve((gras_dict_t*)cfg,name,(void**)&res));
+
+  if (errcode != mismatch_error) {
+    WARNING1("Config elem %s registered twice.",name);
+    /* Will be removed by the insertion of the new one */
+  } 
+
+  res=malloc(sizeof(gras_cfgelm_t));
+  if (!res)
+    RAISE_MALLOC;
+
+  res->type=type;
+  res->min=min;
+  res->max=max;
+
+  switch (type) {
+  case gras_cfgelm_int:
+    TRY(gras_dynar_new(&(res->content), sizeof(int), NULL));
+    break;
+
+  case gras_cfgelm_double:
+    TRY(gras_dynar_new(&(res->content), sizeof(double), NULL));
+    break;
+
+  case gras_cfgelm_string:
+   TRY(gras_dynar_new(&(res->content),sizeof(char*),&gras_cfg_str_free));
+   break;
+
+  case gras_cfgelm_host:
+   TRY(gras_dynar_new(&(res->content),sizeof(gras_host_t*),&gras_cfg_host_free));
+   break;
+
+  default:
+    ERROR1("%d is an invalide type code",type);
+  }
+    
+  return gras_dict_insert((gras_dict_t*)cfg,name,res,&gras_cfgelm_free);
+}
+
+/**
+ * gras_cfg_unregister:
+ * 
+ * @cfg: the config set
+ * @name: the name of the elem to be freed
+ * 
+ * unregister an element from a config set. 
+ * Note that it removes both the DTD and the actual content.
+ */
+
+gras_error_t
+gras_cfg_unregister(gras_cfg_t *cfg,const char *name) {
+  return gras_dict_remove((gras_dict_t*)cfg,name);
+}
+
+/**
+ * gras_cfg_register_str:
+ *
+ * @cfg: the config set
+ * @entry: a string describing the element to register
+ *
+ * Parse a string and register the stuff described.
+ */
+
+gras_error_t
+gras_cfg_register_str(gras_cfg_t *cfg,const char *entry) {
+  char *entrycpy=strdup(entry);
+  char *tok;
+
+  int min,max;
+  gras_cfgelm_type_t type;
+
+  gras_error_t errcode;
+
+  tok=strchr(entrycpy, ':');
+  if (!tok) {
+    ERROR3("%s%s%s",
+         "Invalid config element descriptor: ",entry,
+         "; Should be <name>:<min nb>_to_<max nb>_<type>");
+    free(entrycpy);
+    gras_abort();
+  }
+  *(tok++)='\0';
+
+  min=strtol(tok, &tok, 10);
+  if (!tok) {
+    ERROR1("Invalid minimum in config element descriptor %s",entry);
+    free(entrycpy);
+    gras_abort();
+  }
+
+  if (!strcmp(tok,"_to_")){
+    ERROR3("%s%s%s",
+         "Invalid config element descriptor: ",entry,
+         "; Should be <name>:<min nb>_to_<max nb>_<type>");
+    free(entrycpy);
+    gras_abort();
+  }
+  tok += strlen("_to_");
+
+  max=strtol(tok, &tok, 10);
+  if (!tok) {
+    ERROR1("Invalid maximum in config element descriptor %s",entry);
+    free(entrycpy);
+    gras_abort();
+  }
+
+  if (*(tok++)!='_') {
+    ERROR3("%s%s%s",
+         "Invalid config element descriptor: ",entry,
+         "; Should be <name>:<min nb>_to_<max nb>_<type>");
+    free(entrycpy);
+    gras_abort();
+  }
+
+  for (type=0; 
+       type<gras_cfgelm_type_count && strcmp(tok,gras_cfgelm_type_name[type]); 
+       type++);
+  if (type == gras_cfgelm_type_count) {
+    ERROR3("%s%s%s",
+         "Invalid type in config element descriptor: ",entry,
+         "; Should be one of 'string', 'int', 'host' or 'double'.");
+    free(entrycpy);
+    gras_abort();
+  }
+
+  TRYCLEAN(gras_cfg_register(cfg,entrycpy,type,min,max),
+          free(entrycpy));
+
+  free(entrycpy); /* strdup'ed by dict mechanism, but cannot be const */
+  return no_error;
+}
+
+/**
+ * gras_cfg_check:
+ *
+ * @cfg: the config set
+ * 
+ * Check the config set
+ */
+
+gras_error_t
+gras_cfg_check(gras_cfg_t *cfg) {
+  gras_dict_cursor_t *cursor; 
+  gras_cfgelm_t *cell;
+  char *name;
+  int size;
+
+  gras_assert0(cfg,"NULL config set.");
+
+  gras_dict_foreach((gras_dict_t*)cfg,cursor,name,cell) {
+    size = gras_dynar_length(cell->content);
+    if (cell->min > size) { 
+      ERROR4("Config elem %s needs at least %d %s, but there is only %d values.",
+            name,
+            cell->min,
+            gras_cfgelm_type_name[cell->type],
+            size);
+      return mismatch_error;
+    }
+
+    if (cell->max < size) {
+      ERROR4("Config elem %s accepts at most %d %s, but there is %d values.",
+            name,
+            cell->max,
+            gras_cfgelm_type_name[cell->type],
+            size);
+      return mismatch_error;
+    }
+
+  }
+
+  return no_error;
+}
+
+static gras_error_t gras_cfgelm_get(gras_cfg_t *cfg,
+                                   const char *name,
+                                   gras_cfgelm_type_t type,
+                                   /* OUT */ gras_cfgelm_t **whereto){
+  gras_error_t errcode;
+
+  TRYCATCH(mismatch_error,gras_dict_retrieve((gras_dict_t*)cfg,name,(void**)whereto));
+
+  if (errcode == mismatch_error) {
+    ERROR1("No registered cell %s in this config set",
+          name);
+    return mismatch_error;
+  }
+
+  gras_assert3((*whereto)->type == type,
+              "You tried to access to the config element %s as an %s, but its type is %s.",
+              name,
+              gras_cfgelm_type_name[type],
+              gras_cfgelm_type_name[(*whereto)->type]);
+
+  return no_error;
+}
+
+/**
+ * gras_cfg_get_type:
+ *
+ * @cfg: the config set
+ * @name: the name of the element 
+ * @type: the result
+ *
+ * Give the type of the config element
+ */
+
+gras_error_t
+gras_cfg_get_type(gras_cfg_t *cfg, const char *name, 
+                     /* OUT */gras_cfgelm_type_t *type) {
+
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRYCATCH(mismatch_error,gras_dict_retrieve((gras_dict_t*)cfg,name,(void**)&cell));
+
+  if (errcode == mismatch_error) {
+    ERROR1("Can't get the type of '%s' since this cell does not exist",
+          name);
+    return mismatch_error;
+  }
+
+  *type=cell->type;
+
+  return no_error;
+}
+
+/*----[ Setting ]---------------------------------------------------------*/
+/** 
+ * gras_cfg_set_vargs(): 
+ * @cfg: config set to fill
+ * @varargs: NULL-terminated list of pairs {(const char*)key, value}
+ *
+ * Add some values to the config set.
+ * @warning: if the list isn't NULL terminated, it will segfault. 
+ */
+gras_error_t
+gras_cfg_set_vargs(gras_cfg_t *cfg, va_list pa) {
+  char *str,*name;
+  int i;
+  double d;
+  gras_cfgelm_type_t type;
+
+  gras_error_t errcode;
+  
+  while ((name=va_arg(pa,char *))) {
+
+    if (!gras_cfg_get_type(cfg,name,&type)) {
+      ERROR1("Can't set the property '%s' since it's not registered",name);
+      return mismatch_error;
+    }
+
+    switch (type) {
+    case gras_cfgelm_host:
+      str = va_arg(pa, char *);
+      i=va_arg(pa,int);
+      TRY(gras_cfg_set_host(cfg,name,str,i));
+      break;
+      
+    case gras_cfgelm_string:
+      str=va_arg(pa, char *);
+      TRY(gras_cfg_set_string(cfg, name, str));
+      break;
+
+    case gras_cfgelm_int:
+      i=va_arg(pa,int);
+      TRY(gras_cfg_set_int(cfg,name,i));
+      break;
+
+    case gras_cfgelm_double:
+      d=va_arg(pa,double);
+      TRY(gras_cfg_set_double(cfg,name,d));
+      break;
+
+    default: 
+      RAISE1(unknown_error,"Config element cell %s not valid.",name);
+    }
+  }
+  return no_error;
+}
+
+/** 
+ * gras_cfg_set():
+ * @cfg: config set to fill
+ * @varargs: NULL-terminated list of pairs {(const char*)key, value}
+ *
+ * Add some values to the config set.
+ * @warning: if the list isn't NULL terminated, it will segfault. 
+ */
+gras_error_t gras_cfg_set(gras_cfg_t *cfg, ...) {
+  va_list pa;
+  gras_error_t errcode;
+
+  va_start(pa,cfg);
+  errcode=gras_cfg_set_vargs(cfg,pa);
+  va_end(pa);
+  return errcode;
+}
+
+/**
+ * gras_cfg_set_parse():
+ * @cfg: config set to fill
+ * @options: a string containing the content to add to the config set. This
+ * is a '\t',' ' or '\n' separated list of cells. Each individual cell is
+ * like "[name]:[value]" where [name] is the name of an already registred 
+ * cell, and [value] conforms to the data type under which this cell was
+ * registred.
+ *
+ * Add the cells described in a string to a config set.
+ */
+
+gras_error_t
+gras_cfg_set_parse(gras_cfg_t *cfg, const char *options) {
+  int i;
+  double d;
+  char *str;
+
+  gras_cfgelm_t *cell;
+  char *optionlist_cpy;
+  char *option,  *name,*val;
+
+  int len;
+  gras_error_t errcode;
+
+  if (!options || !strlen(options)) { /* nothing to do */
+    return no_error;
+  }
+  optionlist_cpy=strdup(options);
+
+  DEBUG1("Options list='%s'",options);
+  option=optionlist_cpy;
+  while (1) { /* breaks in the code */
+
+    if (!option) 
+      break;
+    name=option;
+    len=strlen(name);
+    DEBUG3("Parse list '%s'. len=%d; option-name=%d",name,len,option-name);
+
+    /* Pass the value */
+    while (option-name<=(len-1) && *option != ' ' && *option != '\n' && *option != '\t') {
+      //fprintf(stderr,"Take %c.\n",*option);
+      option++;
+    }
+    if (option-name == len) {
+      //fprintf(stderr,"Boundary=EOL\n");
+      option=NULL; /* don't do next iteration */
+
+    } else {
+      //fprintf(stderr,"Boundary on '%c'. len=%d;option-name=%d\n",*option,len,option-name);
+
+      /* Pass the following blank chars */
+      *(option++)='\0';
+      while (option-name<(len-1) && (*option == ' ' || *option == '\n' || *option == '\t')) {
+       //      fprintf(stderr,"Ignore a blank char.\n");
+       option++;
+      }
+      if (option-name == len-1)
+       option=NULL; /* don't do next iteration */
+    }
+    DEBUG2("this='%s';rest='%s'",name,option);
+
+    if (name[0] == ' ' || name[0] == '\n' || name[0] == '\t')
+      continue;
+    if (!strlen(name))
+      break;
+    
+    val=strchr(name,':');
+    if (!val) {
+      free(optionlist_cpy);
+      gras_assert1(FALSE,
+                  "Malformated option: '%s'; Should be of the form 'name:value'",
+                  name);
+    }
+    *(val++)='\0';
+
+    DEBUG2("name='%s';val='%s'",name,val);
+
+    errcode=gras_dict_retrieve((gras_dict_t*)cfg,name,(void**)&cell);
+    switch (errcode) {
+    case no_error:
+      break;
+    case mismatch_error:
+      ERROR1("No registrated cell corresponding to '%s'.",name);
+      free(optionlist_cpy);
+      return mismatch_error;
+      break;
+    default:
+      free(optionlist_cpy);
+      return errcode;
+    }
+
+    switch (cell->type) {
+    case gras_cfgelm_string:
+      TRYCLEAN(gras_cfg_set_string(cfg, name, val),
+              free(optionlist_cpy));
+      break;
+
+    case gras_cfgelm_int:
+      i=strtol(val, &val, 0);
+      if (val==NULL) {
+       free(optionlist_cpy);   
+       gras_assert1(FALSE,
+                    "Value of option %s not valid. Should be an integer",
+                    name);
+      }
+
+      TRYCLEAN(gras_cfg_set_int(cfg,name,i),
+              free(optionlist_cpy));
+      break;
+
+    case gras_cfgelm_double:
+      d=strtod(val, &val);
+      if (val==NULL) {
+       free(optionlist_cpy);   
+       gras_assert1(FALSE,
+              "Value of option %s not valid. Should be a double",
+              name);
+      }
+
+      TRYCLEAN(gras_cfg_set_double(cfg,name,d),
+              free(optionlist_cpy));
+      break;
+
+    case gras_cfgelm_host:
+      str=val;
+      val=strchr(val,':');
+      if (!val) {
+       free(optionlist_cpy);   
+       gras_assert1(FALSE,
+              "Value of option %s not valid. Should be an host (machine:port)",
+              name);
+      }
+
+      *(val++)='\0';
+      i=strtol(val, &val, 0);
+      if (val==NULL) {
+       free(optionlist_cpy);   
+       gras_assert1(FALSE,
+              "Value of option %s not valid. Should be an host (machine:port)",
+              name);
+      }
+
+      TRYCLEAN(gras_cfg_set_host(cfg,name,str,i),
+              free(optionlist_cpy));
+      break;      
+
+    default: 
+      free(optionlist_cpy);
+      RAISE1(unknown_error,"Type of config element %s is not valid.",name);
+    }
+    
+  }
+  free(optionlist_cpy);
+  return no_error;
+}
+
+/**
+ * gras_cfg_set_int:
+ *
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the value of the cell
+ *
+ * Set the value of the cell @name in @cfg with the provided value.
+ */ 
+gras_error_t
+gras_cfg_set_int(gras_cfg_t *cfg,const char*name, int val) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
+
+  if (cell->max > 1) {
+    return gras_dynar_push(cell->content,&val);
+  } else {
+    return gras_dynar_set(cell->content,0,&val);
+  }
+}
+
+/**
+ * gras_cfg_set_double:
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the doule to set
+ * 
+ * Set the value of the cell @name in @cfg with the provided value.
+ */ 
+
+gras_error_t
+gras_cfg_set_double(gras_cfg_t *cfg,const char*name, double val) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
+
+  if (cell->max > 1) {
+    return gras_dynar_push(cell->content,&val);
+  } else {
+    return gras_dynar_set(cell->content,0,&val);
+  }
+}
+
+/**
+ * gras_cfg_set_string:
+ * 
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the value to be added
+ *
+ * Set the value of the cell @name in @cfg with the provided value.
+ */ 
+
+gras_error_t
+gras_cfg_set_string(gras_cfg_t *cfg,const char*name, const char*val) { 
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
+
+  if (cell->max > 1) {
+    return gras_dynar_push(cell->content,&val);
+  } else {
+    return gras_dynar_set(cell->content,0,&val);
+  }
+}
+
+/**
+ * gras_cfg_set_host:
+ * 
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @host: the host
+ * @port: the port number
+ *
+ * Set the value of the cell @name in @cfg with the provided value 
+ * on the given @host to the given @port
+ */ 
+
+gras_error_t 
+gras_cfg_set_host(gras_cfg_t *cfg,const char*name, 
+                 const char *host,int port) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+  gras_host_t *val=malloc(sizeof(gras_host_t));
+
+  if (!val)
+    RAISE_MALLOC;
+  val->name = strdup(name);
+  val->port = port;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
+
+  if (cell->max > 1) {
+    return gras_dynar_push(cell->content,&val);
+  } else {
+    return gras_dynar_set(cell->content,0,&val);
+  }
+}
+
+/* ---- [ Removing ] ---- */
+
+/**
+ * gras_cfg_rm_int:
+ *
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the value to be removed
+ *
+ * Remove the provided @val from the cell @name in @cfg.
+ */
+gras_error_t gras_cfg_rm_int   (gras_cfg_t *cfg,const char*name, int val) {
+
+  gras_cfgelm_t *cell;
+  int cpt,seen;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
+  
+  gras_dynar_foreach(cell->content,cpt,seen) {
+    if (seen == val) {
+      gras_dynar_cursor_rm(cell->content,&cpt);
+      return no_error;
+    }
+  }
+
+  ERROR2("Can't remove the value %d of config element %s: value not found.",
+        val,name);
+  return mismatch_error;
+}
+
+/**
+ * gras_cfg_rm_double:
+ *
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the value to be removed
+ *
+ * Remove the provided @val from the cell @name in @cfg.
+ */
+
+gras_error_t gras_cfg_rm_double(gras_cfg_t *cfg,const char*name, double val) {
+  gras_cfgelm_t *cell;
+  int cpt;
+  double seen;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
+  
+  gras_dynar_foreach(cell->content,cpt,seen) {
+    if (seen == val) {
+      gras_dynar_cursor_rm(cell->content,&cpt);
+      return no_error;
+    }
+  }
+
+  ERROR2("Can't remove the value %f of config element %s: value not found.",
+        val,name);
+  return mismatch_error;
+}
+
+/**
+ * gras_cfg_rm_string:
+ *
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the value of the string which will be removed
+ *
+ * Remove the provided @val from the cell @name in @cfg.
+ */
+gras_error_t
+gras_cfg_rm_string(gras_cfg_t *cfg,const char*name, const char *val) {
+  gras_cfgelm_t *cell;
+  int cpt;
+  char *seen;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
+  
+  gras_dynar_foreach(cell->content,cpt,seen) {
+    if (!strcpy(seen,val)) {
+      gras_dynar_cursor_rm(cell->content,&cpt);
+      return no_error;
+    }
+  }
+
+  ERROR2("Can't remove the value %s of config element %s: value not found.",
+        val,name);
+  return mismatch_error;
+}
+
+/**
+ * gras_cfg_rm_host:
+ * 
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @host: the hostname
+ * @port: the port number
+ *
+ * Remove the provided @host:@port from the cell @name in @cfg.
+ */
+
+gras_error_t
+gras_cfg_rm_host  (gras_cfg_t *cfg,const char*name, const char *host,int port) {
+  gras_cfgelm_t *cell;
+  int cpt;
+  gras_host_t *seen;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
+  
+  gras_dynar_foreach(cell->content,cpt,seen) {
+    if (!strcpy(seen->name,host) && seen->port == port) {
+      gras_dynar_cursor_rm(cell->content,&cpt);
+      return no_error;
+    }
+  }
+
+  ERROR3("Can't remove the value %s:%d of config element %s: value not found.",
+        host,port,name);
+  return mismatch_error;
+}
+
+/* rm everything */
+
+/**
+ * gras_cfg_empty:
+ * 
+ * @cfg: the config set
+ * @name: the name of the cell
+ *
+ * rm evenything
+ */
+
+gras_error_t 
+gras_cfg_empty(gras_cfg_t *cfg,const char*name) {
+  gras_cfgelm_t *cell;
+
+  gras_error_t errcode;
+
+  TRYCATCH(mismatch_error,
+          gras_dict_retrieve((gras_dict_t*)cfg,name,(void**)&cell));
+  if (errcode == mismatch_error) {
+    ERROR1("Can't empty  '%s' since this config element does not exist",
+          name);
+    return mismatch_error;
+  }
+
+  if (cell) {
+    gras_dynar_reset(cell->content);
+  }
+  return no_error;
+}
+
+/*----[ Getting ]---------------------------------------------------------*/
+
+/**
+ * gras_cfg_get_int:
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the wanted value
+ *
+ * Returns the first value from the config set under the given name.
+ * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar() 
+ * instead.
+ *
+ * @warning the returned value is the actual content of the config set
+ */
+gras_error_t
+gras_cfg_get_int   (gras_cfg_t  *cfg, 
+                   const char *name,
+                   int        *val) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
+
+  if (gras_dynar_length(cell->content) > 1) {
+    WARNING2("You asked for the first value of the config element '%s', but there is %d values\n",
+            name, gras_dynar_length(cell->content));
+  }
+
+  gras_dynar_get(cell->content, 0, (void*)val);
+  return no_error;
+}
+
+/**
+ * gras_cfg_get_double:
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @val: the wanted value
+ *
+ * Returns the first value from the config set under the given name.
+ * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar() 
+ * instead.
+ *
+ * @warning the returned value is the actual content of the config set
+ */
+
+gras_error_t
+gras_cfg_get_double(gras_cfg_t *cfg,
+                   const char *name,
+                   double     *val) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
+
+  if (gras_dynar_length(cell->content) > 1) {
+    WARNING2("You asked for the first value of the config element '%s', but there is %d values\n",
+            name, gras_dynar_length(cell->content));
+  }
+
+  gras_dynar_get(cell->content, 0, (void*)val);
+  return no_error;
+}
+
+/**
+ * gras_cfg_get_string:
+ *
+ * @th: the config set
+ * @name: the name of the cell
+ * @val: the wanted value
+ *
+ * Returns the first value from the config set under the given name.
+ * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar() 
+ * instead.
+ *
+ * @warning the returned value is the actual content of the config set
+ */
+
+gras_error_t gras_cfg_get_string(gras_cfg_t *cfg,
+                                const char *name,
+                                char      **val) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  *val=NULL;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
+
+  if (gras_dynar_length(cell->content) > 1) {
+    WARNING2("You asked for the first value of the config element '%s', but there is %d values\n",
+            name, gras_dynar_length(cell->content));
+  }
+
+  gras_dynar_get(cell->content, 0, (void*)val);
+  return no_error;
+}
+
+/**
+ * gras_cfg_get_host:
+ *
+ * @cfg: the config set
+ * @name: the name of the cell
+ * @host: the host
+ * @port: the port number
+ *
+ * Returns the first value from the config set under the given name.
+ * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar() 
+ * instead.
+ *
+ * @warning the returned value is the actual content of the config set
+ */
+
+gras_error_t gras_cfg_get_host  (gras_cfg_t *cfg,
+                                const char *name,
+                                char      **host,
+                                int        *port) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+  gras_host_t *val;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
+
+  if (gras_dynar_length(cell->content) > 1) {
+    WARNING2("You asked for the first value of the config element '%s', but there is %d values\n",
+            name, gras_dynar_length(cell->content));
+  }
+
+  gras_dynar_get(cell->content, 0, (void*)val);
+  *host=val->name;
+  *port=val->port;
+  
+  return no_error;
+}
+
+/**
+ * gras_cfg_get_dynar:
+ * @cfg: where to search in
+ * @name: what to search for
+ * @dynar: result
+ *
+ * Get the data stored in the config bag. 
+ *
+ * @warning the returned value is the actual content of the config set
+ */
+gras_error_t gras_cfg_get_dynar (gras_cfg_t    *cfg,
+                                const char    *name,
+                                gras_dynar_t **dynar) {
+  gras_cfgelm_t *cell;
+  gras_error_t errcode;
+
+  TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
+  *dynar = cell->content;
+  return no_error;
+}
+
diff --git a/src/xbt/dict.c b/src/xbt/dict.c
new file mode 100644 (file)
index 0000000..5ff0537
--- /dev/null
@@ -0,0 +1,206 @@
+/* $Id$ */
+
+/* dict - a generic dictionnary, variation over the B-tree concept          */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003 the OURAGAN project.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+
+#include "gras_private.h"
+#include "dict_private.h"
+
+#include <stdlib.h> /* malloc() */
+#include <string.h> /* strlen() */
+
+#include <stdio.h>
+
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(dict,GRAS);
+
+/*####[ Private prototypes ]#################################################*/
+
+
+/*####[ Code ]###############################################################*/
+
+/**
+ * gras_dict_new:
+ *
+ * @whereto: pointer to the destination
+ *
+ * Creates and initialize a new dictionnary
+ */
+gras_error_t
+gras_dict_new(gras_dict_t **whereto) {
+  gras_dict_t *dict;
+
+  if (!(dict = calloc(1, sizeof(gras_dict_t))))
+    RAISE_MALLOC;
+
+  dict->head=NULL;
+
+  *whereto = dict;
+  
+  return no_error;
+}
+/**
+ * gras_dict_free:
+ * @dict: the dictionnary to be freed
+ *
+ * Frees a cache structure with all its childs.
+ */
+void
+gras_dict_free(gras_dict_t **dict)  {
+  if (dict && *dict) {
+    if ((*dict)->head) {
+      gras_dictelm_free( &( (*dict)->head ) );
+      (*dict)->head = NULL;
+    }
+    free(*dict);
+    *dict=NULL;
+  }
+}
+
+/**
+ * gras_dict_insert_ext:
+ *
+ * @p_dict: the container
+ * @key: the key to insert the new data
+ * @data: the data to add in the dict
+ * @Returns: a gras_error
+ *
+ * Insert the @data in the structure under the @key, which can be any kind 
+ * of data, as long as its length is provided in @key_len.
+ */
+gras_error_t
+gras_dict_insert_ext(gras_dict_t     *p_dict,
+                    const char      *key,
+                    int              key_len,
+                    void            *data,
+                    void_f_pvoid_t  *free_ctn) {
+
+  gras_assert(p_dict);
+
+  return gras_dictelm_insert_ext(&(p_dict->head),
+                                key, key_len, data, free_ctn);
+}
+
+/**
+ * gras_dict_insert:
+ *
+ * @head: the head of the dict
+ * @key: the key to insert the new data
+ * @data: the data to add in the dict
+ * @Returns: a gras_error
+ *
+ * Insert the @data in the structure under the @key, which is a 
+ * null terminated string.
+ */
+gras_error_t
+gras_dict_insert(gras_dict_t    *p_dict,
+                const char     *key,
+                void           *data,
+                void_f_pvoid_t *free_ctn) {
+
+  gras_assert(p_dict);
+  
+  return gras_dictelm_insert(&(p_dict->head), key, data, free_ctn);
+}
+
+/**
+ * gras_dict_retrieve_ext:
+ *
+ * @dict: the dealer of data
+ * @key: the key to find data
+ * @data: the data that we are looking for
+ * @Returns: gras_error
+ *
+ * Search the given @key. mismatch_error when not found.
+ */
+gras_error_t
+gras_dict_retrieve_ext(gras_dict_t    *dict,
+                      const char     *key,
+                      int             key_len,
+                      /* OUT */void **data) {
+
+  gras_assert(dict);
+
+  return gras_dictelm_retrieve_ext(dict->head, key, key_len, data);
+}
+
+/**
+ * gras_dict_retrieve:
+ *
+ * @dict: the dealer of data
+ * @key: the key to find data
+ * @data: the data that we are looking for
+ * @Returns: gras_error
+ *
+ * Search the given @key. mismatch_error when not found.
+ */
+gras_error_t
+gras_dict_retrieve(gras_dict_t    *dict,
+                   const char     *key,
+                   /* OUT */void **data) {
+  gras_assert(dict);
+
+  return gras_dictelm_retrieve(dict->head, key, data);
+}
+
+
+/**
+ * gras_dict_remove_ext:
+ *
+ * @dict: the trash can
+ * @key: the key of the data to be removed
+ * @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ */
+gras_error_t
+gras_dict_remove_ext(gras_dict_t *dict,
+                     const char  *key,
+                     int          key_len) {
+  gras_assert(dict);
+  
+  return gras_dictelm_remove_ext(dict->head, key, key_len);
+}
+
+/**
+ * gras_dict_remove:
+ *
+ * @head: the head of the dict
+ * @key: the key of the data to be removed
+ * @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ */
+gras_error_t
+gras_dict_remove(gras_dict_t *dict,
+                const char  *key) {
+  gras_assert(dict);
+
+  return gras_dictelm_remove(dict->head, key);
+}
+
+
+/**
+ * gras_dict_dump:
+ *
+ * @dict: the exibitionist
+ * @output: a function to dump each data in the tree
+ * @Returns: gras_error_t
+ *
+ * Ouputs the content of the structure. (for debuging purpose). @ouput is a
+ * function to output the data. If NULL, data won't be displayed.
+ */
+
+gras_error_t
+gras_dict_dump(gras_dict_t    *dict,
+               void_f_pvoid_t *output) {
+
+  printf("Dict %p:\n", dict);
+  return gras_dictelm_dump(dict ? dict->head: NULL, output);
+}
+
diff --git a/src/xbt/dict_cursor.c b/src/xbt/dict_cursor.c
new file mode 100644 (file)
index 0000000..a2d12cc
--- /dev/null
@@ -0,0 +1,286 @@
+/* $Id$ */
+
+/* dict_cursor - iterators over dictionnaries                               */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003,2004 Martin Quinson.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+#include "dict_private.h"
+
+#include <stdlib.h> /* malloc() */
+#include <string.h> /* strlen() */
+
+GRAS_LOG_EXTERNAL_CATEGORY(dict);
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(dict_cursor,dict);
+
+
+/*####[ Dict cursor functions ]#############################################*/
+/* To traverse (simple) dicts                                               */
+/* Don't add or remove entries to the dict while traversing !!!             */
+/*###########################################################################*/
+struct gras_dict_cursor_ {
+  /* we store all level encountered until here, to backtrack on next() */
+  gras_dynar_t *keys;
+  gras_dynar_t *key_lens;
+  int           pos;
+  int           pos_len;
+  gras_dictelm_t  *head;
+};
+
+static inline
+gras_error_t
+_cursor_push_keys(gras_dict_cursor_t *p_cursor,
+                  gras_dictelm_t        *p_elm);
+
+#undef gras_dict_CURSOR_DEBUG
+//#define gras_dict_CURSOR_DEBUG 1
+
+/**
+ * gras_dict_cursor_new:
+ *
+ * @head: the head of the dict
+ * @cursor: the curent position in the dict
+ *
+ * Structure creator
+ */
+gras_error_t
+gras_dict_cursor_new(const gras_dict_t          *p_head,
+                     /*OUT*/gras_dict_cursor_t **pp_cursor) {
+  gras_error_t        errcode  = no_error;
+  gras_dict_cursor_t *p_cursor = NULL;
+
+  p_cursor = malloc(sizeof(gras_dict_cursor_t));
+  if (!p_cursor)
+    RAISE_MALLOC;
+
+  TRY(gras_dynar_new(&p_cursor->keys,     sizeof(char **), NULL));
+  TRY(gras_dynar_new(&p_cursor->key_lens, sizeof(int  *),  NULL));
+
+  p_cursor->pos     = 0;
+  p_cursor->pos_len = 0;
+  p_cursor->head    = p_head ? p_head->head : NULL;
+
+  TRY(gras_dict_cursor_rewind(p_cursor));
+
+  *pp_cursor = p_cursor;
+
+  return errcode;
+}
+
+/**
+ * gras_dict_cursor_free:
+ *
+ * @cursor: poor victim
+ *
+ * Structure destructor
+ */
+void
+gras_dict_cursor_free(gras_dict_cursor_t *p_cursor) {
+  if (p_cursor) {
+    gras_dynar_free(p_cursor->keys);
+    gras_dynar_free(p_cursor->key_lens);
+    memset(p_cursor, 0, sizeof(gras_dict_cursor_t));
+    free(p_cursor);
+  }
+}
+
+/**
+ * __cursor_not_null:
+ *
+ * Sanity check to see if the head contains something
+ */
+static inline
+gras_error_t
+__cursor_not_null(gras_dict_cursor_t *p_cursor) {
+
+  gras_assert0(p_cursor, "Null cursor");
+
+  if (!p_cursor->head) {
+    return mismatch_error;
+  }
+
+  return no_error;
+}
+
+
+static inline
+gras_error_t
+_cursor_push_keys(gras_dict_cursor_t *p_cursor,
+                  gras_dictelm_t        *p_elm) {
+  gras_error_t         errcode = no_error;
+  gras_dictelm_t      *p_child = NULL;
+  int                  i       = 0;
+  static volatile int  count   = 0; /* ??? */
+
+  CDEBUG1(dict_cursor, "Push childs of %p in the cursor", p_elm);
+
+  if (p_elm->content) {
+    TRY(gras_dynar_push(p_cursor->keys,     &p_elm->key    ));
+    TRY(gras_dynar_push(p_cursor->key_lens, &p_elm->key_len));
+    count++;
+  }
+
+  gras_dynar_foreach(p_elm->sub, i, p_child) {
+    if (p_child)
+      TRY(_cursor_push_keys(p_cursor, p_child));
+  }
+
+  CDEBUG1(dict_cursor, "Count = %d", count);
+
+  return errcode;
+}
+
+/**
+ * gras_dict_cursor_rewind:
+ * @cursor: the cursor
+ * @Returns: gras_error_t
+ *
+ * back to the first element
+ */
+gras_error_t
+gras_dict_cursor_rewind(gras_dict_cursor_t *p_cursor) {
+  gras_error_t errcode = no_error;
+
+  CDEBUG0(dict_cursor, "gras_dict_cursor_rewind");
+  gras_assert(p_cursor);
+
+  gras_dynar_reset(p_cursor->keys);
+  gras_dynar_reset(p_cursor->key_lens);
+
+  if (!p_cursor->head)
+    return no_error;
+
+  TRY(_cursor_push_keys(p_cursor, p_cursor->head));
+
+  gras_dynar_cursor_first(p_cursor->keys,     &p_cursor->pos    );
+  gras_dynar_cursor_first(p_cursor->key_lens, &p_cursor->pos_len);
+
+  return errcode;
+}
+
+/**
+ * gras_dict_cursor_first:
+ * @dict: on what to let the cursor iterate
+ * @cursor: dest address
+ *
+ * Create the cursor if it does not exists. Rewind it in any case.
+ */
+void         gras_dict_cursor_first    (const gras_dict_t   *dict,
+                                       gras_dict_cursor_t **cursor){
+  gras_error_t errcode;
+
+  if (!*cursor) {
+    DEBUG0("Create the cursor on first use");
+    errcode = gras_dict_cursor_new(dict,cursor);
+    gras_assert1(errcode == no_error, "Unable to create the cursor, got error %s",
+                gras_error_name(errcode));
+  }
+
+  errcode = gras_dict_cursor_rewind(*cursor);
+  gras_assert1(errcode == no_error, "Unable to rewind the cursor before use, got error %s",
+              gras_error_name(errcode));
+}
+
+
+/**
+ * gras_dict_cursor_step:
+ * @cursor: the cursor
+ *
+ * Move to the next element. Returns mismatch_error if no next element.
+ */
+void
+gras_dict_cursor_step(gras_dict_cursor_t *p_cursor) {
+  gras_assert(p_cursor);
+
+  gras_dynar_cursor_step(p_cursor->keys,     &p_cursor->pos);
+  gras_dynar_cursor_step(p_cursor->key_lens, &p_cursor->pos_len);
+}
+
+/**
+ * gras_dict_cursor_get_or_free:
+ * @cursor: the cursor
+ * @Returns: true if it's ok, false if there is no more data
+ *
+ * Get current data
+ */
+int
+gras_dict_cursor_get_or_free(gras_dict_cursor_t **cursor,
+                            char               **key,
+                            void               **data) {
+  gras_error_t  errcode = no_error;
+  int           key_len = 0;
+  
+  if (!cursor || !(*cursor))
+    return FALSE;
+
+  if (gras_dynar_length((*cursor)->keys) <= (*cursor)->pos) {
+    gras_dict_cursor_free(*cursor);
+    *cursor=NULL;
+    return FALSE;
+  }
+    
+  gras_dynar_get((*cursor)->keys,     (*cursor)->pos,     key    );
+  gras_dynar_get((*cursor)->key_lens, (*cursor)->pos_len, &key_len);
+
+  errcode = gras_dictelm_retrieve_ext((*cursor)->head, *key, key_len, data);
+  if (errcode == mismatch_error) {
+    gras_dict_cursor_free(*cursor);
+    *cursor=NULL;
+    return FALSE;
+  }
+
+  gras_assert1(errcode == no_error,
+              "Unexpected problem while retrieving the content of cursor. Got %s",
+              gras_error_name(errcode));
+
+  return TRUE;
+}
+
+/**
+ * gras_dict_cursor_get_key:
+ * @cursor: the cursor
+ * @key: the current element
+ * @Returns: gras_error_t
+ *
+ * Get current key
+ */
+gras_error_t
+gras_dict_cursor_get_key(gras_dict_cursor_t  *p_cursor,
+                         /*OUT*/char        **key) {
+  gras_error_t errcode = no_error;
+
+  TRY(__cursor_not_null(p_cursor));
+
+  gras_dynar_get(p_cursor->keys, p_cursor->pos - 1, key);
+
+  return errcode;
+}
+
+/**
+ * gras_dict_cursor_get_data:
+ * @cursor: the cursor
+ *
+ * Get current data
+ */
+gras_error_t
+gras_dict_cursor_get_data(gras_dict_cursor_t  *p_cursor,
+                          /*OUT*/void        **data) {
+  gras_error_t  errcode = no_error;
+  char         *key     = NULL;
+  int           key_len = 0;
+
+  TRY(__cursor_not_null(p_cursor));
+
+  gras_dynar_get(p_cursor->keys,     p_cursor->pos-1,     &key    );
+  gras_dynar_get(p_cursor->key_lens, p_cursor->pos_len-1, &key_len);
+
+  TRY(gras_dictelm_retrieve_ext(p_cursor->head, key, key_len, data));
+
+  return errcode;
+}
+
+
diff --git a/src/xbt/dict_elm.c b/src/xbt/dict_elm.c
new file mode 100644 (file)
index 0000000..162f7dc
--- /dev/null
@@ -0,0 +1,1010 @@
+/* $Id$ */
+
+/* dict - a generic dictionnary, variation over the B-tree concept          */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003 the OURAGAN project.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+
+#include "gras_private.h"
+#include "dict_private.h"  /* prototypes of this module */
+
+#include <stdlib.h> /* malloc() */
+#include <string.h> /* strlen() */
+
+#include <stdio.h>
+
+GRAS_LOG_EXTERNAL_CATEGORY(dict);
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(dict_elm,dict);
+
+GRAS_LOG_NEW_SUBCATEGORY(dict_add,dict);
+GRAS_LOG_NEW_SUBCATEGORY(dict_search,dict);
+GRAS_LOG_NEW_SUBCATEGORY(dict_remove,dict);
+GRAS_LOG_NEW_SUBCATEGORY(dict_collapse,dict);
+GRAS_LOG_NEW_SUBCATEGORY(dict_multi,dict);
+
+/*####[ Private prototypes ]#################################################*/
+
+static inline gras_error_t _gras_dictelm_alloc(char                *key,
+                                              int                  offset,
+                                              int                  key_len,
+                                              void                *data,
+                                              void_f_pvoid_t      *free_ctn,
+                                              /*OUT*/gras_dictelm_t **where);
+static void         _dictelm_wrapper_free(void*);
+
+static inline void  _str_prefix_lgr(const char *key1,
+                                   int         key_len1,
+                                   const char *key2,
+                                   int         key_len2,
+                                   int        *offset,
+                                   int        *match);
+
+
+static gras_error_t _gras_dictelm_dump_rec(gras_dictelm_t *head,
+                                          int             offset,
+                                          void_f_pvoid_t *output);
+
+
+
+static gras_error_t _gras_dictelm_insert_rec(gras_dictelm_t *head,
+                                            char           *key,
+                                            int             key_len,
+                                            int             offset,
+                                            void           *data,
+                                            void_f_pvoid_t *free_ctn);
+static gras_error_t _gras_dictelm_retrieve_rec(gras_dictelm_t *head,
+                                              const char     *key,
+                                              int             key_len,
+                                              int             offset,
+                                              /* OUT */void **data);
+static gras_error_t _gras_dictelm_remove_rec(gras_dictelm_t *head,
+                                            const char     *key,
+                                            int             key_len,
+                                            int             offset);
+
+static inline
+void
+_collapse_if_need(gras_dictelm_t *p_head,
+                 int             pos,
+                 int             offset);
+
+/* ---- */
+
+static inline
+void *
+memdup(const void * const ptr,
+       const size_t       length) {
+  void * new_ptr = NULL;
+
+  new_ptr = malloc(length);
+
+  if (new_ptr) {
+    memcpy(new_ptr, ptr, length);
+  }
+
+  return new_ptr;
+}
+
+/*
+ * _gras_nibble_to_char:
+ *
+ * Change any byte to a printable char
+ */
+
+static inline
+char
+_gras_nibble_to_char(unsigned char c) {
+  c &= 0x0f;
+  return c>9 ? c-10+'a' : c + '0';
+}
+
+/*
+ * _gras_bytes_to_string:
+ *
+ * Change any byte array to a printable string
+ * The length of string_container should at least be data_len*2+1 
+ */
+static inline
+char *
+_gras_bytes_to_string(char * const ptr,
+                      int          data_len,
+                      char * const string_container) {
+  unsigned char *src = (unsigned char *)ptr;
+           char *dst = string_container;
+
+  while (data_len--) {
+    *dst++ = _gras_nibble_to_char(*src   & 0x0f     );
+    *dst++ = _gras_nibble_to_char(*src++ & 0xf0 >> 4);
+  }
+
+  *dst = 0;
+
+  return ptr;
+}
+
+/* ---- */
+
+/*
+ * _gras_dictelm_alloc:
+ *
+ * Alloc a dict element with no child.
+ */
+static 
+inline
+gras_error_t
+_gras_dictelm_alloc(char                *key,
+                   int                  key_len,
+                   int                  offset,
+                   void                *data,
+                   void_f_pvoid_t      *free_ctn,
+                 /*OUT*/gras_dictelm_t **pp_elm) {
+  gras_error_t   errcode = no_error;
+  gras_dictelm_t *p_elm  = NULL;
+
+  if (!(p_elm = calloc(1, sizeof(gras_dictelm_t)))) {
+    if (free_ctn && data) {
+      free_ctn(data);
+    }
+
+    RAISE_MALLOC;
+  }
+
+  p_elm->key      = key;
+  p_elm->key_len  = key_len;
+  p_elm->offset   = offset;
+  p_elm->content  = data;
+  p_elm->free_ctn = free_ctn;
+
+  errcode = gras_dynar_new(&(p_elm->sub), sizeof(gras_dictelm_t*), 
+                          _dictelm_wrapper_free);
+  if (errcode != no_error) {
+    if (free_ctn && data) {
+      free_ctn(data);
+    }
+    free(p_elm);
+    return errcode;
+  }
+
+  *pp_elm = p_elm;
+
+  return errcode;
+}
+
+/**
+ * gras_dictelm_free:
+ *
+ * @pp_elm: the dict elem to be freed
+ *
+ * Frees a dictionnary element with all its childs.
+ */
+void
+gras_dictelm_free(gras_dictelm_t **pp_elm)  {
+  if (*pp_elm) {
+    gras_dictelm_t *p_elm = *pp_elm;
+
+    gras_dynar_free(p_elm->sub);
+
+    if (p_elm->key) {
+      free(p_elm->key);
+    }
+
+    if (p_elm->free_ctn && p_elm->content) {
+      p_elm->free_ctn(p_elm->content);
+    }
+
+    memset(p_elm, 0, sizeof (*p_elm));
+
+    free(p_elm);
+    *pp_elm = NULL;
+  }
+}
+
+/**
+ * _dictelm_wrapper_free:
+ *
+ * a wrapper to free dictelm with the right prototype to be usable within dynar
+ */
+static
+void
+_dictelm_wrapper_free(void *pp_elm) {
+  DEBUG2("Free dictelm %p (*=%p)", pp_elm, *(void**)pp_elm);
+  gras_dictelm_free((gras_dictelm_t**)pp_elm);
+}
+
+/*####[ utility functions ]##################################################*/
+/**
+ * _str_prefix_lgr:
+ *
+ *
+ * Returns the length of the common prefix of @str1 and @str2.
+ * Do make sure the strings are not null
+ */
+static inline
+void
+_str_prefix_lgr(const char *key1,
+               int         key_len1,
+               const char *key2,
+               int         key_len2,
+               int        *p_offset,
+               int        *p_match) {
+  const int old_offset = *p_offset;
+  int       o          = *p_offset;
+  int       m          = *p_match;
+
+  m = 0;
+
+  /*CDEBUG3(dict_search, "%s: [%s] <=> [%s]", __FUNCTION__, s1, s2);*/
+
+  if (o < key_len1  &&  o < key_len2) {
+
+    while (key1[o] == key2[o]) {
+      o++;
+
+      if (!(o < key_len1  &&  o < key_len2))
+        break;
+
+    }
+
+  }
+
+
+  if (o != old_offset) {
+
+    if (o >= key_len1) {
+
+      if (o >= key_len2) {
+        m = 1;
+      } else {
+        m = 2;
+      }
+
+    } else if (o >= key_len2) {
+      m = 3;
+    } else {
+      m = 4;
+    }
+  }
+
+
+  *p_offset = o;
+  *p_match  = m;
+}
+
+/**
+ * _dictelm_child_cmp:
+ *
+ * Compares two dictelm keys and return their matching (using the same 
+ * convention than @_gras_dict_child_search() )
+ */
+static inline
+void
+_dict_child_cmp(gras_dictelm_t *p_dict,
+                int          pos,
+                const char  *key,
+                const int    key_len,
+                int         *p_offset,
+                int         *p_match,
+                int         *p_cmp) {
+  gras_dictelm_t  *p_child = NULL;
+  int           cmp     = 0;
+  int           o       = *p_offset;
+  int           m       = *p_match;
+
+  gras_dynar_get(p_dict->sub, pos, &p_child);
+
+  /* Compute the length of the prefix
+     and if the searched key is before or after cur */
+  _str_prefix_lgr(p_child->key, p_child->key_len,
+                 key,          key_len,
+                 &o, &m);
+
+
+  if (m) /* found, get out */
+    goto end;
+
+  if (o < p_child->key_len  &&  (o >= key_len  ||  key[o] < p_child->key[o])) {
+    cmp = -1;
+  } else {
+    cmp =  1;
+  }
+
+  CDEBUG3(dict_search, "Cmp %s and %s => %d", p_child->key + *p_offset,
+         key + *p_offset, cmp);
+
+ end:
+  *p_offset = o;
+  *p_match  = m;
+  *p_cmp    = cmp;
+}
+
+/**
+ * _gras_dict_child_search:
+ *
+ * Search where would be inserted @key between the childs of @p_elm.
+ * 
+ * Returns position of the child having a common prefix with this key        
+ * If *match==0, no child have a common prefix                               
+ *               *pos is where to add the key                                
+ * If *match==1, A child (located at *pos) have exactly this key             
+ * If *match==2, A child (located at *pos) constitutes a prefix of the key   
+ *               the recursion have to go on that guy                        
+ *               *prefix = the size of the key eaten at this level           
+ * If *match==3  The key is a prefix of the child at *pos                    
+ * If *match==4, A child (loc. at *pos) share a common prefix with this key  
+ *               *prefix = size of the prefix.                               
+ *               If searching, that's a mismatch.                            
+ *               If inserting, you have to break the child and create an     
+ *                 internal node having {child, key} as childs               
+ * offset is used in input and output. In input, that's the length of the key
+ *  handled by previous levels of recursion. In output, that the one counting
+ *  also this level.                                                         
+ */
+static inline
+void
+_gras_dictelm_child_search(gras_dictelm_t *p_elm,
+                          const char  *key,
+                          int          key_len,
+                          int         *p_pos,
+                          int         *p_offset,
+                          int         *p_match) {
+
+  int          p       = 0;
+  int          o       = *p_offset;
+  int          m       = 0;
+  int          len     = 0;
+
+  
+  CDEBUG3(dict_search, "search child [%s] under [%s] (len=%d)",
+         key,
+          p_elm?p_elm->key:"(head)",
+         (p_elm&&p_elm->sub)?gras_dynar_length(p_elm->sub):0);
+  
+
+  len = gras_dynar_length(p_elm->sub);
+
+  for (p = 0; p < len; p++) {
+    int          cmp     = 0;
+
+    _dict_child_cmp(p_elm, p, key, key_len, &o, &m, &cmp);
+
+    if (m)
+      break;
+
+    o = *p_offset;
+    m = 0;
+  }
+
+  *p_offset = o;
+  *p_pos    = p;
+  *p_match  = m;
+  CDEBUG3(dict_search, "search [%s] in [%s] => %s",
+         key,
+          p_elm?p_elm->key:"(head)",
+         ( m == 0 ? "no child have a common prefix" :
+           ( m == 1 ? "selected child have exactly this key" :
+             ( m == 2 ? "selected child constitutes a prefix" :
+               ( m == 3 ? "key is a prefix of selected child" :
+                 (m == 4 ? "selected child share a prefix" :
+                  "internal error")))))
+         );  
+}
+
+/**
+ * _gras_dictelm_change_value:
+ *
+ * Change the value of the dictelm, making sure to free the old one, if any.
+ */
+static inline
+void
+_gras_dictelm_change_value(gras_dictelm_t    *p_elm,
+                          void           *data,
+                          void_f_pvoid_t *free_ctn) {
+
+  if (p_elm->content && p_elm->free_ctn) {
+    p_elm->free_ctn(p_elm->content);
+  }
+
+  p_elm->free_ctn = free_ctn;
+  p_elm->content  = data;
+}
+
+/**
+ * _gras_dictelm_insert_rec:
+ *
+ * @head: the head of the dict
+ * @key: the key to insert the new data
+ * @offset: offset on key.
+ * @data: the data to add in the dict
+ * @Returns: a gras_error
+ *
+ * Insert the @data in the structure under the @key. The @key is destroyed
+ * in the process. Think to strdup it before.
+ *
+ * This is a helper function to gras_dict_insert which locks the struct and
+ * strdup the key before action. 
+ */
+gras_error_t
+_gras_dictelm_insert_rec(gras_dictelm_t     *p_head,
+                        char            *key,
+                        int              key_len,
+                        int              offset,
+                        void            *data,
+                        void_f_pvoid_t  *free_ctn) {
+  gras_error_t errcode    = no_error;
+  int          match      = 0;
+  int          pos        = 0;
+  const int    old_offset = offset;
+
+  CDEBUG4(dict_add, "--> Insert '%s' after '%s' (offset=%d) in tree %p",
+         key, ((p_head && p_head->key) ? p_head->key : "(head)"), offset,
+         p_head);
+
+  /*** The trivial cases first ***/
+
+  /* there is no key (we did enough recursion), change the value of head */
+  if (offset >= key_len) {
+
+    CDEBUG0(dict_add, "--> Change the value of head");
+
+    _gras_dictelm_change_value(p_head, data, free_ctn);
+    free(key); /* Keep the key used in the tree */
+
+    return errcode;
+  }
+
+  /*** Search where to add this child, and how ***/
+  _gras_dictelm_child_search(p_head, key, key_len, &pos, &offset, &match);
+
+  CDEBUG3(dict_add, "child_search => pos=%d, offset=%d, match=%d",
+         pos, offset, match);
+
+  switch (match) {
+
+  case 0: /* no child have a common prefix */
+    {
+      gras_dictelm_t *p_child = NULL;
+
+      TRY(_gras_dictelm_alloc(key, key_len, offset, data, free_ctn, &p_child));
+      CDEBUG1(dict_add, "-> Add a child %p", p_child);
+      TRY(gras_dynar_insert_at(p_head->sub, pos, &p_child));
+
+      return errcode;
+    }
+
+  case 1: /* A child have exactly this key => change its value*/
+    {
+      gras_dictelm_t *p_child = NULL;
+
+      gras_dynar_get(p_head->sub, pos, &p_child);
+      CDEBUG1(dict_add, "-> Change the value of the child %p", p_child);
+      _gras_dictelm_change_value(p_child, data, free_ctn);
+
+      free(key);
+
+      return errcode;
+    }
+
+  case 2: /* A child constitutes a prefix of the key => recurse */
+    {
+      gras_dictelm_t *p_child = NULL;
+
+      gras_dynar_get(p_head->sub, pos, &p_child);
+      CDEBUG2(dict_add,"-> Recurse on %p (offset=%d)", p_child, offset);
+
+      return _gras_dictelm_insert_rec(p_child, key, key_len, 
+                                     offset, data, free_ctn);
+    }
+
+  case 3: /* The key is a prefix of the child => child becomes child of p_new */
+    {
+      gras_dictelm_t *p_new   = NULL;
+      gras_dictelm_t *p_child = NULL;
+
+      gras_dynar_get(p_head->sub, pos, &p_child);
+      TRY(_gras_dictelm_alloc(key, key_len, old_offset, data, free_ctn, &p_new));
+
+      CDEBUG2(dict_add, "-> The child %p become child of new dict (%p)",
+              p_child, p_new);
+
+      TRY(gras_dynar_push(p_new->sub, &p_child));
+      p_child->offset = offset;
+      TRY(gras_dynar_set(p_head->sub, pos, &p_new));
+
+      return errcode;
+    }
+
+  case 4: /* A child share a common prefix with this key => Common ancestor */
+    {
+      gras_dictelm_t *p_new       = NULL;
+      gras_dictelm_t *p_child     = NULL;
+      gras_dictelm_t *p_anc       = NULL;
+      char        *anc_key     = NULL;
+      int          anc_key_len = offset;
+
+      TRY(_gras_dictelm_alloc(key, key_len, offset, data, free_ctn, &p_new));
+      gras_dynar_get(p_head->sub, pos, &p_child);
+
+      anc_key = memdup(key, anc_key_len);
+
+      TRY(_gras_dictelm_alloc(anc_key, anc_key_len, old_offset, 
+                             NULL, NULL, &p_anc));
+
+      CDEBUG2(dict_add, "-> Make a common ancestor %p (%s)", p_anc, anc_key);
+
+      if (key[offset] < p_child->key[offset]) {
+        TRY(gras_dynar_push(p_anc->sub, &p_new));
+        TRY(gras_dynar_push(p_anc->sub, &p_child));
+      } else {
+        TRY(gras_dynar_push(p_anc->sub, &p_child));
+        TRY(gras_dynar_push(p_anc->sub, &p_new));
+      }
+
+      p_child->offset = offset;
+
+      TRY(gras_dynar_set(p_head->sub, pos, &p_anc));
+
+      return errcode;
+    }
+
+  default:
+    RAISE_IMPOSSIBLE;
+  }
+
+}
+
+/**
+ * gras_dictelm_insert_ext:
+ *
+ * @head: the head of the dict
+ * @key: the key to insert the new data
+ * @data: the data to add in the dict
+ * @Returns: a gras_error
+ *
+ * Insert the @data in the structure under the @key, which can be any kind 
+ * of data, as long as its length is provided in @key_len.
+ */
+gras_error_t
+gras_dictelm_insert_ext(gras_dictelm_t **pp_head,
+                       const char      *_key,
+                       int              key_len,
+                       void            *data,
+                       void_f_pvoid_t  *free_ctn) {
+  gras_error_t  errcode =  no_error;
+  gras_dictelm_t  *p_head  = *pp_head;
+  char         *key     =  NULL;
+
+  key = memdup(_key, key_len);
+  if (!key) 
+    RAISE_MALLOC;
+
+  /* there is no head, create it */
+  if (!p_head) {
+    gras_dictelm_t *p_child = NULL;
+
+    CDEBUG0(dict_add, "Create an head");
+
+    /* The head is priviledged by being the only one with a NULL key */
+    TRY(_gras_dictelm_alloc(NULL, 0, 0, NULL, NULL, &p_head));
+
+    TRY(_gras_dictelm_alloc(key, key_len, 0, data, free_ctn, &p_child));
+    TRY(gras_dynar_insert_at(p_head->sub, 0, &p_child));
+
+    *pp_head = p_head;
+
+    return errcode;
+  }
+
+  return _gras_dictelm_insert_rec(p_head, key, key_len, 0, data, free_ctn);
+}
+
+/**
+ * gras_dictelm_insert:
+ *
+ * @head: the head of the dict
+ * @key: the key to insert the new data
+ * @data: the data to add in the dict
+ * @Returns: a gras_error
+ *
+ * Insert the @data in the structure under the @key, which is a 
+ * null terminated string.
+ */
+gras_error_t
+gras_dictelm_insert(gras_dictelm_t **pp_head,
+                   const char      *_key,
+                   void            *data,
+                   void_f_pvoid_t  *free_ctn) {
+
+  return gras_dictelm_insert_ext(pp_head, _key, 1+strlen(_key), data, free_ctn);
+}
+
+/**
+ * _gras_dict_retrieve_rec:
+ *
+ * @head: the head of the dict
+ * @key: the key to find data
+ * @offset: offset on the key
+ * @data: the data that we are looking for
+ * @Returns: gras_error
+ *
+ * Search the given @key. mismatch_error when not found.
+ */
+static 
+gras_error_t
+_gras_dictelm_retrieve_rec(gras_dictelm_t *p_head,
+                          const char     *key,
+                          int             key_len,
+                          int             offset,
+                          /* OUT */void **data) {
+
+  gras_error_t errcode = no_error;
+
+  CDEBUG2(dict_search, "Search %s in %p", key, p_head); 
+
+  /*** The trivial case first ***/
+
+  /* we did enough recursion, we're done */
+  if (offset >= key_len) {
+    *data = p_head->content;
+
+    return errcode;
+  }
+
+  {
+    int match = 0;
+    int pos   = 0;
+
+    *data = NULL; // Make it ready to answer 'not found' in one operation
+
+    /*** Search where is the good child, and how good it is ***/
+    _gras_dictelm_child_search(p_head, key, key_len, &pos, &offset, &match);
+
+    switch (match) {
+
+    case 0: /* no child have a common prefix */
+      return mismatch_error;
+
+    case 1: /* A child have exactly this key => Got it */
+      {
+        gras_dictelm_t *p_child = NULL;
+
+        gras_dynar_get(p_head->sub, pos, &p_child);
+        *data = p_child->content;
+
+        return errcode;
+      }
+
+    case 2: /* A child constitutes a prefix of the key => recurse */
+      {
+        gras_dictelm_t *p_child = NULL;
+
+        gras_dynar_get(p_head->sub, pos, &p_child);
+
+        return _gras_dictelm_retrieve_rec(p_child, key, key_len, offset, data);
+      }
+
+    case 3: /* The key is a prefix of the child => not found */
+      return mismatch_error;
+
+    case 4: /* A child share a common prefix with this key => not found */
+      return mismatch_error;
+
+    default:
+      RAISE_IMPOSSIBLE;
+    }
+  }
+}
+
+/**
+ * gras_dictelm_retrieve_ext:
+ *
+ * @head: the head of the dict
+ * @key: the key to find data
+ * @data: the data that we are looking for
+ * @Returns: gras_error
+ *
+ * Search the given @key. mismatch_error when not found.
+ */
+gras_error_t
+gras_dictelm_retrieve_ext(gras_dictelm_t *p_head,
+                         const char     *key,
+                         int             key_len,
+                         /* OUT */void **data) {
+  /* there is no head, go to hell */
+  if (!p_head) {
+    return mismatch_error;
+  }
+
+  return _gras_dictelm_retrieve_rec(p_head, key, key_len, 0, data);
+}
+
+/**
+ * gras_dictelm_retrieve:
+ *
+ * @head: the head of the dict
+ * @key: the key to find data
+ * @data: the data that we are looking for
+ * @Returns: gras_error
+ *
+ * Search the given @key. mismatch_error when not found.
+ */
+gras_error_t
+gras_dictelm_retrieve(gras_dictelm_t    *p_head,
+                   const char     *key,
+                   /* OUT */void **data) {
+
+  return gras_dictelm_retrieve_ext(p_head, key, 1+strlen(key), data);
+}
+
+/*----[ _gras_dict_collapse ]------------------------------------------------*/
+static inline
+void
+_collapse_if_need(gras_dictelm_t *p_head,
+                 int             pos,
+                 int             offset) {
+  gras_dictelm_t  *p_child = NULL;
+
+  CDEBUG2(dict_collapse, "Collapse %d of %p... ", pos, p_head); fflush(stdout);
+
+  if (pos >= 0) {
+    /* Remove the child if |it's key| == 0 (meaning it's dead) */
+    gras_dynar_get(p_head->sub, pos, &p_child);
+
+    if (offset >= p_child->key_len) {
+
+      gras_assert0(gras_dynar_length(p_child->sub) == 0,
+                  "Found a dead child with grand childs. Internal error");
+
+      CDEBUG1(dict_collapse, "Remove dead child %p.... ", p_child);
+      gras_dynar_remove_at(p_head->sub, pos, &p_child);
+    }
+  }
+
+  if (!p_head->key) {
+    CDEBUG0(dict_collapse, "Do not collapse the head, you stupid programm");
+    return;
+  }
+
+  if (p_head->content || p_head->free_ctn ||
+      gras_dynar_length(p_head->sub) != 1) {
+    CDEBUG0(dict_collapse, "Cannot collapse");
+    return; /* cannot collapse */
+  }
+
+  gras_dynar_get(p_head->sub, 0, &p_child);
+
+  /* Get the child's key as new key */
+  CDEBUG1(dict_collapse, "Do collapse with only child %s", p_child->key); 
+
+  p_head->content  = p_child->content;
+  p_head->free_ctn = p_child->free_ctn;
+  free(p_head->key);
+  p_head->key      = p_child->key;
+  p_head->key_len  = p_child->key_len;
+
+  gras_dynar_free_container(p_head->sub) ;
+
+  p_head->sub = p_child->sub;
+  free(p_child);
+}
+
+/**
+ * _gras_dict_remove_rec:
+ *
+ * @head: the head of the dict
+ * @key: the key of the data to be removed
+ * @offset: offset on the key
+ * @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ */
+gras_error_t
+_gras_dictelm_remove_rec(gras_dictelm_t *p_head,
+                        const char  *key,
+                        int          key_len,
+                        int          offset) {
+  gras_error_t errcode = no_error;
+
+  /* there is no key to search, we did enough recursion => kill current */
+  if (offset >= key_len) {
+    int killme = 0; /* do I need to suicide me ? */
+
+    if (p_head->content && p_head->free_ctn) {
+      p_head->free_ctn(p_head->content);
+    }
+
+    killme = !gras_dynar_length(p_head->sub);
+    p_head->content  = NULL;
+    p_head->free_ctn = NULL;
+    _collapse_if_need(p_head, -1, offset);
+
+    if (killme) {
+      DEBUG0("kill this node");
+      p_head->key_len = 0; /* killme. Cleanup done one step higher in recursion */
+    }
+
+    return errcode;
+
+  } else {
+    int match      =      0;
+    int pos        =      0;
+    int old_offset = offset;
+
+    /*** Search where is the good child, and how good it is ***/
+    _gras_dictelm_child_search(p_head, key, key_len, &pos, &offset, &match);
+
+    switch (match) {
+
+    case 1: /* A child have exactly this key           => recurse */
+    case 2: /* A child constitutes a prefix of the key => recurse */
+
+      {
+        gras_dictelm_t *p_child = NULL;
+
+        gras_dynar_get(p_head->sub, pos, &p_child);
+        /*DEBUG4("Recurse on child %d of %p to remove %s (prefix=%d)",
+          pos, p_child, key+offset, offset);*/
+        TRY(_gras_dictelm_remove_rec(p_child, key, key_len, offset));
+
+        _collapse_if_need(p_head, pos, old_offset);
+       return no_error;
+      }
+
+
+    case 0: /* no child have a common prefix */
+    case 3: /* The key is a prefix of the child => not found */
+    case 4: /* A child share a common prefix with this key => not found */
+
+      return mismatch_error;
+
+
+    default:
+      RAISE_IMPOSSIBLE;
+
+    }
+  }
+}
+
+/**
+ * gras_dictelm_remove_ext:
+ *
+ * @head: the head of the dict
+ * @key: the key of the data to be removed
+ * @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ */
+gras_error_t
+gras_dictelm_remove_ext(gras_dictelm_t *p_head,
+                     const char  *key,
+                     int          key_len) {
+  /* there is no head, go to hell */
+  if (!p_head) {
+    RAISE0(mismatch_error, "there is no head, go to hell");
+  }
+  
+  return _gras_dictelm_remove_rec(p_head, key, key_len, 0);
+}
+
+/**
+ * gras_dictelm_remove:
+ *
+ * @head: the head of the dict
+ * @key: the key of the data to be removed
+ * @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ */
+gras_error_t
+gras_dictelm_remove(gras_dictelm_t *p_head,
+                   const char     *key) {
+  return _gras_dictelm_remove_rec(p_head, key, 1+strlen(key),0);
+}
+
+/*----[ _gras_dict_dump_rec ]------------------------------------------------*/
+/* private function to do the job of gras_dict_dump recursively              */
+/*---------------------------------------------------------------------------*/
+static
+gras_error_t
+_gras_dictelm_dump_rec(gras_dictelm_t *p_head,
+                      int             offset,
+                      void_f_pvoid_t *output) {
+  gras_error_t  errcode = no_error;
+  gras_dictelm_t  *p_child =     NULL;
+  char         *key     =     NULL;
+  int           key_len =        0;
+  int           i       =        0;
+
+  if (!p_head)
+    return no_error;
+
+  printf("[%p] ", p_head);
+
+  key     = p_head->key;
+  key_len = p_head->key_len;
+
+  if (key_len) {
+    printf ("  ");
+  }
+
+  for (i = 0; i < offset; i++) {
+    printf("-");
+  }
+
+  fflush(stdout);
+
+  if (key) {
+
+    if (!key_len) {
+      printf ("HEAD");
+    } else {
+      char *key_string = NULL;
+
+      key_string = malloc(key_len*2+1);
+      if (!key_string)
+        RAISE_MALLOC;
+
+      _gras_bytes_to_string(key, key_len, key_string);
+
+      printf("%s|(%d)", key_string + offset, offset);
+
+      free(key_string);
+    }
+
+  }
+
+  printf(" -> ");
+
+  if (p_head->content) {
+
+    if (output) {
+      output(p_head->content);
+    } else {
+      printf("(data)");
+    }
+
+  } else {
+    printf("(null)");
+  }
+
+  printf("    \t\t\t[ %d child(s) ]\n", gras_dynar_length(p_head->sub));
+
+  gras_dynar_foreach(p_head->sub, i, p_child) 
+    TRY(_gras_dictelm_dump_rec(p_child, p_child->offset, output));
+
+  return errcode;
+}
+
+/**
+ * gras_dictelm_dump:
+ *
+ * @head: the head of the dict
+ * @output: a function to dump each data in the tree
+ * @Returns: gras_error_t
+ *
+ * Ouputs the content of the structure. (for debuging purpose). @ouput is a
+ * function to output the data. If NULL, data won't be displayed.
+ */
+
+gras_error_t
+gras_dictelm_dump(gras_dictelm_t *p_head,
+               void_f_pvoid_t *output) {
+  return _gras_dictelm_dump_rec(p_head, 0, output);
+}
+
+/**
+ * gras_dictelm_print_fct:
+ *
+ * @data:
+ *
+ * To dump multidict, this function dumps a dict
+ */
+
+void
+gras_dictelm_print_fct(void *data) {
+  printf("tree %p", data);
+}
+
diff --git a/src/xbt/dict_multi.c b/src/xbt/dict_multi.c
new file mode 100644 (file)
index 0000000..4cf5e99
--- /dev/null
@@ -0,0 +1,248 @@
+/* $Id$ */
+
+/* dict_multi - dictionnaries of dictionnaries of ... of data               */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003,2004 Martin Quinson.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+
+#include <stdlib.h> /* malloc() */
+#include <string.h> /* strlen() */
+
+/*####[ Multi dict functions ]##############################################*/
+/*###############################"##########################################*/
+/**
+ * gras_mutidict_insert:
+ *
+ * @head: the head of dict
+ * @keycount: the number of the key
+ * @key: the key
+ * @data: the data to insert
+ * @Returns: gras_error_t
+ *
+ * Insert the data in the structure under the @keycount @key.
+ */
+
+gras_error_t
+gras_multidict_insert_ext(gras_dict_t    **pp_head,
+                          int              keycount,
+                          char           **key,
+                          int             *key_len,
+                          void            *data,
+                          void_f_pvoid_t  *free_ctn) {
+  gras_error_t  errcode   = no_error;
+  gras_dictelm_t  *p_elm    =     NULL;
+  gras_dictelm_t  *p_subdict =     NULL;
+  int           i         =        0;
+
+  CDEBUG2(dict_multi, "fast_multidict_insert(%p,%d). Keys:", *pp_head, keycount);
+
+  /*
+  for (i = 0; i < keycount; i++) {
+    CDEBUG1(dict_multi, "\"%s\"", key[i]);
+  }
+  */
+
+  gras_assert0(keycount >= 1, "Can't insert less than one key in a multidict");
+
+  if (keycount == 1)
+    return gras_dict_insert_ext(pp_head, key[0], key_len[0], data, free_ctn);
+
+  if (!*pp_head) {
+    TRY(_gras_dict_alloc(NULL, 0, 0, NULL, NULL, pp_head));
+  }
+
+  p_elm = *pp_head;
+
+  for (i = 0; i < keycount-1; i++) {
+
+    /* search the dict of next level */
+    TRYCATCH(gras_dict_retrieve(p_elm, key[i], (void*)&p_subdict), mismatch_error);
+
+    /* make sure the dict of next level exists */
+    if (errcode == mismatch_error) {
+      TRY(_gras_dict_alloc(NULL, 0, 0, NULL, NULL, &p_subdict));
+      TRY(gras_dict_insert_ext(&p_elm, key[i], key_len[i], &p_subdict,
+                               _free_dict));
+    }
+
+    p_elm = p_subdict;
+  }
+
+  return gras_dict_insert_ext(&p_elm, key[i], key_len[i], data, free_ctn);
+}
+
+gras_error_t
+gras_multidict_insert(gras_dictelm_t    **pp_head,
+                      int              keycount,
+                      char           **key,
+                      void            *data,
+                      void_f_pvoid_t  *free_ctn) {
+  gras_error_t  errcode = no_error;
+  int          *key_len = NULL;
+  int           i       = 0;
+
+  key_len = malloc(keycount * sizeof (int));
+  if (!key_len)
+    RAISE_MALLOC;
+
+  for (i = 0; i < keycount; i++) {
+    key_len[i] = 1+strlen(key[i]);
+  }
+
+  TRYCLEAN(gras_multidict_insert_ext(pp_head, keycount, key, key_len, data, free_ctn),
+           free(key_len));
+
+  free(key_len);
+
+  return errcode;
+}
+
+/**
+ * gras_mutidict_retrieve:
+ *
+ * @head: the head of dict
+ * @keycount: the number of the key
+ * @key: the key
+ * @data: where to put the data retrieved
+ * @Returns: gras_error_t
+ *
+ * Search the given @key. data=NULL when not found
+ */
+
+
+gras_error_t
+gras_multidict_retrieve_ext(gras_dictelm_t    *p_head,
+                            int             keycount,
+                            const char    **key,
+                            int            *key_len,
+                            /* OUT */void **data) {
+  gras_error_t  errcode = no_error;
+  gras_dictelm_t  *p_elm  =   p_head;
+  int           i       =        0;
+
+  CDEBUG2(dict_multi, "fast_multidict_retrieve(%p, %d). Keys:", p_head, keycount);
+
+  /*
+  for (i = 0; i < keycount; i++) {
+    CDEBUG1(dict_multi, "\"%s\"", key[i]);
+  }
+  */
+
+  i = 0;
+
+  while (p_elm && i < keycount-1) {
+
+    TRY(gras_dict_retrieve_ext(p_elm, key[i], key_len[i], (void**)p_elm));
+
+    /*
+    if (p_elm) {
+      CDEBUG3(dict_multi,"Found level %d for key %s in multitree %", i, key[i], p_head);
+    } else {
+      CDEBUG3(dict_multi,"NOT found level %d for key %s in multitree %p", i, key[i], p_head);
+    }
+    */
+
+    i++;
+  }
+
+  if (p_elm) { // Found all dicts to the data
+
+    //    gras_dict_dump(dict,&gras_dict_prints);
+    return gras_dict_retrieve_ext(p_elm, key[i], key_len[i], data);
+
+  } else {
+
+    *data = NULL;
+
+    return 1;
+  }
+
+}
+
+gras_error_t
+gras_multidict_retrieve(gras_dictelm_t    *p_head,
+                        int             keycount,
+                        const char    **key,
+                        /* OUT */void **data) {
+  gras_error_t  errcode = no_error;
+  int          *key_len = NULL;
+  int           i       = 0;
+
+  key_len = malloc(keycount * sizeof (int));
+  if (!key_len)
+    RAISE_MALLOC;
+
+  for (i = 0; i < keycount; i++) {
+    key_len[i] = 1+strlen(key[i]);
+  }
+
+  TRYCLEAN(gras_multidict_retrieve_ext(p_head, keycount, key, key_len, data),
+           free(key_len));
+  free(key_len);
+
+  return errcode;
+}
+
+
+/**
+ *  gras_mutidict_remove:
+ *
+ *  @head: the head of dict
+ *  @keycount: the number of the key
+ *  @key: the key
+ *  @Returns: gras_error_t
+ *
+ * Remove the entry associated with the given @key
+ * Removing a non-existant key is ok.
+ */
+
+gras_error_t
+gras_multidict_remove_ext(gras_dictelm_t  *p_head,
+                          int           keycount,
+                          const char  **key,
+                          int          *key_len) {
+  gras_dictelm_t *p_elm = p_head;
+  int          i      =      0;
+
+  while (p_elm && i < keycount-1) {
+    if (!gras_dict_retrieve_ext(p_elm, key[i], key_len[i], (void**)&p_elm)) {
+      return 0;
+    }
+  }
+
+  if (p_elm) {
+    // Found all dicts to the data
+    return gras_dict_remove_ext(p_elm, key[i], key_len[i]);
+  } else {
+    return 1;
+  }
+
+}
+
+gras_error_t
+gras_multidict_remove(gras_dictelm_t  *p_head,
+                      int           keycount,
+                      const char  **key) {
+  gras_error_t  errcode = no_error;
+  int          *key_len = NULL;
+  int           i       = 0;
+
+  key_len = malloc(keycount * sizeof (int));
+  if (!key_len)
+    RAISE_MALLOC;
+
+  for (i = 0; i < keycount; i++) {
+    key_len[i] = 1+strlen(key[i]);
+  }
+
+  TRYCLEAN(gras_multidict_remove_ext(p_head, keycount, key, key_len),
+           free(key_len));
+  free(key_len);
+
+  return errcode;
+}
diff --git a/src/xbt/dict_private.h b/src/xbt/dict_private.h
new file mode 100644 (file)
index 0000000..619124d
--- /dev/null
@@ -0,0 +1,63 @@
+/* $Id$ */
+
+/* dict_elm - elements of generic dictionnaries                             */
+/* This file is not to be loaded from anywhere but dict.c                   */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003,2004 Martin Quinson.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#ifndef _GRAS_DICT_ELM_T_
+#define _GRAS_DICT_ELM_T_
+
+/*####[ Type definition ]####################################################*/
+typedef struct gras_dictelm_ {
+  char           *key;
+  int             key_len;
+  int             offset; /* offset on the key */
+  void           *content;
+  void_f_pvoid_t *free_ctn; /*pointer to the function to call to free this ctn*/
+
+  gras_dynar_t   *sub; /* sub */
+} gras_dictelm_t;
+
+struct gras_dict_ {
+  gras_dictelm_t *head;
+};
+
+/*####[ Function prototypes ]################################################*/
+void         gras_dictelm_free        (gras_dictelm_t **pp_elm);
+
+gras_error_t gras_dictelm_insert      (gras_dictelm_t **pp_head,
+                                      const char      *_key,
+                                      void            *data,
+                                      void_f_pvoid_t  *free_ctn);
+gras_error_t gras_dictelm_insert_ext  (gras_dictelm_t **pp_head,
+                                      const char      *_key,
+                                      int              key_len,
+                                      void            *data,
+                                      void_f_pvoid_t  *free_ctn);
+
+gras_error_t gras_dictelm_retrieve    (gras_dictelm_t *p_head,
+                                      const char     *key,
+                                      /* OUT */void **data);
+gras_error_t gras_dictelm_retrieve_ext(gras_dictelm_t *p_head,
+                                      const char     *key,
+                                      int             key_len,
+                                      /* OUT */void **data);
+
+gras_error_t gras_dictelm_remove      (gras_dictelm_t *p_head,
+                                      const char  *key);
+gras_error_t gras_dictelm_remove_ext  (gras_dictelm_t *p_head,
+                                      const char  *key,
+                                      int          key_len);
+
+gras_error_t gras_dictelm_dump        (gras_dictelm_t *p_head,
+                                      void_f_pvoid_t *output);
+
+void         gras_dictelm_print_fct   (void *data);
+
+#endif  /* _GRAS_DICT_ELM_T_ */
+
diff --git a/src/xbt/dynar.c b/src/xbt/dynar.c
new file mode 100644 (file)
index 0000000..16a5f00
--- /dev/null
@@ -0,0 +1,555 @@
+/* $Id$ */
+
+/* a generic DYNamic ARray                                                  */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003 the OURAGAN project.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+
+
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(dynar,GRAS);
+
+struct gras_dynar_s {
+  size_t          size;
+  size_t          used;
+  size_t          elmsize;
+  void           *data;
+  void_f_pvoid_t *free;
+};
+
+#define __sanity_check_dynar(dynar)       \
+           gras_assert0(dynar,           \
+                       "dynar is NULL")
+#define __sanity_check_idx(idx)                \
+           gras_assert1(idx >= 0,             \
+                       "dynar idx(=%d) < 0", \
+                       idx)
+#define __check_inbound_idx(dynar, idx)                                                \
+           gras_assert2(idx < dynar->used,                                             \
+                       "dynar is not that long. You asked %d, but it's only %d long", \
+                       idx, dynar->used)
+#define __check_sloppy_inbound_idx(dynar, idx)                                         \
+           gras_assert2(idx <= dynar->used,                                            \
+                       "dynar is not that long. You asked %d, but it's only %d long", \
+                       idx, dynar->used)
+#define __check_populated_dynar(dynar)            \
+           gras_assert0(dynar->used,              \
+                       "dynar contains nothing")
+
+
+static inline
+void
+_gras_clear_mem(void * const ptr,
+                const size_t length) {
+  memset(ptr, 0, length);
+}
+
+static inline
+gras_error_t
+_gras_dynar_expand(gras_dynar_t * const dynar,
+                   const int            nb) {
+  gras_error_t errcode     = no_error;
+  const size_t old_size    = dynar->size;
+
+  if (nb > old_size) {
+    char * const old_data    = dynar->data;
+
+    const size_t elmsize     = dynar->elmsize;
+    const size_t old_length  = old_size*elmsize;
+
+    const size_t used        = dynar->used;
+    const size_t used_length = used*elmsize;
+
+    const size_t new_size    = nb > (2*(old_size+1)) ? nb : (2*(old_size+1));
+    const size_t new_length  = new_size*elmsize;
+    char * const new_data    = calloc(1, elmsize*new_size);
+
+    DEBUG3("expend %p from %d to %d elements", dynar, old_size, nb);
+    if (!new_data)
+      RAISE_MALLOC;
+
+    if (old_data) {
+      memcpy(new_data, old_data, used_length);
+      _gras_clear_mem(old_data, old_length);
+      free(old_data);
+    }
+
+    _gras_clear_mem(new_data + used_length, new_length - used_length);
+
+    dynar->size = new_size;
+    dynar->data = new_data;
+  }
+
+  return errcode;
+}
+
+static inline
+void *
+_gras_dynar_elm(const gras_dynar_t * const dynar,
+                const size_t               idx) {
+  char * const data    = dynar->data;
+  const size_t elmsize = dynar->elmsize;
+
+  return data + idx*elmsize;
+}
+
+static inline
+void
+_gras_dynar_get_elm(void               * const dst,
+                    const gras_dynar_t * const dynar,
+                    const size_t               idx) {
+  void * const elm     = _gras_dynar_elm(dynar, idx);
+  const size_t elmsize = dynar->elmsize;
+
+  memcpy(dst, elm, elmsize);
+}
+
+static inline
+void
+_gras_dynar_put_elm(const gras_dynar_t * const dynar,
+                    const size_t               idx,
+                    const void         * const src) {
+  void * const elm     = _gras_dynar_elm(dynar, idx);
+  const size_t elmsize = dynar->elmsize;
+
+  memcpy(elm, src, elmsize);
+}
+
+/**
+ * gras_dynar_new:
+ * @whereto: pointer to where the dynar should be created
+ * @elm_size: size of each element in the dynar
+ * @free_func: function to call each time we want to get rid of an element (or NULL if nothing to do).
+ * @Returns: malloc_error or no_error
+ *
+ * Creates a new dynar. If a free_func is provided, the elements have to be
+ * pointer of pointer. That is to say that dynars can contain either base
+ * types (int, char, double, etc) or pointer of pointers (struct **).
+ */
+gras_error_t
+gras_dynar_new(gras_dynar_t   ** const p_dynar,
+               const size_t            elmsize,
+               void_f_pvoid_t  * const free_func) {
+  gras_error_t  errcode = no_error;
+  gras_dynar_t *dynar   = NULL;
+
+  if (!(dynar = calloc(1, sizeof(gras_dynar_t))))
+    RAISE_MALLOC;
+
+  dynar->size    = 0;
+  dynar->used    = 0;
+  dynar->elmsize = elmsize;
+  dynar->data    = NULL;
+  dynar->free    = free_func;
+
+  *p_dynar = dynar;
+
+  return errcode;
+}
+
+/**
+ * gras_dynar_free_container:
+ * @dynar: poor victim
+ *
+ * kilkil a dynar BUT NOT its content. Ie, the array is freed, but not what
+ * its contain points to.
+ */
+void
+gras_dynar_free_container(gras_dynar_t * const dynar) {
+  if (dynar) {
+
+    if (dynar->data) {
+      _gras_clear_mem(dynar->data, dynar->size);
+      free(dynar->data);
+    }
+
+    _gras_clear_mem(dynar, sizeof(gras_dynar_t));
+
+    free(dynar);
+  }
+}
+
+/**
+ * gras_dynar_reset:
+ * @dynar: who to squeeze
+ *
+ * Frees the content and set the size to 0
+ */
+void
+gras_dynar_reset(gras_dynar_t * const dynar) {
+
+  __sanity_check_dynar(dynar);
+
+  if (dynar->free) {
+    gras_dynar_map(dynar, dynar->free);
+  }
+
+  if (dynar->data) {
+    _gras_clear_mem(dynar->data, dynar->size);
+    free(dynar->data);
+  }
+
+  dynar->size = 0;
+  dynar->used = 0;
+  dynar->data = NULL;
+}
+
+/**
+ * gras_dynar_free:
+ * @dynar: poor victim
+ *
+ * kilkil a dynar and its content
+ */
+
+void
+gras_dynar_free(gras_dynar_t * const dynar) {
+  if (dynar) {
+    gras_dynar_reset(dynar);
+    gras_dynar_free_container(dynar);
+  }
+}
+
+/**
+ * gras_dynar_length:
+ * @dynar: the dynar we want to mesure
+ *
+ * Returns the count of elements in a dynar
+ */
+size_t
+gras_dynar_length(const gras_dynar_t * const dynar) {
+  return (dynar ? dynar->used : (size_t)0);
+}
+
+/**
+ * gras_dynar_get:
+ * @dynar: information dealer
+ * @idx: index of the slot we want to retrive
+ * @dst: where to pu the result to.
+ *
+ * Retrieve the Nth element of a dynar. Warning, the returned value is the actual content of 
+ * the dynar. Make a copy before fooling with it.
+ */
+void
+gras_dynar_get(const gras_dynar_t * const dynar,
+               const int                  idx,
+               void               * const dst) {
+
+  __sanity_check_dynar(dynar);
+  __sanity_check_idx(idx);
+  __check_inbound_idx(dynar, idx);
+
+  _gras_dynar_get_elm(dst, dynar, idx);
+}
+
+/**
+ * gras_dynar_set:
+ * @dynar:
+ * @idx:
+ * @src: What will be feeded to the dynar
+ * @Returns: malloc_error or no_error
+ *
+ * Set the Nth element of a dynar, expanding the dynar if needed, BUT NOT freeing
+ * the previous value at this position. If you want to free the previous content,
+ * use gras_dynar_remplace().
+ */
+gras_error_t
+gras_dynar_set(gras_dynar_t * const dynar,
+               const int            idx,
+               const void   * const src) {
+  gras_error_t errcode = no_error;
+
+  __sanity_check_dynar(dynar);
+  __sanity_check_idx(idx);
+
+  TRY(_gras_dynar_expand(dynar, idx+1));
+
+  if (idx >= dynar->used) {
+    dynar->used = idx+1;
+  }
+
+  _gras_dynar_put_elm(dynar, idx, src);
+
+  return errcode;
+}
+
+/**
+ * gras_dynar_remplace:
+ * @dynar:
+ * @idx:
+ * @object:
+ * @Returns: malloc_error or no_error
+ *
+ * Set the Nth element of a dynar, expanding the dynar if needed, AND DO
+ * free the previous value at this position. If you don't want to free the
+ * previous content, use gras_dynar_set().
+ */
+gras_error_t
+gras_dynar_remplace(gras_dynar_t * const dynar,
+                    const int            idx,
+                    const void   * const object) {
+  gras_error_t errcode = no_error;
+
+  __sanity_check_dynar(dynar);
+  __sanity_check_idx(idx);
+
+  if (idx < dynar->used && dynar->free) {
+    void * const old_object = _gras_dynar_elm(dynar, idx);
+
+    dynar->free(old_object);
+  }
+
+  errcode = gras_dynar_set(dynar, idx, object);
+
+  return errcode;
+}
+
+/**
+ * gras_dynar_insert_at:
+ * @dynar:
+ * @idx:
+ * @src: What will be feeded to the dynar
+ * @Returns: malloc_error or no_error
+ *
+ * Set the Nth element of a dynar, expanding the dynar if needed, and
+ * moving the previously existing value and all subsequent ones to one
+ * position right in the dynar.
+ */
+gras_error_t
+gras_dynar_insert_at(gras_dynar_t * const dynar,
+                     const int            idx,
+                     const void   * const src) {
+  gras_error_t errcode = no_error;
+
+  __sanity_check_dynar(dynar);
+  __sanity_check_idx(idx);
+  __check_sloppy_inbound_idx(dynar, idx);
+
+  {
+    const size_t old_used = dynar->used;
+    const size_t new_used = old_used + 1;
+
+    TRY(_gras_dynar_expand(dynar, new_used));
+
+    {
+      const size_t nb_shift =  old_used - idx;
+      const size_t elmsize  =  dynar->elmsize;
+
+      const size_t offset   =  nb_shift*elmsize;
+
+      void * const elm_src  = _gras_dynar_elm(dynar, idx);
+      void * const elm_dst  = _gras_dynar_elm(dynar, idx+1);
+
+      memmove(elm_dst, elm_src, offset);
+    }
+
+    _gras_dynar_put_elm(dynar, idx, src);
+    dynar->used = new_used;
+  }
+
+  return errcode;
+}
+
+/**
+ * gras_dynar_remove_at:
+ * @dynar: 
+ * @idx:
+ * @object:
+ *
+ * Get the Nth element of a dynar, removing it from the dynar and moving
+ * all subsequent values to one position left in the dynar.
+ */
+void
+gras_dynar_remove_at(gras_dynar_t * const dynar,
+                     const int            idx,
+                     void         * const object) {
+
+  __sanity_check_dynar(dynar);
+  __sanity_check_idx(idx);
+  __check_inbound_idx(dynar, idx);
+
+  if (object)
+    _gras_dynar_get_elm(object, dynar, idx);
+
+  {
+    const size_t old_used = dynar->used;
+    const size_t new_used = old_used - 1;
+
+    const size_t nb_shift =  old_used-1 - idx;
+    const size_t elmsize  =  dynar->elmsize;
+
+    const size_t offset   =  nb_shift*elmsize;
+
+    void * const elm_src  = _gras_dynar_elm(dynar, idx+1);
+    void * const elm_dst  = _gras_dynar_elm(dynar, idx);
+
+    memmove(elm_dst, elm_src, offset);
+
+    dynar->used = new_used;
+  }
+}
+
+/**
+ * gras_dynar_push:
+ * @dynar:
+ * @src:
+ * @Returns: malloc_error or no_error
+ *
+ * Add an element at the end of the dynar
+ */
+gras_error_t
+gras_dynar_push(gras_dynar_t * const dynar,
+                const void   * const src) {
+  __sanity_check_dynar(dynar);
+  return gras_dynar_insert_at(dynar, dynar->used, src);
+}
+
+/**
+ * gras_dynar_pop:
+ * @dynar:
+ * @dst:
+ *
+ * Get and remove the last element of the dynar
+ */
+void
+gras_dynar_pop(gras_dynar_t * const dynar,
+               void         * const dst) {
+  __sanity_check_dynar(dynar);
+  __check_populated_dynar(dynar);
+  gras_dynar_remove_at(dynar, dynar->used-1, dst);
+}
+
+/**
+ * gras_dynar_unshift:
+ * @dynar:
+ * @src:
+ * @Returns: malloc_error or no_error
+ *
+ * Add an element at the begining of the dynar (rather long, Use
+ * gras_dynar_push() when possible)
+ */
+gras_error_t
+gras_dynar_unshift(gras_dynar_t * const dynar,
+                   const void   * const src) {
+  __sanity_check_dynar(dynar);
+  return gras_dynar_insert_at(dynar, 0, src);
+}
+
+/**
+ * gras_dynar_shift:
+ * @dynar:
+ * @dst:
+ *
+ * Get and remove the first element of the dynar (rather long, Use
+ * gras_dynar_pop() when possible)
+ */
+void
+gras_dynar_shift(gras_dynar_t * const dynar,
+                 void         * const dst) {
+
+  __sanity_check_dynar(dynar);
+  __check_populated_dynar(dynar);
+  gras_dynar_remove_at(dynar, 0, dst);
+}
+
+/**
+ * gras_dynar_map:
+ * @dynar:
+ * @operator:
+ *
+ * Apply a function to each member of a dynar (this function may change the
+ * value of the element itself, but should not mess with the dynar).
+ */
+void
+gras_dynar_map(const gras_dynar_t * const dynar,
+               void_f_pvoid_t     * const operator) {
+
+  __sanity_check_dynar(dynar);
+
+  {
+    char         elm[64];
+    const size_t used = dynar->used;
+    size_t       i    = 0;
+
+    for (i = 0; i < used; i++) {
+      _gras_dynar_get_elm(elm, dynar, i);
+      operator(elm);
+    }
+  }
+}
+
+/**
+ * gras_dynar_first:
+ *
+ * Put the cursor at the begining of the dynar. (actually, one step before
+ * the begining, so that you can iterate over the dynar with a for loop).
+ *
+ * Dynar cursor are as dumb as possible. If you insert or remove elements
+ * from the dynar between the creation and end, you'll fuck up your
+ * cursors.
+ *
+ */
+void
+gras_dynar_cursor_first(const gras_dynar_t * const dynar,
+                       int                * const cursor) {
+
+  __sanity_check_dynar(dynar);
+  DEBUG1("Set cursor on %p to the first position",dynar);
+  *cursor = 0;
+}
+
+/**
+ * gras_dynar_cursor_step:
+ *
+ * Move the cursor to the next value (and return true), or return false.
+ */
+void
+gras_dynar_cursor_step(const gras_dynar_t * const dynar,
+                      int                * const cursor) {
+  
+  __sanity_check_dynar(dynar);
+  (*cursor)++;
+}
+
+/**
+ * gras_dynar_cursor_get:
+ *
+ * Get the current value of the cursor
+ */
+int
+gras_dynar_cursor_get(const gras_dynar_t * const dynar,
+                     int                * const cursor,
+                     void               * const dst) {
+
+  __sanity_check_dynar(dynar);
+  {
+
+    const int idx = *cursor;
+
+    if (idx >= dynar->used) {
+      DEBUG1("Cursor on %p already on last elem",dynar);
+      return FALSE;
+    }
+    DEBUG2("Cash out cursor on %p at %d",dynar,idx);
+
+    _gras_dynar_get_elm(dst, dynar, idx);
+  }
+  return TRUE;
+
+}
+
+/**
+ * gras_dynar_cursor_rm:
+ * @dynar:
+ * @cursor:
+ *
+ * Remove the entry pointed by the cursor, for use in the middle of a foreach
+ */
+void gras_dynar_cursor_rm(gras_dynar_t * dynar,
+                         int          * const cursor) {
+  
+  gras_dynar_remove_at(dynar,(*cursor)--,NULL);
+}
diff --git a/src/xbt/error.c b/src/xbt/error.c
new file mode 100644 (file)
index 0000000..6e5e1b8
--- /dev/null
@@ -0,0 +1,37 @@
+/* $Id$ */
+
+/* error - Error handling functions                                         */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2001,2002,2003,2004 the OURAGAN project.                   */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+
+/**
+ * gras_error_name:
+ * @errcode: 
+ * @Returns: the printable name of an error code
+ *
+ * usefull to do nice error repporting messages
+ */
+
+const char *gras_error_name(gras_error_t errcode)  {
+
+   switch (errcode) {
+      
+    case no_error: return "success";
+    case malloc_error: return "malloc";
+    case mismatch_error: return "mismatch";
+    case system_error: return "system";
+    case network_error: return "network";
+    case timeout_error: return "timeout";
+    case thread_error: return "thread";
+    case unknown_error: return "unclassified";
+    default:
+      return "SEVERE ERROR in error repporting module";
+   }
+}
+
diff --git a/src/xbt/log.c b/src/xbt/log.c
new file mode 100644 (file)
index 0000000..a9c834a
--- /dev/null
@@ -0,0 +1,372 @@
+/* $Id$ */
+
+/* log - a generic logging facility in the spirit of log4j                  */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003, 2004 Martin Quinson.                                 */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+typedef struct {
+  char *catname;
+  gras_log_priority_t thresh;
+} gras_log_setting_t;
+
+static gras_dynar_t *gras_log_settings=NULL;
+static void _free_setting(void *s) {
+  gras_log_setting_t *set=(gras_log_setting_t*)s;
+  if (set) {
+    free(set->catname);
+    free(set);
+  }
+}
+
+const char *gras_log_priority_names[8] = {
+  "NONE",
+  "DEBUG",
+  "VERBOSE",
+  "INFO",
+  "WARNING",
+  "ERROR",
+  "CRITICAL"
+};
+
+gras_log_category_t _GRAS_LOGV(GRAS_LOG_ROOT_CAT) = {
+  0, 0, 0,
+  "root", gras_log_priority_uninitialized, 0,
+  NULL, 0
+};
+GRAS_LOG_NEW_SUBCATEGORY(GRAS,GRAS_LOG_ROOT_CAT);
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(log,GRAS);
+
+
+static void _apply_control(gras_log_category_t* cat) {
+  int cursor;
+  gras_log_setting_t *setting=NULL;
+  int found = 0;
+
+  if (!gras_log_settings)
+    return;
+
+  gras_assert0(cat,"NULL category");
+  gras_assert(cat->name);
+
+  gras_dynar_foreach(gras_log_settings,cursor,setting) {
+    gras_assert0(setting,"Damnit, NULL cat in the list");
+    gras_assert1(setting->catname,"NULL setting(=%p)->catname",setting);
+
+    if (!strcmp(setting->catname,cat->name)) {
+      found = 1;
+
+      gras_log_threshold_set(cat, setting->thresh);
+      gras_dynar_cursor_rm(gras_log_settings,&cursor);
+
+      if (cat->threshold <= gras_log_priority_verbose) {
+       gras_log_event_t _log_ev = 
+         {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__,
+          "Apply settings for category '%s': set threshold to %s (=%d)",};
+       _gras_log_event_log(&_log_ev, cat->name,
+                           gras_log_priority_names[cat->threshold], cat->threshold);
+      }
+    }
+  }
+  if (!found && cat->threshold <= gras_log_priority_verbose) {
+    gras_log_event_t _log_ev = 
+      {cat,gras_log_priority_verbose,__FILE__,__FUNCTION__,__LINE__,
+       "Category '%s': inherited threshold = %s (=%d)",};
+    _gras_log_event_log(&_log_ev, cat->name,
+                       gras_log_priority_names[cat->threshold], cat->threshold);
+  }
+
+}
+
+void _gras_log_event_log( gras_log_event_t* ev, ...) {
+  gras_log_category_t* cat = ev->cat;
+  va_start(ev->ap, ev);
+  while(1) {
+    gras_log_appender_t* appender = cat->appender;
+    if (appender != NULL) {
+      appender->do_append(appender, ev);
+    }
+    if (!cat->willLogToParent)
+      break;
+
+    cat = cat->parent;
+  } 
+  va_end(ev->ap);
+}
+
+static void _cat_init(gras_log_category_t* category) {
+  if (category == &_GRAS_LOGV(GRAS_LOG_ROOT_CAT)) {
+    category->threshold = gras_log_priority_info;
+    category->appender = gras_log_default_appender;
+  } else {
+    gras_log_parent_set(category, category->parent);
+  }
+  _apply_control(category);
+}
+
+/*
+ * This gets called the first time a category is referenced and performs the
+ * initialization. 
+ * Also resets threshold to inherited!
+ */
+int _gras_log_cat_init(gras_log_priority_t priority,
+                      gras_log_category_t* category) {
+    
+  _cat_init(category);
+        
+  return priority >= category->threshold;
+}
+
+void gras_log_parent_set(gras_log_category_t* cat,
+                        gras_log_category_t* parent) {
+
+  gras_assert0(cat,"NULL category to be given a parent");
+  gras_assert1(parent,"The parent category of %s is NULL",cat->name);
+
+  // unlink from current parent
+  if (cat->threshold != gras_log_priority_uninitialized) {
+    gras_log_category_t** cpp = &parent->firstChild;
+    while(*cpp != cat && *cpp != NULL) {
+      cpp = &(*cpp)->nextSibling;
+    }
+    assert(*cpp == cat);
+    *cpp = cat->nextSibling;
+  }
+
+  // Set new parent
+  cat->parent = parent;
+  cat->nextSibling = parent->firstChild;
+  parent->firstChild = cat;
+
+  // Make sure parent is initialized
+  if (parent->threshold == gras_log_priority_uninitialized) {
+    _cat_init(parent);
+  }
+    
+  // Reset priority
+  cat->threshold = parent->threshold;
+  cat->isThreshInherited = 1;
+} // log_setParent
+
+static void _set_inherited_thresholds(gras_log_category_t* cat) {
+  gras_log_category_t* child = cat->firstChild;
+  for( ; child != NULL; child = child->nextSibling) {
+    if (child->isThreshInherited) {
+      if (cat != &_GRAS_LOGV(log))
+       VERB3("Set category threshold of %s to %s (=%d)",
+             child->name,gras_log_priority_names[cat->threshold],cat->threshold);
+      child->threshold = cat->threshold;
+      _set_inherited_thresholds(child);
+    }
+  }
+}
+
+void gras_log_threshold_set(gras_log_category_t* cat, 
+                           gras_log_priority_t threshold) {
+  cat->threshold = threshold;
+  cat->isThreshInherited = 0;
+  _set_inherited_thresholds(cat);
+}
+
+static gras_error_t _gras_log_parse_setting(const char* control_string,
+                                           gras_log_setting_t *set) {
+  const char *name, *dot, *eq;
+  
+  set->catname=NULL;
+  if (!*control_string) 
+    return no_error;
+  DEBUG1("Parse log setting '%s'",control_string);
+
+  control_string += strspn(control_string, " ");
+  name = control_string;
+  control_string += strcspn(control_string, ".= ");
+  dot = control_string;
+  control_string += strcspn(control_string, "= ");
+  eq = control_string;
+  control_string += strcspn(control_string, " ");
+
+  gras_assert1(*dot == '.' && *eq == '=',
+              "Invalid control string '%s'",control_string);
+
+  if (!strncmp(dot + 1, "thresh", MIN(eq - dot - 1,strlen("thresh")))) {
+    int i;
+    char *neweq=strdup(eq+1);
+    char *p=neweq-1;
+    
+    while (*(++p) != '\0') {
+      if (*p >= 'a' && *p <= 'z') {
+       *p-='a'-'A';
+      }
+    }
+    
+    DEBUG1("New priority name = %s",neweq);
+    for (i=0; i<6; i++) {
+      if (!strncmp(gras_log_priority_names[i],neweq,p-eq)) {
+       DEBUG1("This is priority %d",i);
+       break;
+      }
+    }
+    if (i<6) {
+      set->thresh=i;
+    } else {
+      gras_assert1(FALSE,"Unknown priority name: %s",eq+1);
+    }
+    free(neweq);
+  } else {
+    char buff[512];
+    snprintf(buff,MIN(512,eq - dot - 1),"%s",dot+1);
+    gras_assert1(FALSE,"Unknown setting of the log category: %s",buff);
+  }
+  if (!(set->catname=malloc(dot - name+1)))
+    RAISE_MALLOC;
+    
+  strncat(set->catname,name,dot-name);
+  DEBUG1("This is for cat '%s'", set->catname);
+  return no_error;
+}
+
+static gras_error_t _gras_log_cat_searchsub(gras_log_category_t *cat,char *name,gras_log_category_t**whereto) {
+  gras_error_t errcode;
+  gras_log_category_t *child;
+  
+  if (!strcmp(cat->name,name)) {
+    *whereto=cat;
+    return no_error;
+  }
+  for(child=cat->firstChild ; child != NULL; child = child->nextSibling) {
+    errcode=_gras_log_cat_searchsub(child,name,whereto);
+    if (errcode==no_error)
+      return no_error;
+  }
+  return mismatch_error;
+}
+
+static void _cleanup_double_spaces(char *s) {
+  char *p = s;
+  int   e = 0;
+  
+  while (1) {
+    if (!*p)
+      goto end;
+    
+    if (!isspace(*p))
+      break;
+    
+    p++;
+  }
+  
+  e = 1;
+  
+  do {
+    if (e)
+      *s++ = *p;
+    
+    if (!*++p)
+      goto end;
+    
+    if (e ^ !isspace(*p))
+      if ((e = !e))
+       *s++ = ' ';
+  } while (1);
+
+ end:
+  *s = '\0';
+}
+
+/**
+ * gras_log_control_set:
+ * @cs: What to parse
+ * @Returns: malloc_error or no_error
+ *
+ * Typically passed a command-line argument. The string has the syntax:
+ *
+ *      ( [category] "." [keyword] "=" value (" ")... )...
+ *
+ * where [category] is one the category names and keyword is one of the
+ * following:
+ *
+ *      thresh         value is an integer priority level. Sets the category's
+ *                        threshold priority.
+ *
+ * @warning
+ * This routine may only be called once and that must be before any other
+ * logging command! Typically, this is done from main().
+ */
+gras_error_t gras_log_control_set(const char* control_string) {
+  gras_error_t errcode;
+  gras_log_setting_t *set;
+  char *cs;
+  char *p;
+  int done = 0;
+  
+  DEBUG1("Parse log settings '%s'",control_string);
+  if (control_string == NULL)
+    return no_error;
+  if (gras_log_settings == NULL)
+    TRY(gras_dynar_new(&gras_log_settings,sizeof(gras_log_setting_t*),_free_setting));
+
+  if (!(set = malloc(sizeof(gras_log_setting_t))))
+    RAISE_MALLOC;
+
+  if (!(cs=strdup(control_string)))
+    RAISE_MALLOC;
+  _cleanup_double_spaces(cs);
+
+  while (!done) {
+    gras_log_category_t *cat;
+    
+    p=strrchr(cs,' ');
+    if (p) {
+      *p='\0';
+      *p++;
+    } else {
+      p=cs;
+      done = 1;
+    }
+    errcode = _gras_log_parse_setting(p,set);
+    if (errcode != no_error) {
+      free(set);
+      free(cs);
+    }
+    
+    TRYCATCH(_gras_log_cat_searchsub(&_GRAS_LOGV(root),set->catname,&cat),mismatch_error);
+    if (errcode == mismatch_error) {
+      DEBUG0("Store for further application");
+      DEBUG1("push %p to the settings",set);
+      TRY(gras_dynar_push(gras_log_settings,&set));
+      /* malloc in advance the next slot */
+      if (!(set = malloc(sizeof(gras_log_setting_t)))) { 
+       free(cs);
+       RAISE_MALLOC;
+      }
+    } else {
+      DEBUG0("Apply directly");
+      free(set->catname);
+      gras_log_threshold_set(cat,set->thresh);
+    }
+  }
+  free(set);
+  free(cs);
+  return no_error;
+} 
+
+void gras_log_appender_set(gras_log_category_t* cat, gras_log_appender_t* app) {
+  cat->appender = app;
+}
+
+void gras_log_finalize(void);
+void gras_log_finalize(void) {
+  gras_dynar_free(gras_log_settings);
+}
diff --git a/src/xbt/log_default_appender.c b/src/xbt/log_default_appender.c
new file mode 100644 (file)
index 0000000..d385a71
--- /dev/null
@@ -0,0 +1,73 @@
+// $Id$
+// Copyright (c) 2001, Bit Farm, Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3. The name of the author may not be used to endorse or promote products
+//    derived from this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <gras.h>
+#include <stdio.h>
+
+/**
+ * The root category's default logging function.
+ */
+
+extern const char *gras_log_priority_names[7];
+
+static void append_file(gras_log_appender_t* this, gras_log_event_t* ev);
+
+/*
+struct gras_log_appender_file_s {
+  gras_log_appender_t* appender;
+  FILE *file;
+};
+*/
+
+static gras_log_appender_t gras_log_appender_file = { append_file, NULL } ;
+/* appender_data=FILE* */
+
+gras_log_appender_t* gras_log_default_appender  = &gras_log_appender_file;
+
+static void append_file(gras_log_appender_t* this, gras_log_event_t* ev) {
+
+    // TODO: define a format field in struct for timestamp, etc.
+    const char *pn;
+    char buf[20];
+    //    struct DefaultLogAppender* this = (struct DefaultLogAppender*)this0;
+    
+    if ((FILE*)(this->appender_data) == NULL)
+      this->appender_data = (void*)stderr;
+    
+    if (ev->priority < 0) {
+        pn = "Negative Priority NOT ALLOWED!!";
+    }
+    else if (ev->priority < sizeof(gras_log_priority_names)) {
+        pn = gras_log_priority_names[ev->priority];
+    } else {
+        sprintf(buf, "%s+%d",
+                gras_log_priority_names[sizeof(gras_log_priority_names)-1],
+                ev->priority - sizeof(gras_log_priority_names) + 1);
+    }
+    fprintf(stderr, "%s:%d: ", ev->fileName, ev->lineNum);
+    fprintf(stderr, "[%s/%s] ", ev->cat->name,pn);
+    vfprintf(stderr, ev->fmt, ev->ap);
+    fprintf(stderr, "\n");
+}
diff --git a/src/xbt/module.c b/src/xbt/module.c
new file mode 100644 (file)
index 0000000..f21cd23
--- /dev/null
@@ -0,0 +1,59 @@
+/* $Id$ */
+
+/* module handling                                                          */
+
+/* Authors: Martin Quinson                                                  */
+/* Copyright (C) 2003 the OURAGAN project.                                  */
+
+/* This program is free software; you can redistribute it and/or modify it
+   under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include "gras_private.h"
+
+GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(module,GRAS);
+
+extern void gras_log_finalize(void);
+
+struct gras_module_ {
+  gras_dynar_t *deps;
+  gras_cfg_t *cfg;
+  int ref;
+  gras_module_new_fct_t new;
+  gras_module_finalize_fct_t finalize;
+};
+
+
+/**
+ * gras_init:
+ * @argc:
+ * @argv:
+ *
+ * Initialize the gras mecanisms.
+ */
+void
+gras_init(int argc,char **argv) {
+  int i;
+  char *opt;
+  gras_error_t errcode;
+
+  INFO0("Initialize GRAS");
+  for (i=1; i<argc; i++) {
+    if (!strncmp(argv[i],"--gras-log=",strlen("--gras-log="))) {
+      opt=strchr(argv[i],'=');
+      opt++;
+      TRYFAIL(gras_log_control_set(opt));
+    }
+  }
+}
+
+/**
+ * gras_finalize:
+ * @argc:
+ * @argv:
+ *
+ * Finalize the gras mecanisms.
+ */
+void 
+gras_finalize(){
+  gras_log_finalize();
+}