--- /dev/null
+/* $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;
+}
+
--- /dev/null
+/* $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);
+}
+
--- /dev/null
+/* $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;
+}
+
+
--- /dev/null
+/* $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);
+}
+
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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_ */
+
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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";
+ }
+}
+
--- /dev/null
+/* $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);
+}
--- /dev/null
+// $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");
+}
--- /dev/null
+/* $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();
+}