Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Tweak things here and there so that we can propagate exceptions across the network...
[simgrid.git] / src / xbt / config.c
1 /* $Id$ */
2
3 /* config - Dictionnary where the type of each variable is provided.            */
4
5 /* This is useful to build named structs, like option or property sets.     */
6
7 /* Copyright (c) 2001,2002,2003,2004 Martin Quinson. All rights reserved.   */
8
9 /* This program is free software; you can redistribute it and/or modify it
10  * under the terms of the license (GNU LGPL) which comes with this package. */
11
12 #include "xbt/misc.h"
13 #include "xbt/sysdep.h"
14 #include "xbt/log.h"
15 #include "xbt/ex.h"
16 #include "xbt/dynar.h"
17 #include "xbt/dict.h"
18
19 #include "xbt/config.h" /* prototypes of this module */
20
21 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(config,xbt,"configuration support");
22
23 /* xbt_cfgelm_t: the typedef corresponding to a config variable. 
24
25    Both data and DTD are mixed, but fixing it now would prevent me to ever
26    defend my thesis. */
27
28 typedef struct {
29   /* Allowed type of the variable */
30   e_xbt_cfgelm_type_t type;
31   int min,max;
32   
33   /* Callbacks */
34   xbt_cfg_cb_t cb_set;
35   xbt_cfg_cb_t cb_rm;
36
37   /* actual content 
38      (cannot be an union because type host uses both str and i) */
39   xbt_dynar_t content;
40 } s_xbt_cfgelm_t,*xbt_cfgelm_t;
41
42 static const char *xbt_cfgelm_type_name[xbt_cfgelm_type_count]=
43   {"int","double","string","host","any"};
44
45 /* Internal stuff used in cache to free a variable */
46 static void xbt_cfgelm_free(void *data);
47
48 /* Retrieve the variable we'll modify */
49 static xbt_cfgelm_t xbt_cfgelm_get(xbt_cfg_t cfg, const char *name,
50                                    e_xbt_cfgelm_type_t type);
51
52 static void xbt_cfg_str_free(void *d){
53   free(*(void**)d);
54 }
55 static void xbt_cfg_host_free(void *d){
56   xbt_host_t *h=(xbt_host_t*) *(void**)d; 
57   if (h) {
58     if (h->name) free(h->name);
59     free(h);
60   }
61 }
62
63 /*----[ Memory management ]-----------------------------------------------*/
64
65 /** @brief Constructor
66  *
67  * Initialise an config set
68  */
69
70
71 xbt_cfg_t xbt_cfg_new(void) {
72   return (xbt_cfg_t)xbt_dict_new();
73 }
74
75 /** \brief Copy an existing configuration set
76  *
77  * \arg whereto the config set to be created
78  * \arg tocopy the source data
79  *
80  * This only copy the registrations, not the actual content
81  */
82
83 void
84 xbt_cfg_cpy(xbt_cfg_t tocopy,xbt_cfg_t *whereto) {
85   xbt_dict_cursor_t cursor=NULL; 
86   xbt_cfgelm_t variable=NULL;
87   char *name=NULL;
88   
89   *whereto=NULL;
90   xbt_assert0(tocopy,"cannot copy NULL config");
91
92   xbt_dict_foreach((xbt_dict_t)tocopy,cursor,name,variable) {
93     xbt_cfg_register(*whereto, name, variable->type, variable->min, variable->max,
94                      variable->cb_set, variable->cb_rm);
95   }
96 }
97
98 /** @brief Destructor */
99 void xbt_cfg_free(xbt_cfg_t *cfg) {
100   xbt_dict_free((xbt_dict_t*)cfg);
101 }
102
103 /** @brief Dump a config set for debuging purpose
104  *
105  * \arg name The name to give to this config set
106  * \arg indent what to write at the begining of each line (right number of spaces)
107  * \arg cfg the config set
108  */
109 void xbt_cfg_dump(const char *name,const char *indent,xbt_cfg_t cfg) {
110   xbt_dict_t dict = (xbt_dict_t) cfg;
111   xbt_dict_cursor_t cursor=NULL; 
112   xbt_cfgelm_t variable=NULL;
113   char *key=NULL;
114   int i; 
115   int size;
116   int ival;
117   char *sval;
118   double dval;
119   xbt_host_t *hval;
120
121   if (name)
122     printf("%s>> Dumping of the config set '%s':\n",indent,name);
123   xbt_dict_foreach(dict,cursor,key,variable) {
124
125     printf("%s  %s:",indent,key);
126
127     size = xbt_dynar_length(variable->content);
128     printf("%d_to_%d_%s. Actual size=%d. prerm=%p,postset=%p, List of values:\n",
129            variable->min,variable->max,xbt_cfgelm_type_name[variable->type],
130            size,
131            variable->cb_rm, variable->cb_set);
132
133     switch (variable->type) {
134        
135     case xbt_cfgelm_int:
136       for (i=0; i<size; i++) {
137         ival = xbt_dynar_get_as(variable->content,i,int);
138         printf ("%s    %d\n",indent,ival);
139       }
140       break;
141
142     case xbt_cfgelm_double:
143       for (i=0; i<size; i++) {
144         dval = xbt_dynar_get_as(variable->content,i,double);
145         printf ("%s    %f\n",indent,dval);
146       }
147       break;
148
149     case xbt_cfgelm_string:
150       for (i=0; i<size; i++) {
151         sval = xbt_dynar_get_as(variable->content,i,char*);
152         printf ("%s    %s\n",indent,sval);
153       }
154       break;
155
156     case xbt_cfgelm_host:
157       for (i=0; i<size; i++) {
158         hval = xbt_dynar_get_as(variable->content,i,xbt_host_t*);
159         printf ("%s    %s:%d\n",indent,hval->name,hval->port);
160       }
161       break;
162
163     default:
164       printf("%s    Invalid type!!\n",indent);
165     }
166
167   }
168
169   if (name) printf("%s<< End of the config set '%s'\n",indent,name);
170   fflush(stdout);
171
172   xbt_dict_cursor_free(&cursor);
173   return;
174 }
175
176 /*
177  * free an config element
178  */
179
180 void xbt_cfgelm_free(void *data) {
181   xbt_cfgelm_t c=(xbt_cfgelm_t)data;
182
183   if (!c) return;
184   xbt_dynar_free(&(c->content));
185   free(c);
186 }
187
188 /*----[ Registering stuff ]-----------------------------------------------*/
189
190 /** @brief Register an element within a config set
191  *
192  *  @arg cfg the config set
193  *  @arg type the type of the config element
194  *  @arg min the minimum
195  *  @arg max the maximum
196  */
197
198 void
199 xbt_cfg_register(xbt_cfg_t cfg,
200                  const char *name, e_xbt_cfgelm_type_t type,
201                  int min, int max,
202                  xbt_cfg_cb_t cb_set,  xbt_cfg_cb_t cb_rm){
203   xbt_cfgelm_t res;
204   xbt_ex_t e;
205   int found=0;
206
207   xbt_assert(cfg);
208   xbt_assert4(type>=xbt_cfgelm_int && type<=xbt_cfgelm_host,
209               "type of %s not valid (%d should be between %d and %d)",
210               name,type,xbt_cfgelm_int, xbt_cfgelm_host);
211   DEBUG5("Register cfg elm %s (%d to %d %s (=%d))",
212          name,min,max,xbt_cfgelm_type_name[type],type);
213   TRY {
214     res = xbt_dict_get((xbt_dict_t)cfg,name);
215   } CATCH(e) {
216     if (e.category == not_found_error) {
217       found = 1;
218       xbt_ex_free(e);
219     } else {
220       RETHROW;
221     }
222   }
223
224   if (!found) {
225     WARN1("Config elem %s registered twice.",name);
226     /* Will be removed by the insertion of the new one */
227   } 
228
229   res=xbt_new(s_xbt_cfgelm_t,1);
230
231   res->type=type;
232   res->min=min;
233   res->max=max;
234   res->cb_set = cb_set;
235   res->cb_rm  = cb_rm;
236
237   switch (type) {
238   case xbt_cfgelm_int:
239     res->content = xbt_dynar_new(sizeof(int), NULL);
240     break;
241
242   case xbt_cfgelm_double:
243     res->content = xbt_dynar_new(sizeof(double), NULL);
244     break;
245
246   case xbt_cfgelm_string:
247    res->content = xbt_dynar_new(sizeof(char*),&xbt_cfg_str_free);
248    break;
249
250   case xbt_cfgelm_host:
251    res->content = xbt_dynar_new(sizeof(xbt_host_t*),&xbt_cfg_host_free);
252    break;
253
254   default:
255     ERROR1("%d is an invalide type code",type);
256   }
257     
258   xbt_dict_set((xbt_dict_t)cfg,name,res,&xbt_cfgelm_free);
259 }
260
261 /** @brief Unregister an element from a config set. 
262  * 
263  *  @arg cfg the config set
264  *  @arg name the name of the elem to be freed
265  * 
266  *  Note that it removes both the description and the actual content.
267  *  Throws not_found when no such element exists.
268  */
269
270 void
271 xbt_cfg_unregister(xbt_cfg_t cfg,const char *name) {
272   xbt_dict_remove((xbt_dict_t)cfg,name);
273 }
274
275 /**
276  * @brief Parse a string and register the stuff described.
277  *
278  * @arg cfg the config set
279  * @arg entry a string describing the element to register
280  *
281  * The string may consist in several variable descriptions separated by a space. 
282  * Each of them must use the following syntax: \<name\>:\<min nb\>_to_\<max nb\>_\<type\>
283  * with type being one of  'string','int', 'host' or 'double'.
284  */
285
286 void
287 xbt_cfg_register_str(xbt_cfg_t cfg,const char *entry) {
288   char *entrycpy=xbt_strdup(entry);
289   char *tok;
290
291   int min,max;
292   e_xbt_cfgelm_type_t type;
293
294   tok=strchr(entrycpy, ':');
295   xbt_assert2(tok,"Invalid config element descriptor: %s%s",
296               entry,
297               "; Should be <name>:<min nb>_to_<max nb>_<type>");
298   *(tok++)='\0';
299
300   min=strtol(tok, &tok, 10);
301   xbt_assert1(tok,"Invalid minimum in config element descriptor %s",entry);
302
303   xbt_assert2(strcmp(tok,"_to_"),
304               "Invalid config element descriptor : %s%s",           
305               entry,
306               "; Should be <name>:<min nb>_to_<max nb>_<type>");
307   tok += strlen("_to_");
308
309   max=strtol(tok, &tok, 10);
310   xbt_assert1(tok,"Invalid maximum in config element descriptor %s",entry);
311
312   xbt_assert2( *(tok++) =='_',
313                "Invalid config element descriptor: %s%s",entry,
314                "; Should be <name>:<min nb>_to_<max nb>_<type>");
315
316   for (type=0; 
317        type<xbt_cfgelm_type_count && strcmp(tok,xbt_cfgelm_type_name[type]); 
318        type++);
319   xbt_assert2(type < xbt_cfgelm_type_count,
320           "Invalid type in config element descriptor: %s%s",entry,
321           "; Should be one of 'string', 'int', 'host' or 'double'.");
322
323   xbt_cfg_register(cfg,entrycpy,type,min,max,NULL,NULL);
324
325   free(entrycpy); /* strdup'ed by dict mechanism, but cannot be const */
326 }
327
328 /** @brief Check that each variable have the right amount of values */
329
330 void
331 xbt_cfg_check(xbt_cfg_t cfg) {
332   xbt_dict_cursor_t cursor; 
333   xbt_cfgelm_t variable;
334   char *name;
335   int size;
336
337   xbt_assert0(cfg,"NULL config set.");
338
339   xbt_dict_foreach((xbt_dict_t)cfg,cursor,name,variable) {
340     size = xbt_dynar_length(variable->content);
341     if (variable->min > size) { 
342       xbt_dict_cursor_free(&cursor);
343       THROW4(mismatch_error,0,
344              "Config elem %s needs at least %d %s, but there is only %d values.",
345              name,
346              variable->min,
347              xbt_cfgelm_type_name[variable->type],
348              size); 
349     }
350
351     if (variable->max > 0 && variable->max < size) {
352       xbt_dict_cursor_free(&cursor);
353       THROW4(mismatch_error,0,
354              "Config elem %s accepts at most %d %s, but there is %d values.",
355              name,
356              variable->max,
357              xbt_cfgelm_type_name[variable->type],
358              size);
359     }
360   }
361
362   xbt_dict_cursor_free(&cursor);
363 }
364
365 static xbt_cfgelm_t xbt_cfgelm_get(xbt_cfg_t  cfg,
366                                    const char *name,
367                                    e_xbt_cfgelm_type_t type){
368   xbt_cfgelm_t res=NULL;
369   xbt_ex_t e;
370
371   TRY {
372     res = xbt_dict_get((xbt_dict_t)cfg,name);
373   } CATCH(e) {
374     if (e.category == not_found_error) {
375       xbt_ex_free(e);
376       THROW1(not_found_error,0,
377              "No registered variable '%s' in this config set",name);
378     }
379     RETHROW;
380   }
381
382   xbt_assert3(type == xbt_cfgelm_any || res->type == type,
383                "You tried to access to the config element %s as an %s, but its type is %s.",
384                name,
385                xbt_cfgelm_type_name[type],
386                xbt_cfgelm_type_name[res->type]);
387
388   return res;
389 }
390
391 /** @brief Get the type of this variable in that configuration set
392  *
393  * \arg cfg the config set
394  * \arg name the name of the element 
395  * \arg type the result
396  *
397  */
398
399 e_xbt_cfgelm_type_t 
400 xbt_cfg_get_type(xbt_cfg_t cfg, const char *name) {
401
402   xbt_cfgelm_t variable = NULL;
403   xbt_ex_t e;
404
405   TRY {
406     variable = xbt_dict_get((xbt_dict_t)cfg,name);
407   } CATCH(e) {
408     if (e.category == not_found_error) { 
409       xbt_ex_free(e);
410       THROW1(not_found_error,0,
411              "Can't get the type of '%s' since this variable does not exist",name);
412     }
413     RETHROW;
414   }
415
416   INFO1("type in variable = %d",variable->type);
417
418   return variable->type;
419 }
420
421 /*----[ Setting ]---------------------------------------------------------*/
422 /**  @brief va_args version of xbt_cfg_set
423  *
424  * \arg cfg config set to fill
425  * \arg n   variable name
426  * \arg pa  variable value
427  *
428  * Add some values to the config set.
429  */
430 void
431 xbt_cfg_set_vargs(xbt_cfg_t cfg, const char *name, va_list pa) {
432   char *str;
433   int i;
434   double d;
435   e_xbt_cfgelm_type_t type=0; /* Set a dummy value to make gcc happy. It cannot get uninitialized */
436
437   xbt_ex_t e;
438   
439   TRY {
440     type = xbt_cfg_get_type(cfg,name);
441   } CATCH(e) {
442     if (e.category == not_found_error) {
443       xbt_ex_free(e);
444       THROW1(not_found_error,0,"Can't set the property '%s' since it's not registered",name);
445     }
446     RETHROW;
447   }
448
449   switch (type) {
450   case xbt_cfgelm_host:
451     str = va_arg(pa, char *);
452     i=va_arg(pa,int);
453     xbt_cfg_set_host(cfg,name,str,i);
454     break;
455       
456   case xbt_cfgelm_string:
457     str=va_arg(pa, char *);
458     xbt_cfg_set_string(cfg, name, str);
459     break;
460
461   case xbt_cfgelm_int:
462     i=va_arg(pa,int);
463     xbt_cfg_set_int(cfg,name,i);
464     break;
465
466   case xbt_cfgelm_double:
467     d=va_arg(pa,double);
468     xbt_cfg_set_double(cfg,name,d);
469     break;
470
471   default:
472     xbt_assert2(0,"Config element variable %s not valid (type=%d)",name,type);
473   }
474 }
475
476 /** @brief Add a NULL-terminated list of pairs {(char*)key, value} to the set
477  *
478  * \arg cfg config set to fill
479  * \arg name variable name
480  * \arg varargs variable value
481  *
482  */
483 void xbt_cfg_set(xbt_cfg_t cfg, const char *name, ...) {
484   va_list pa;
485
486   va_start(pa,name);
487   xbt_cfg_set_vargs(cfg,name,pa);
488   va_end(pa);
489 }
490
491 /** @brief Add values parsed from a string into a config set
492  *
493  * \arg cfg config set to fill
494  * \arg options a string containing the content to add to the config set. This
495  * is a '\\t',' ' or '\\n' separated list of variables. Each individual variable is
496  * like "[name]:[value]" where [name] is the name of an already registred 
497  * variable, and [value] conforms to the data type under which this variable was
498  * registred.
499  *
500  * @todo This is a crude manual parser, it should be a proper lexer.
501  */
502
503 void
504 xbt_cfg_set_parse(xbt_cfg_t cfg, const char *options) {
505   xbt_ex_t e;
506
507   int i;
508   double d;
509   char *str;
510
511   xbt_cfgelm_t variable=NULL;
512   char *optionlist_cpy;
513   char *option,  *name,*val;
514
515   int len;
516
517   XBT_IN;
518   if (!options || !strlen(options)) { /* nothing to do */
519     return;
520   }
521   optionlist_cpy=xbt_strdup(options);
522
523   DEBUG1("List to parse and set:'%s'",options);
524   option=optionlist_cpy;
525   while (1) { /* breaks in the code */
526
527     if (!option) 
528       break;
529     name=option;
530     len=strlen(name);
531     DEBUG3("Still to parse and set: '%s'. len=%d; option-name=%ld",
532            name,len,(long)(option-name));
533
534     /* Pass the value */
535     while (option-name<=(len-1) && *option != ' ' && *option != '\n' && *option != '\t') {
536       DEBUG1("Take %c.",*option);
537       option++;
538     }
539     if (option-name == len) {
540       DEBUG0("Boundary=EOL");
541       option=NULL; /* don't do next iteration */
542
543     } else {
544       DEBUG3("Boundary on '%c'. len=%d;option-name=%ld",
545              *option,len,(long)(option-name));
546
547       /* Pass the following blank chars */
548       *(option++)='\0';
549       while (option-name<(len-1) && 
550              (*option == ' ' || *option == '\n' || *option == '\t')) {
551         /*      fprintf(stderr,"Ignore a blank char.\n");*/
552         option++;
553       }
554       if (option-name == len-1)
555         option=NULL; /* don't do next iteration */
556     }
557     DEBUG2("parse now:'%s'; parse later:'%s'",name,option);
558
559     if (name[0] == ' ' || name[0] == '\n' || name[0] == '\t')
560       continue;
561     if (!strlen(name))
562       break;
563     
564     val=strchr(name,':');
565     if (!val) {
566       free(optionlist_cpy);
567       xbt_assert1(FALSE,
568              "Option '%s' badly formated. Should be of the form 'name:value'",
569                   name);
570     }
571     *(val++)='\0';
572
573     DEBUG2("name='%s';val='%s'",name,val);
574
575     TRY {
576       variable = xbt_dict_get((xbt_dict_t)cfg,name);
577     } CATCH(e) {
578       /* put it back on what won't get freed, ie within "options" and out of "optionlist_cpy" */
579       name = (char*) (optionlist_cpy-name + options); 
580       free(optionlist_cpy);
581       if (e.category == not_found_error) {
582         xbt_ex_free(e);
583         THROW1(not_found_error,0,"No registrated variable corresponding to '%s'.",name);
584       }
585       RETHROW;
586     }
587
588     TRY {
589       switch (variable->type) {
590       case xbt_cfgelm_string:
591         xbt_cfg_set_string(cfg, name, val); /* throws */
592         break;
593
594       case xbt_cfgelm_int:
595         i=strtol(val, &val, 0);
596         if (val==NULL) {
597           free(optionlist_cpy); 
598           xbt_assert1(FALSE,
599                       "Value of option %s not valid. Should be an integer",
600                       name);
601         }
602
603         xbt_cfg_set_int(cfg,name,i); /* throws */
604         break;
605
606       case xbt_cfgelm_double:
607         d=strtod(val, &val);
608         if (val==NULL) {
609           free(optionlist_cpy); 
610           xbt_assert1(FALSE,
611                       "Value of option %s not valid. Should be a double",
612                       name);
613         }
614
615         xbt_cfg_set_double(cfg,name,d); /* throws */
616         break;
617
618       case xbt_cfgelm_host:
619         str=val;
620         val=strchr(val,':');
621         if (!val) {
622           free(optionlist_cpy); 
623           xbt_assert1(FALSE,
624                       "Value of option %s not valid. Should be an host (machine:port)",
625                       name);
626         }
627         
628         *(val++)='\0';
629         i=strtol(val, &val, 0);
630         if (val==NULL) {
631           free(optionlist_cpy); 
632           xbt_assert1(FALSE,
633                       "Value of option %s not valid. Should be an host (machine:port)",
634                       name);
635         }
636
637         xbt_cfg_set_host(cfg,name,str,i); /* throws */ 
638         break;      
639
640       default: 
641         THROW1(unknown_error,0,"Type of config element %s is not valid.",name);
642       }
643     } CATCH(e) {
644       free(optionlist_cpy);
645       RETHROW;
646     }
647   }
648   free(optionlist_cpy);
649
650 }
651
652 /** @brief Set or add an integer value to \a name within \a cfg
653  *
654  * \arg cfg the config set
655  * \arg name the name of the variable
656  * \arg val the value of the variable
657  */ 
658 void
659 xbt_cfg_set_int(xbt_cfg_t cfg,const char*name, int val) {
660   xbt_cfgelm_t variable;
661
662   VERB2("Configuration setting: %s=%d",name,val);
663   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_int);
664
665   if (variable->max == 1) {
666     if (variable->cb_rm && xbt_dynar_length(variable->content))
667       (*variable->cb_rm)(name, 0);
668           
669     xbt_dynar_set(variable->content,0,&val);
670   } else {
671     if (variable->max && xbt_dynar_length(variable->content) == variable->max)
672       THROW3(mismatch_error,0,
673              "Cannot add value %d to the config element %s since it's already full (size=%d)",
674              val,name,variable->max); 
675              
676     xbt_dynar_push(variable->content,&val);
677   }
678
679   if (variable->cb_set)
680     (*variable->cb_set)(name, xbt_dynar_length(variable->content) -1);
681 }
682
683 /** @brief Set or add a double value to \a name within \a cfg
684  * 
685  * \arg cfg the config set
686  * \arg name the name of the variable
687  * \arg val the doule to set
688  */ 
689
690 void
691 xbt_cfg_set_double(xbt_cfg_t cfg,const char*name, double val) {
692   xbt_cfgelm_t variable;
693
694   VERB2("Configuration setting: %s=%f",name,val);
695   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_double);
696
697   if (variable->max == 1) {
698     if (variable->cb_rm && xbt_dynar_length(variable->content))
699       (*variable->cb_rm)(name, 0);
700           
701     xbt_dynar_set(variable->content,0,&val);
702   } else {
703     if (variable->max && xbt_dynar_length(variable->content) == variable->max)
704       THROW3(mismatch_error,0,
705              "Cannot add value %f to the config element %s since it's already full (size=%d)",
706              val,name,variable->max); 
707              
708     xbt_dynar_push(variable->content,&val);
709   }
710
711   if (variable->cb_set)
712     (*variable->cb_set)(name, xbt_dynar_length(variable->content) -1);
713 }
714
715 /** @brief Set or add a string value to \a name within \a cfg
716  * 
717  * \arg cfg the config set
718  * \arg name the name of the variable
719  * \arg val the value to be added
720  *
721  */ 
722
723 void
724 xbt_cfg_set_string(xbt_cfg_t cfg,const char*name, const char*val) { 
725   xbt_cfgelm_t variable;
726   char *newval = xbt_strdup(val);
727
728   VERB2("Configuration setting: %s=%s",name,val);
729   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_string);
730
731   if (variable->max == 1) {
732     if (variable->cb_rm && xbt_dynar_length(variable->content))
733       (*variable->cb_rm)(name, 0);
734           
735     xbt_dynar_set(variable->content,0,&newval);
736   } else {
737     if (variable->max && xbt_dynar_length(variable->content) == variable->max)
738       THROW3(mismatch_error,0,
739              "Cannot add value %s to the config element %s since it's already full (size=%d)",
740              name,val,variable->max); 
741              
742     xbt_dynar_push(variable->content,&newval);
743   }
744
745   if (variable->cb_set)
746     (*variable->cb_set)(name, xbt_dynar_length(variable->content) -1);
747 }
748
749 /** @brief Set or add an host value to \a name within \a cfg
750  * 
751  * \arg cfg the config set
752  * \arg name the name of the variable
753  * \arg host the host
754  * \arg port the port number
755  *
756  * \e host values are composed of a string (hostname) and an integer (port)
757  */ 
758
759 void
760 xbt_cfg_set_host(xbt_cfg_t cfg,const char*name, 
761                   const char *host,int port) {
762   xbt_cfgelm_t variable;
763   xbt_host_t *val=xbt_new(xbt_host_t,1);
764
765   VERB3("Configuration setting: %s=%s:%d",name,host,port);
766
767   val->name = xbt_strdup(name);
768   val->port = port;
769
770   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_host);
771
772   if (variable->max == 1) {
773     if (variable->cb_rm && xbt_dynar_length(variable->content))
774       (*variable->cb_rm)(name, 0);
775           
776     xbt_dynar_set(variable->content,0,&val);
777   } else {
778     if (variable->max && xbt_dynar_length(variable->content) == variable->max)
779       THROW4(mismatch_error,0,
780              "Cannot add value %s:%d to the config element %s since it's already full (size=%d)",
781              host,port,name,variable->max); 
782              
783     xbt_dynar_push(variable->content,&val);
784   }
785
786   if (variable->cb_set)
787     (*variable->cb_set)(name, xbt_dynar_length(variable->content) -1);
788 }
789
790 /* ---- [ Removing ] ---- */
791
792 /** @brief Remove the provided \e val integer value from a variable
793  *
794  * \arg cfg the config set
795  * \arg name the name of the variable
796  * \arg val the value to be removed
797  */
798 void xbt_cfg_rm_int(xbt_cfg_t cfg,const char*name, int val) {
799
800   xbt_cfgelm_t variable;
801   int cpt,seen;
802
803   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_int);
804   
805   if (xbt_dynar_length(variable->content) == variable->min)
806     THROW3(mismatch_error,0,
807            "Cannot remove value %d from the config element %s since it's already at its minimal size (=%d)",
808            val,name,variable->min); 
809
810   xbt_dynar_foreach(variable->content,cpt,seen) {
811     if (seen == val) {
812       if (variable->cb_rm) (*variable->cb_rm)(name, cpt);
813       xbt_dynar_cursor_rm(variable->content,&cpt);
814       return;
815     }
816   }
817
818   THROW2(not_found_error,0,
819          "Can't remove the value %d of config element %s: value not found.",val,name);
820 }
821
822 /** @brief Remove the provided \e val double value from a variable
823  *
824  * \arg cfg the config set
825  * \arg name the name of the variable
826  * \arg val the value to be removed
827  */
828
829 void xbt_cfg_rm_double(xbt_cfg_t cfg,const char*name, double val) {
830   xbt_cfgelm_t variable;
831   int cpt;
832   double seen;
833
834   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_double);
835   
836   if (xbt_dynar_length(variable->content) == variable->min)
837     THROW3(mismatch_error,0,
838            "Cannot remove value %f from the config element %s since it's already at its minimal size (=%d)",
839            val,name,variable->min); 
840
841   xbt_dynar_foreach(variable->content,cpt,seen) {
842     if (seen == val) {
843       xbt_dynar_cursor_rm(variable->content,&cpt);
844       if (variable->cb_rm)
845         (*variable->cb_rm)(name, cpt);
846       return;
847     }
848   }
849
850   THROW2(not_found_error,0,
851          "Can't remove the value %f of config element %s: value not found.",val,name);
852 }
853
854 /** @brief Remove the provided \e val string value from a variable
855  *
856  * \arg cfg the config set
857  * \arg name the name of the variable
858  * \arg val the value of the string which will be removed
859  */
860 void
861 xbt_cfg_rm_string(xbt_cfg_t cfg,const char*name, const char *val) {
862   xbt_cfgelm_t variable;
863   int cpt;
864   char *seen;
865
866   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_string);
867   
868   if (xbt_dynar_length(variable->content) == variable->min)
869     THROW3(mismatch_error,0,
870            "Cannot remove value %s from the config element %s since it's already at its minimal size (=%d)",
871            name,val,variable->min); 
872            
873   xbt_dynar_foreach(variable->content,cpt,seen) {
874     if (!strcpy(seen,val)) {
875       if (variable->cb_rm)
876         (*variable->cb_rm)(name, cpt);
877       xbt_dynar_cursor_rm(variable->content,&cpt);
878       return;
879     }
880   }
881
882   THROW2(not_found_error,0,
883          "Can't remove the value %s of config element %s: value not found.",val,name);
884 }
885
886 /** @brief Remove the provided \e val host value from a variable
887  * 
888  * \arg cfg the config set
889  * \arg name the name of the variable
890  * \arg host the hostname
891  * \arg port the port number
892  */
893
894 void
895 xbt_cfg_rm_host(xbt_cfg_t cfg,const char*name, const char *host,int port) {
896   xbt_cfgelm_t variable;
897   int cpt;
898   xbt_host_t *seen;
899
900   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_host);
901   
902   if (xbt_dynar_length(variable->content) == variable->min)
903     THROW4(mismatch_error,0,
904            "Cannot remove value %s:%d from the config element %s since it's already at its minimal size (=%d)",
905            host,port,name,variable->min); 
906            
907   xbt_dynar_foreach(variable->content,cpt,seen) {
908     if (!strcpy(seen->name,host) && seen->port == port) {
909       if (variable->cb_rm) (*variable->cb_rm)(name, cpt);
910       xbt_dynar_cursor_rm(variable->content,&cpt);
911       return;
912     }
913   }
914
915   THROW3(not_found_error,0,
916          "Can't remove the value %s:%d of config element %s: value not found.",
917          host,port,name);
918 }
919
920 /** @brief Remove the \e pos th value from the provided variable */
921
922 void xbt_cfg_rm_at   (xbt_cfg_t cfg, const char *name, int pos) {
923
924   xbt_cfgelm_t variable;
925
926   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_any);
927   
928   if (xbt_dynar_length(variable->content) == variable->min)
929     THROW3(mismatch_error,0,
930            "Cannot remove %dth value from the config element %s since it's already at its minimal size (=%d)",
931            pos,name,variable->min); 
932   
933   if (variable->cb_rm) (*variable->cb_rm)(name, pos);     
934   xbt_dynar_remove_at(variable->content, pos, NULL);
935 }
936
937 /** @brief Remove all the values from a variable
938  * 
939  * \arg cfg the config set
940  * \arg name the name of the variable
941  */
942
943 void
944 xbt_cfg_empty(xbt_cfg_t cfg,const char*name) {
945   xbt_cfgelm_t variable=NULL;
946   xbt_ex_t e;
947
948   TRY {
949     variable = xbt_dict_get((xbt_dict_t)cfg,name);
950   } CATCH(e) {
951     if (e.category != not_found_error)
952       RETHROW;
953
954     xbt_ex_free(e);
955     THROW1(not_found_error,0,
956            "Can't empty  '%s' since this config element does not exist", name);
957   }
958
959   if (variable) {
960     if (variable->cb_rm) {
961       int cpt;
962       void *ignored;
963       xbt_dynar_foreach(variable->content,cpt,ignored) {
964         (*variable->cb_rm)(name, cpt);
965       }
966     }
967     xbt_dynar_reset(variable->content);
968   }
969 }
970
971 /*----[ Getting ]---------------------------------------------------------*/
972
973 /** @brief Retrieve an integer value of a variable (get a warning if not uniq)
974  *
975  * \arg cfg the config set
976  * \arg name the name of the variable
977  * \arg val the wanted value
978  *
979  * Returns the first value from the config set under the given name.
980  * If there is more than one value, it will issue a warning. Consider using 
981  * xbt_cfg_get_dynar() instead.
982  *
983  * \warning the returned value is the actual content of the config set
984  */
985 int 
986 xbt_cfg_get_int (xbt_cfg_t  cfg, const char *name) {
987   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_int);
988
989   if (xbt_dynar_length(variable->content) > 1) {
990     WARN2("You asked for the first value of the config element '%s', but there is %lu values",
991           name, xbt_dynar_length(variable->content));
992   }
993
994   return xbt_dynar_get_as(variable->content, 0, int);
995 }
996
997 /** @brief Retrieve a double value of a variable (get a warning if not uniq)
998  * 
999  * \arg cfg the config set
1000  * \arg name the name of the variable
1001  * \arg val the wanted value
1002  *
1003  * Returns the first value from the config set under the given name.
1004  * If there is more than one value, it will issue a warning. Consider using 
1005  * xbt_cfg_get_dynar() instead.
1006  *
1007  * \warning the returned value is the actual content of the config set
1008  */
1009
1010 double
1011 xbt_cfg_get_double(xbt_cfg_t  cfg, const char *name) {
1012   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_double);
1013
1014   if (xbt_dynar_length(variable->content) > 1) {
1015     WARN2("You asked for the first value of the config element '%s', but there is %lu values\n",
1016              name, xbt_dynar_length(variable->content));
1017   }
1018
1019   return xbt_dynar_get_as(variable->content, 0, double);
1020 }
1021
1022 /** @brief Retrieve a string value of a variable (get a warning if not uniq)
1023  *
1024  * \arg th the config set
1025  * \arg name the name of the variable
1026  * \arg val the wanted value
1027  *
1028  * Returns the first value from the config set under the given name.
1029  * If there is more than one value, it will issue a warning. Consider using 
1030  * xbt_cfg_get_dynar() instead.
1031  *
1032  * \warning the returned value is the actual content of the config set
1033  */
1034
1035 char* xbt_cfg_get_string(xbt_cfg_t  cfg, const char *name) {
1036   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_string);
1037
1038   if (xbt_dynar_length(variable->content) > 1) {
1039     WARN2("You asked for the first value of the config element '%s', but there is %lu values\n",
1040           name, xbt_dynar_length(variable->content));
1041   }
1042
1043   return xbt_dynar_get_as(variable->content, 0, char *);
1044 }
1045
1046 /** @brief Retrieve an host value of a variable (get a warning if not uniq)
1047  *
1048  * \arg cfg the config set
1049  * \arg name the name of the variable
1050  * \arg host the host
1051  * \arg port the port number
1052  *
1053  * Returns the first value from the config set under the given name.
1054  * If there is more than one value, it will issue a warning. Consider using
1055  * xbt_cfg_get_dynar() instead.
1056  *
1057  * \warning the returned value is the actual content of the config set
1058  */
1059
1060 void xbt_cfg_get_host  (xbt_cfg_t   cfg,  const char *name,
1061                         char      **host, int        *port) {
1062   xbt_cfgelm_t variable;
1063   xbt_host_t  *val;
1064
1065   variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_host);
1066
1067   if (xbt_dynar_length(variable->content) > 1) {
1068     WARN2("You asked for the first value of the config element '%s', but there is %lu values\n",
1069              name, xbt_dynar_length(variable->content));
1070   }
1071
1072   val = xbt_dynar_get_as(variable->content, 0, xbt_host_t*);
1073   *host=val->name;
1074   *port=val->port;
1075 }
1076
1077 /** @brief Retrieve the dynar of all the values stored in a variable
1078  * 
1079  * \arg cfg where to search in
1080  * \arg name what to search for
1081  * \arg dynar result
1082  *
1083  * Get the data stored in the config set. 
1084  *
1085  * \warning the returned value is the actual content of the config set
1086  */
1087 xbt_dynar_t xbt_cfg_get_dynar (xbt_cfg_t    cfg, const char *name) {
1088   xbt_cfgelm_t variable=NULL;
1089   xbt_ex_t     e;
1090
1091   TRY {
1092     variable = xbt_dict_get((xbt_dict_t)cfg,name);
1093   } CATCH(e) {
1094     if (e.category == not_found_error) {
1095       xbt_ex_free(e);
1096       THROW1(not_found_error,0,
1097              "No registered variable %s in this config set",name);
1098     }
1099     RETHROW;
1100   }
1101
1102   return variable->content;
1103 }
1104
1105
1106 /** @brief Retrieve one of the integer value of a variable */
1107 int
1108 xbt_cfg_get_int_at(xbt_cfg_t cfg, const char *name, int pos) {
1109                   
1110   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_int);
1111   return xbt_dynar_get_as(variable->content, pos, int);
1112 }
1113
1114 /** @brief Retrieve one of the double value of a variable */
1115 double
1116 xbt_cfg_get_double_at(xbt_cfg_t cfg, const char *name, int pos) {
1117                   
1118   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_double);
1119   return xbt_dynar_get_as(variable->content, pos, double);
1120 }
1121
1122
1123 /** @brief Retrieve one of the string value of a variable */
1124 char*
1125 xbt_cfg_get_string_at(xbt_cfg_t cfg, const char *name, int pos) {
1126                   
1127   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_string);
1128   return xbt_dynar_get_as(variable->content, pos, char*);
1129 }
1130
1131 /** @brief Retrieve one of the host value of a variable */
1132 void
1133 xbt_cfg_get_host_at(xbt_cfg_t cfg, const char *name, int pos,
1134                     char **host, int *port) {
1135                   
1136   xbt_cfgelm_t variable = xbt_cfgelm_get(cfg,name,xbt_cfgelm_int);
1137   xbt_host_t *val = xbt_dynar_get_ptr(variable->content, pos);
1138
1139   *port = val->port;
1140   *host = val->name;
1141 }
1142
1143
1144 #ifdef SIMGRID_TEST
1145 #include "xbt.h"
1146 #include "xbt/ex.h"
1147
1148 XBT_TEST_SUITE("config","Configuration support");
1149
1150 static xbt_cfg_t make_set(){
1151   xbt_cfg_t set=NULL; 
1152
1153   set = xbt_cfg_new();
1154   xbt_cfg_register_str(set,"speed:1_to_2_int");
1155   xbt_cfg_register_str(set,"hostname:1_to_1_string");
1156   xbt_cfg_register_str(set,"user:1_to_10_string");
1157
1158   return set;
1159 } /* end_of_make_set */
1160
1161 XBT_TEST_UNIT("memuse",test_config_memuse,"Alloc and free a config set") {
1162   xbt_cfg_t set=make_set();
1163   xbt_test_add0("Alloc and free a config set");
1164   xbt_cfg_set_parse(set, "hostname:veloce user:mquinson\nuser:oaumage\tuser:alegrand");
1165   xbt_cfg_free(&set);
1166   xbt_cfg_free(&set);
1167 }
1168
1169 XBT_TEST_UNIT("validation",test_config_validation,"Validation tests") {
1170   xbt_cfg_t set = set=make_set();
1171   xbt_ex_t e;
1172   
1173   xbt_test_add0("Having too few elements for speed");
1174   xbt_cfg_set_parse(set, "hostname:veloce user:mquinson\nuser:oaumage\tuser:alegrand");
1175   TRY {
1176     xbt_cfg_check(set);
1177   } CATCH(e) {
1178     if (e.category != mismatch_error || 
1179         strncmp(e.msg,"Config elem speed needs",strlen("Config elem speed needs")))
1180       xbt_test_fail1("Got an exception. msg=%s",e.msg);
1181     xbt_ex_free(e);
1182   }
1183   xbt_cfg_free(&set);
1184   xbt_cfg_free(&set);
1185
1186
1187
1188   xbt_test_add0("Having too much values of 'speed'");
1189   set=make_set(); 
1190   xbt_cfg_set_parse(set,"hostname:toto:42 user:alegrand");
1191   TRY {
1192     xbt_cfg_set_parse(set,"speed:42 speed:24 speed:34");
1193   } CATCH(e) {
1194     if (e.category != mismatch_error ||
1195         strncmp(e.msg,"Cannot add value 34 to the config elem speed",
1196                 strlen("Config elem speed needs")))
1197       xbt_test_fail1("Got an exception. msg=%s",e.msg);
1198     xbt_ex_free(e);
1199   }
1200   xbt_cfg_check(set);
1201   xbt_cfg_free(&set);
1202   xbt_cfg_free(&set);
1203
1204 }
1205
1206 XBT_TEST_UNIT("use",test_config_use,"Data retrieving tests") {
1207   xbt_cfg_t set = set=make_set();
1208  
1209
1210   xbt_test_add0("Get a single value");
1211   {     
1212     /* get_single_value */
1213     int ival;
1214     xbt_cfg_t myset=make_set();
1215     
1216     xbt_cfg_set_parse(myset,"hostname:toto:42 speed:42");
1217     ival = xbt_cfg_get_int(myset,"speed"); 
1218     if (ival != 42) 
1219       xbt_test_fail1("Speed value = %d, I expected 42",ival);
1220     xbt_cfg_free(&myset);
1221   }
1222
1223   xbt_test_add0("Get multiple values");
1224   {     
1225     /* get_multiple_value */
1226     xbt_dynar_t dyn; 
1227     xbt_cfg_t myset=make_set();
1228     
1229     xbt_cfg_set_parse(myset, "hostname:veloce user:foo\nuser:bar\tuser:toto");
1230     xbt_cfg_set_parse(myset,"speed:42");
1231     xbt_cfg_check(myset); 
1232     dyn = xbt_cfg_get_dynar(myset,"user");
1233
1234     if (xbt_dynar_length(dyn) != 3) 
1235       xbt_test_fail1("Dynar length = %d, I expected 3", (int)xbt_dynar_length(dyn));
1236
1237     if (strcmp(xbt_dynar_get_as(dyn,0,char*),"foo"))
1238       xbt_test_fail1("Dynar[0] = %s, I expected foo",   xbt_dynar_get_as(dyn,0,char*));
1239
1240     if (strcmp(xbt_dynar_get_as(dyn,1,char*),"bar"))
1241       xbt_test_fail1("Dynar[1] = %s, I expected bar",   xbt_dynar_get_as(dyn,1,char*));
1242
1243     if (strcmp(xbt_dynar_get_as(dyn,2,char*),"toto"))
1244       xbt_test_fail1("Dynar[2] = %s, I expected toto",  xbt_dynar_get_as(dyn,2,char*));
1245     xbt_cfg_free(&myset);    
1246   }
1247   
1248   xbt_test_add0("Access to a non-existant entry");
1249   {     
1250     /* non-existant_entry */
1251     xbt_cfg_t myset=make_set();
1252     xbt_ex_t e;
1253     
1254     TRY {
1255       xbt_cfg_set_parse(myset, "color:blue");
1256     } CATCH(e) {
1257       if (e.category != not_found_error)
1258         xbt_test_exception(e);
1259       xbt_ex_free(e);
1260     }
1261     xbt_cfg_free(&myset);    
1262   }
1263 }
1264 #endif /* SIMGRID_TEST */