Logo AND Algorithmique Numérique Distribuée

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