Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of git+ssh://scm.gforge.inria.fr//gitroot/simgrid/simgrid
[simgrid.git] / src / xbt / config.cpp
1 /* Copyright (c) 2004-2014,2016. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 #include <stdio.h>
5
6 #include <cerrno>
7 #include <cstring>
8 #include <climits>
9 #include <functional>
10 #include <stdexcept>
11 #include <string>
12 #include <type_traits>
13
14 #include <xbt/config.h>
15 #include <xbt/config.hpp>
16 #include "xbt/misc.h"
17 #include "xbt/sysdep.h"
18 #include "xbt/log.h"
19 #include "xbt/ex.h"
20 #include "xbt/dynar.h"
21 #include "xbt/dict.h"
22
23 // *****
24
25 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_cfg, xbt, "configuration support");
26
27 XBT_EXPORT_NO_IMPORT(xbt_cfg_t) simgrid_config = NULL;
28
29 namespace {
30
31 static inline
32 void increment(e_xbt_cfgelm_type_t& type)
33 {
34   typedef std::underlying_type<e_xbt_cfgelm_type_t>::type underlying_type;
35   type = (e_xbt_cfgelm_type_t) ((underlying_type) type + 1);
36 }
37
38 }
39
40 /* xbt_cfgelm_t: the typedef corresponding to a config variable. */
41
42 typedef struct s_xbt_cfgelm_t {
43   /* Description */
44   std::string desc;
45
46   /* Allowed type of the variable */
47   e_xbt_cfgelm_type_t type;
48   bool isdefault = true;
49
50   /* Callback */
51   xbt_cfg_cb_t cb_set = nullptr;
52
53   /* Advanced callback (for xbt_cfgelm_string only) */
54   std::function<void(const char* value)> callback;
55
56   /* actual content (could be an union or something) */
57   xbt_dynar_t content = nullptr;
58
59   ~s_xbt_cfgelm_t()
60   {
61     XBT_DEBUG("Frees cfgelm %p", this);
62     if (this->type != xbt_cfgelm_alias)
63       xbt_dynar_free(&(this->content));
64   }
65
66 } s_xbt_cfgelm_t, *xbt_cfgelm_t;
67
68 static const char *xbt_cfgelm_type_name[xbt_cfgelm_type_count] = { "int", "double", "string", "boolean", "any", "outofbound" };
69
70 const struct xbt_boolean_couple xbt_cfgelm_boolean_values[] = {
71   { "yes",    "no"},
72   {  "on",   "off"},
73   {"true", "false"},
74   {   "1",     "0"},
75   {  NULL,    NULL}
76 };
77
78 /* Internal stuff used in cache to free a variable */
79 static void xbt_cfgelm_free(void *data)
80 {
81   if (data)
82     delete (xbt_cfgelm_t) data;
83 }
84
85 /* Retrieve the variable we'll modify */
86 static xbt_cfgelm_t xbt_cfgelm_get(xbt_cfg_t cfg, const char *name, e_xbt_cfgelm_type_t type);
87
88 /*----[ Memory management ]-----------------------------------------------*/
89 /** @brief Constructor
90  *
91  * Initialise a config set
92  */
93 xbt_cfg_t xbt_cfg_new(void)
94 {
95   return (xbt_cfg_t) xbt_dict_new_homogeneous(&xbt_cfgelm_free);
96 }
97
98 /** @brief Destructor */
99 void xbt_cfg_free(xbt_cfg_t * cfg)
100 {
101   XBT_DEBUG("Frees cfg set %p", cfg);
102   xbt_dict_free((xbt_dict_t *) cfg);
103 }
104
105 /** @brief Dump a config set for debuging purpose
106  *
107  * @param name The name to give to this config set
108  * @param indent what to write at the beginning of each line (right number of spaces)
109  * @param cfg the config set
110  */
111 void xbt_cfg_dump(const char *name, const char *indent, xbt_cfg_t cfg)
112 {
113   xbt_dict_t dict = (xbt_dict_t) cfg;
114   xbt_dict_cursor_t cursor = NULL;
115   xbt_cfgelm_t variable = NULL;
116   char *key = NULL;
117   int i;
118   int size;
119   int ival;
120   char *sval;
121   double dval;
122
123   if (name)
124     printf("%s>> Dumping of the config set '%s':\n", indent, name);
125
126   xbt_dict_foreach(dict, cursor, key, variable) {
127     printf("%s  %s:", indent, key);
128
129     size = xbt_dynar_length(variable->content);
130     printf ("%s. Actual size=%d. postset=%p\n",
131             xbt_cfgelm_type_name[variable->type], size, variable->cb_set);
132
133     switch (variable->type) {
134     case xbt_cfgelm_int:
135       for (i = 0; i < size; i++) {
136         ival = xbt_dynar_get_as(variable->content, i, int);
137         printf("%s    %d\n", indent, ival);
138       }
139       break;
140     case xbt_cfgelm_double:
141       for (i = 0; i < size; i++) {
142         dval = xbt_dynar_get_as(variable->content, i, double);
143         printf("%s    %f\n", indent, dval);
144       }
145       break;
146     case xbt_cfgelm_string:
147       for (i = 0; i < size; i++) {
148         sval = xbt_dynar_get_as(variable->content, i, char *);
149         printf("%s    %s\n", indent, sval);
150       }
151       break;
152     case xbt_cfgelm_boolean:
153       for (i = 0; i < size; i++) {
154         ival = xbt_dynar_get_as(variable->content, i, int);
155         printf("%s    %d\n", indent, ival);
156       }
157       break;
158     case xbt_cfgelm_alias:
159       /* no content */
160       break;
161     default:
162       printf("%s    Invalid type!!\n", indent);
163       break;
164     }
165   }
166
167   if (name)
168     printf("%s<< End of the config set '%s'\n", indent, name);
169   fflush(stdout);
170
171   xbt_dict_cursor_free(&cursor);
172 }
173
174 /*----[ Registering stuff ]-----------------------------------------------*/
175 /** @brief Register an element within a config set
176  *
177  *  @param cfg the config set
178  *  @param name the name of the config element
179  *  @param desc a description for this item (used by xbt_cfg_help())
180  *  @param type the type of the config element
181  *  @param cb_set callback function called when a value is set
182  */
183 static void xbt_cfg_register(
184   xbt_cfg_t * cfg, const char *name, const char *desc, e_xbt_cfgelm_type_t type,
185   xbt_cfg_cb_t cb_set,
186   std::function<void(const char* value)> callback = std::function<void(const char* value)>())
187 {
188   if (*cfg == NULL)
189     *cfg = xbt_cfg_new();
190   xbt_assert(type >= xbt_cfgelm_int && type <= xbt_cfgelm_boolean,
191               "type of %s not valid (%d should be between %d and %d)",
192              name, (int)type, xbt_cfgelm_int, xbt_cfgelm_boolean);
193
194   xbt_cfgelm_t res = (xbt_cfgelm_t) xbt_dict_get_or_null((xbt_dict_t) * cfg, name);
195   xbt_assert(NULL == res, "Refusing to register the config element '%s' twice.", name);
196
197   res = new s_xbt_cfgelm_t();
198   XBT_DEBUG("Register cfg elm %s (%s) (%s (=%d) @%p in set %p)",
199             name, desc, xbt_cfgelm_type_name[type], (int)type, res, *cfg);
200   res->type = type;
201   if (desc)
202     res->desc = desc;
203   res->cb_set = cb_set;
204   res->callback = std::move(callback);
205
206   switch (type) {
207   case xbt_cfgelm_int:
208     res->content = xbt_dynar_new(sizeof(int), NULL);
209     break;
210   case xbt_cfgelm_double:
211     res->content = xbt_dynar_new(sizeof(double), NULL);
212     break;
213   case xbt_cfgelm_string:
214     res->content = xbt_dynar_new(sizeof(char *), xbt_free_ref);
215     break;
216   case xbt_cfgelm_boolean:
217     res->content = xbt_dynar_new(sizeof(int), NULL);
218     break;
219   default:
220     XBT_ERROR("%d is an invalid type code", (int)type);
221     break;
222   }
223
224   xbt_dict_set((xbt_dict_t) * cfg, name, res, NULL);
225 }
226
227 void xbt_cfg_register_double(const char *name, double default_value,xbt_cfg_cb_t cb_set, const char *desc){
228   xbt_cfg_register(&simgrid_config,name,desc,xbt_cfgelm_double,cb_set);
229   xbt_cfg_setdefault_double(name, default_value);
230 }
231 void xbt_cfg_register_int(const char *name, int default_value,xbt_cfg_cb_t cb_set, const char *desc) {
232   xbt_cfg_register(&simgrid_config,name,desc,xbt_cfgelm_int,cb_set);
233   xbt_cfg_setdefault_int(name, default_value);
234 }
235 void xbt_cfg_register_string(const char *name, const char *default_value, xbt_cfg_cb_t cb_set, const char *desc){
236   xbt_cfg_register(&simgrid_config,name,desc,xbt_cfgelm_string,cb_set);
237   xbt_cfg_setdefault_string(name, default_value);
238 }
239 void xbt_cfg_register_boolean(const char *name, const char*default_value,xbt_cfg_cb_t cb_set, const char *desc){
240   xbt_cfg_register(&simgrid_config,name,desc,xbt_cfgelm_boolean,cb_set);
241   xbt_cfg_setdefault_boolean(name, default_value);
242 }
243
244 void xbt_cfg_register_alias(const char *newname, const char *oldname)
245 {
246   if (simgrid_config == NULL)
247     simgrid_config = xbt_cfg_new();
248
249   xbt_cfgelm_t res = (xbt_cfgelm_t) xbt_dict_get_or_null((xbt_dict_t) simgrid_config, oldname);
250   xbt_assert(NULL == res, "Refusing to register the option '%s' twice.", oldname);
251
252   res = (xbt_cfgelm_t) xbt_dict_get_or_null((xbt_dict_t) simgrid_config, newname);
253   xbt_assert(res, "Cannot define an alias to the non-existing option '%s'.", newname);
254
255   res = new s_xbt_cfgelm_t();
256   XBT_DEBUG("Register cfg alias %s -> %s)",oldname,newname);
257
258   res->desc = bprintf("Deprecated alias for %s",newname);
259   res->type = xbt_cfgelm_alias;
260   res->content = (xbt_dynar_t)newname;
261
262   xbt_dict_set((xbt_dict_t) simgrid_config, oldname, res, NULL);
263 }
264
265 /**
266  * @brief Parse a string and register the stuff described.
267  *
268  * @param cfg the config set
269  * @param entry a string describing the element to register
270  *
271  * The string may consist in several variable descriptions separated by a space.
272  * Each of them must use the following syntax: \<name\>:\<type\>
273  * with type being one of  'string','int','bool' or 'double'.
274  *
275  * Note that this does not allow to set the description, so you should prefer the other interface
276  */
277 void xbt_cfg_register_str(xbt_cfg_t * cfg, const char *entry)
278 {
279   char *entrycpy = xbt_strdup(entry);
280   char *tok;
281
282   e_xbt_cfgelm_type_t type;
283   XBT_DEBUG("Register string '%s'", entry);
284
285   tok = strchr(entrycpy, ':');
286   xbt_assert(tok, "Invalid config element descriptor: %s; Should be <name>:<type>", entry);
287   *(tok++) = '\0';
288
289   for (type = (e_xbt_cfgelm_type_t)0; type < xbt_cfgelm_type_count && strcmp(tok, xbt_cfgelm_type_name[type]); increment(type));
290   xbt_assert(type < xbt_cfgelm_type_count,
291       "Invalid type in config element descriptor: %s; Should be one of 'string', 'int' or 'double'.", entry);
292
293   xbt_cfg_register(cfg, entrycpy, NULL, type, NULL);
294
295   free(entrycpy);               /* strdup'ed by dict mechanism, but cannot be const */
296 }
297
298 /** @brief Displays the declared aliases and their description */
299 void xbt_cfg_aliases(void)
300 {
301   xbt_dict_cursor_t dict_cursor;
302   unsigned int dynar_cursor;
303   xbt_cfgelm_t variable;
304   char *name;
305   xbt_dynar_t names = xbt_dynar_new(sizeof(char *), NULL);
306
307   xbt_dict_foreach((xbt_dict_t )simgrid_config, dict_cursor, name, variable)
308     xbt_dynar_push(names, &name);
309   xbt_dynar_sort_strings(names);
310
311   xbt_dynar_foreach(names, dynar_cursor, name) {
312     variable = (xbt_cfgelm_t) xbt_dict_get((xbt_dict_t )simgrid_config, name);
313
314     if (variable->type == xbt_cfgelm_alias)
315       printf("   %s: %s\n", name, variable->desc.c_str());
316   }
317 }
318
319 /** @brief Displays the declared options and their description */
320 void xbt_cfg_help(void)
321 {
322   xbt_dict_cursor_t dict_cursor;
323   unsigned int dynar_cursor;
324   xbt_cfgelm_t variable;
325   char *name;
326   xbt_dynar_t names = xbt_dynar_new(sizeof(char *), NULL);
327
328   xbt_dict_foreach((xbt_dict_t )simgrid_config, dict_cursor, name, variable)
329     xbt_dynar_push(names, &name);
330   xbt_dynar_sort_strings(names);
331
332   xbt_dynar_foreach(names, dynar_cursor, name) {
333     int size;
334     variable = (xbt_cfgelm_t) xbt_dict_get((xbt_dict_t )simgrid_config, name);
335     if (variable->type == xbt_cfgelm_alias)
336       continue;
337
338     printf("   %s: %s\n", name, variable->desc.c_str());
339     printf("       Type: %s; ", xbt_cfgelm_type_name[variable->type]);
340     size = xbt_dynar_length(variable->content);
341     printf("Current value: ");
342
343     if (size != 1)
344       printf(size == 0 ? "n/a\n" : "{ ");
345     for (int i = 0; i < size; i++) {
346       const char *sep = (size == 1 ? "\n" : (i < size - 1 ? ", " : " }\n"));
347
348       switch (variable->type) {
349       case xbt_cfgelm_int:
350         printf("%d%s", xbt_dynar_get_as(variable->content, i, int), sep);
351         break;
352       case xbt_cfgelm_double:
353         printf("%f%s", xbt_dynar_get_as(variable->content, i, double), sep);
354         break;
355       case xbt_cfgelm_string:
356         printf("'%s'%s", xbt_dynar_get_as(variable->content, i, char *), sep);
357         break;
358       case xbt_cfgelm_boolean: {
359         int b = xbt_dynar_get_as(variable->content, i, int);
360         const char *bs = b ? xbt_cfgelm_boolean_values[0].true_val: xbt_cfgelm_boolean_values[0].false_val;
361         if (b == 0 || b == 1)
362           printf("'%s'%s", bs, sep);
363         else
364           printf("'%s/%d'%s", bs, b, sep);
365         break;
366       }
367       default:
368         printf("Invalid type!!%s", sep);
369         break;
370       }
371     }
372   }
373   xbt_dynar_free(&names);
374 }
375
376 static xbt_cfgelm_t xbt_cfgelm_get(xbt_cfg_t cfg, const char *name, e_xbt_cfgelm_type_t type)
377 {
378   xbt_cfgelm_t res = (xbt_cfgelm_t) xbt_dict_get_or_null((xbt_dict_t) cfg, name);
379
380   // The user used the old name. Switch to the new one after a short warning
381   while (res && res->type == xbt_cfgelm_alias) {
382     const char* newname = (const char *)res->content;
383     XBT_INFO("Option %s has been renamed to %s. Consider switching.", name, newname);
384     res = xbt_cfgelm_get(cfg, newname, type);
385   }
386
387   if (!res) {
388     xbt_cfg_help();
389     fflush(stdout);
390     THROWF(not_found_error, 0, "No registered variable '%s' in this config set.", name);
391   }
392
393   xbt_assert(type == xbt_cfgelm_any || res->type == type,
394               "You tried to access to the config element %s as an %s, but its type is %s.",
395               name, xbt_cfgelm_type_name[type], xbt_cfgelm_type_name[res->type]);
396   return res;
397 }
398
399 /** @brief Get the type of this variable in that configuration set
400  *
401  * @param cfg the config set
402  * @param name the name of the element
403  *
404  * @return the type of the given element
405  */
406 e_xbt_cfgelm_type_t xbt_cfg_get_type(xbt_cfg_t cfg, const char *name)
407 {
408   xbt_cfgelm_t variable = NULL;
409
410   variable = (xbt_cfgelm_t) xbt_dict_get_or_null((xbt_dict_t) cfg, name);
411   if (!variable)
412     THROWF(not_found_error, 0, "Can't get the type of '%s' since this variable does not exist", name);
413
414   XBT_DEBUG("type in variable = %d", (int)variable->type);
415   return variable->type;
416 }
417
418 /*----[ Setting ]---------------------------------------------------------*/
419 /**  @brief va_args version of xbt_cfg_set
420  *
421  * @param cfg config set to fill
422  * @param name  variable name
423  * @param pa  variable value
424  *
425  * Add some values to the config set.
426  */
427 void xbt_cfg_set_vargs(xbt_cfg_t cfg, const char *name, va_list pa)
428 {
429   char *str;
430   int i;
431   double d;
432   e_xbt_cfgelm_type_t type = xbt_cfgelm_type_count; /* Set a dummy value to make gcc happy. It cannot get uninitialized */
433
434   xbt_ex_t e;
435
436   TRY {
437     type = xbt_cfg_get_type(cfg, name);
438   }
439   CATCH(e) {
440     if (e.category == not_found_error) {
441       xbt_ex_free(e);
442       THROWF(not_found_error, 0, "Can't set the property '%s' since it's not registered", name);
443     }
444     RETHROW;
445   }
446
447   switch (type) {
448   case xbt_cfgelm_string:
449     str = va_arg(pa, char *);
450     xbt_cfg_set_string(name, str);
451     break;
452   case xbt_cfgelm_int:
453     i = va_arg(pa, int);
454     xbt_cfg_set_int(name, i);
455     break;
456   case xbt_cfgelm_double:
457     d = va_arg(pa, double);
458     xbt_cfg_set_double(name, d);
459     break;
460   case xbt_cfgelm_boolean:
461     str = va_arg(pa, char *);
462     xbt_cfg_set_boolean(name, str);
463     break;
464   default:
465     xbt_die("Config element variable %s not valid (type=%d)", name, (int)type);
466   }
467 }
468
469 /** @brief Add a NULL-terminated list of pairs {(char*)key, value} to the set
470  *
471  * @param cfg config set to fill
472  * @param name variable name
473  * @param ... variable value
474  */
475 void xbt_cfg_set(xbt_cfg_t cfg, const char *name, ...)
476 {
477   va_list pa;
478
479   va_start(pa, name);
480   xbt_cfg_set_vargs(cfg, name, pa);
481   va_end(pa);
482 }
483
484 /** @brief Add values parsed from a string into a config set
485  *
486  * @param options a string containing the content to add to the config set. This is a '\\t',' ' or '\\n' or ','
487  * separated list of variables. Each individual variable is like "[name]:[value]" where [name] is the name of an
488  * already registered variable, and [value] conforms to the data type under which this variable was registered.
489  *
490  * @todo This is a crude manual parser, it should be a proper lexer.
491  */
492 void xbt_cfg_set_parse(const char *options)
493 {
494   if (!options || !strlen(options)) {   /* nothing to do */
495     return;
496   }
497   char *optionlist_cpy = xbt_strdup(options);
498
499   XBT_DEBUG("List to parse and set:'%s'", options);
500   char *option = optionlist_cpy;
501   while (1) {                   /* breaks in the code */
502     if (!option)
503       break;
504     char *name = option;
505     int len = strlen(name);
506     XBT_DEBUG("Still to parse and set: '%s'. len=%d; option-name=%ld", name, len, (long) (option - name));
507
508     /* Pass the value */
509     while (option - name <= (len - 1) && *option != ' ' && *option != '\n' && *option != '\t' && *option != ',') {
510       XBT_DEBUG("Take %c.", *option);
511       option++;
512     }
513     if (option - name == len) {
514       XBT_DEBUG("Boundary=EOL");
515       option = NULL;            /* don't do next iteration */
516     } else {
517       XBT_DEBUG("Boundary on '%c'. len=%d;option-name=%ld", *option, len, (long) (option - name));
518       /* Pass the following blank chars */
519       *(option++) = '\0';
520       while (option - name < (len - 1) && (*option == ' ' || *option == '\n' || *option == '\t')) {
521         /*      fprintf(stderr,"Ignore a blank char.\n"); */
522         option++;
523       }
524       if (option - name == len - 1)
525         option = NULL;          /* don't do next iteration */
526     }
527     XBT_DEBUG("parse now:'%s'; parse later:'%s'", name, option);
528
529     if (name[0] == ' ' || name[0] == '\n' || name[0] == '\t')
530       continue;
531     if (!strlen(name))
532       break;
533
534     char *val = strchr(name, ':');
535     xbt_assert(val, "Option '%s' badly formatted. Should be of the form 'name:value'", name);
536     /* don't free(optionlist_cpy) if the assert fails, 'name' points inside it */
537     *(val++) = '\0';
538
539     if (strncmp(name, "contexts/", strlen("contexts/")) && strncmp(name, "path", strlen("path")))
540       XBT_INFO("Configuration change: Set '%s' to '%s'", name, val);
541
542     TRY {
543       xbt_cfg_set_as_string(name,val);
544     } CATCH_ANONYMOUS {
545       free(optionlist_cpy);
546       RETHROW;
547     }
548   }
549   free(optionlist_cpy);
550 }
551
552 /** @brief Set the value of a variable, using the string representation of that value
553  *
554  * @param key name of the variable to modify
555  * @param value string representation of the value to set
556  *
557  * @return the first char after the parsed value in val
558  */
559
560 void *xbt_cfg_set_as_string(const char *key, const char *value) {
561   xbt_ex_t e;
562
563   char *ret;
564   volatile xbt_cfgelm_t variable = NULL;
565   int i;
566   double d;
567
568   TRY {
569     while (variable == NULL) {
570       variable = (xbt_cfgelm_t) xbt_dict_get((xbt_dict_t) simgrid_config, key);
571       if (variable->type == xbt_cfgelm_alias) {
572         const char *newname = (const char*)variable->content;
573         XBT_INFO("Note: configuration '%s' is deprecated. Please use '%s' instead.", key, newname);
574         key = newname;
575         variable = NULL;
576       }
577     }
578   } CATCH(e) {
579     if (e.category == not_found_error) {
580       xbt_ex_free(e);
581       THROWF(not_found_error, 0, "No registered variable corresponding to '%s'.", key);
582     }
583     RETHROW;
584   }
585
586   switch (variable->type) {
587   case xbt_cfgelm_string:
588     xbt_cfg_set_string(key, value);     /* throws */
589     break;
590   case xbt_cfgelm_int:
591     i = strtol(value, &ret, 0);
592     if (ret == value) {
593       xbt_die("Value of option %s not valid. Should be an integer", key);
594     }
595     xbt_cfg_set_int(key, i);  /* throws */
596     break;
597   case xbt_cfgelm_double:
598     d = strtod(value, &ret);
599     if (ret == value) {
600       xbt_die("Value of option %s not valid. Should be a double", key);
601     }
602     xbt_cfg_set_double(key, d);       /* throws */
603     break;
604   case xbt_cfgelm_boolean:
605     xbt_cfg_set_boolean(key, value);  /* throws */
606     ret = (char *)value + strlen(value);
607     break;
608   default:
609     THROWF(unknown_error, 0, "Type of config element %s is not valid.", key);
610     break;
611   }
612   return ret;
613 }
614
615 /** @brief Set an integer value to \a name within \a cfg if it wasn't changed yet
616  *
617  * This is useful to change the default value of a variable while allowing
618  * users to override it with command line arguments
619  */
620 void xbt_cfg_setdefault_int(const char *name, int val)
621 {
622   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_int);
623
624   if (variable->isdefault){
625     xbt_cfg_set_int(name, val);
626     variable->isdefault = true;
627   } else
628     XBT_DEBUG("Do not override configuration variable '%s' with value '%d' because it was already set.", name, val);
629 }
630
631 /** @brief Set an integer value to \a name within \a cfg if it wasn't changed yet
632  *
633  * This is useful to change the default value of a variable while allowing
634  * users to override it with command line arguments
635  */
636 void xbt_cfg_setdefault_double(const char *name, double val)
637 {
638   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_double);
639
640   if (variable->isdefault) {
641     xbt_cfg_set_double(name, val);
642     variable->isdefault = true;
643   } else
644     XBT_DEBUG("Do not override configuration variable '%s' with value '%f' because it was already set.", name, val);
645 }
646
647 /** @brief Set a string value to \a name within \a cfg if it wasn't changed yet
648  *
649  * This is useful to change the default value of a variable while allowing
650  * users to override it with command line arguments
651  */
652 void xbt_cfg_setdefault_string(const char *name, const char *val)
653 {
654   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_string);
655
656   if (variable->isdefault){
657     xbt_cfg_set_string(name, val);
658     variable->isdefault = true;
659   } else
660     XBT_DEBUG("Do not override configuration variable '%s' with value '%s' because it was already set.", name, val);
661 }
662
663 /** @brief Set an boolean value to \a name within \a cfg if it wasn't changed yet
664  *
665  * This is useful to change the default value of a variable while allowing
666  * users to override it with command line arguments
667  */
668 void xbt_cfg_setdefault_boolean(const char *name, const char *val)
669 {
670   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_boolean);
671
672   if (variable->isdefault){
673     xbt_cfg_set_boolean(name, val);
674     variable->isdefault = true;
675   }
676    else
677     XBT_DEBUG("Do not override configuration variable '%s' with value '%s' because it was already set.", name, val);
678 }
679
680 /** @brief Set an integer value to \a name within \a cfg
681  *
682  * @param name the name of the variable
683  * @param val the value of the variable
684  */
685 void xbt_cfg_set_int(const char *name, int val)
686 {
687   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_int);
688
689   xbt_dynar_set(variable->content, 0, &val);
690
691   if (variable->cb_set)
692     variable->cb_set(name);
693   variable->isdefault = false;
694 }
695
696 /** @brief Set or add a double value to \a name within \a cfg
697  *
698  * @param name the name of the variable
699  * @param val the double to set
700  */
701 void xbt_cfg_set_double(const char *name, double val)
702 {
703   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_double);
704
705   xbt_dynar_set(variable->content, 0, &val);
706
707   if (variable->cb_set)
708     variable->cb_set(name);
709   variable->isdefault = false;
710 }
711
712 /** @brief Set or add a string value to \a name within \a cfg
713  *
714  * @param cfg the config set
715  * @param name the name of the variable
716  * @param val the value to be added
717  *
718  */
719 void xbt_cfg_set_string(const char *name, const char *val)
720 {
721   char *newval = xbt_strdup(val);
722   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_string);
723
724   if (!xbt_dynar_is_empty(variable->content)) {
725     char *sval = xbt_dynar_get_as(variable->content, 0, char *);
726     free(sval);
727   }
728
729   xbt_dynar_set(variable->content, 0, &newval);
730
731   if (variable->cb_set)
732     variable->cb_set(name);
733
734   if (variable->callback) {
735     try {
736       variable->callback(val);
737     }
738     catch(std::range_error& e) {
739       xbt_die("Invalid flag %s=%s: %s", val, name, e.what());
740     }
741     catch(std::exception& e) {
742       xbt_die("Error for flag %s=%s: %s", val, name, e.what());
743     }
744     catch(...) {
745       xbt_die("Error for flag %s=%s", val, name);
746     }
747   }
748
749   variable->isdefault = false;
750 }
751
752 /** @brief Set or add a boolean value to \a name within \a cfg
753  *
754  * @param name the name of the variable
755  * @param val the value of the variable
756  */
757 void xbt_cfg_set_boolean(const char *name, const char *val)
758 {
759   int bval=-1;
760   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_boolean);
761
762   for (int i = 0; xbt_cfgelm_boolean_values[i].true_val != NULL; i++) {
763     if (strcmp(val, xbt_cfgelm_boolean_values[i].true_val) == 0){
764       bval = 1;
765       break;
766     }
767     if (strcmp(val, xbt_cfgelm_boolean_values[i].false_val) == 0){
768       bval = 0;
769       break;
770     }
771   }
772   xbt_assert(bval != -1, "Value of option '%s' not valid. Should be a boolean (yes,no,on,off,true,false,0,1)", val);
773   xbt_dynar_set(variable->content, 0, &bval);
774
775   if (variable->cb_set)
776     variable->cb_set(name);
777   variable->isdefault = false;
778 }
779
780
781 /* Say if the value is the default value */
782 int xbt_cfg_is_default_value(const char *name)
783 {
784   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_any);
785   return variable->isdefault;
786 }
787
788 /*----[ Getting ]---------------------------------------------------------*/
789 /** @brief Retrieve an integer value of a variable (get a warning if not uniq)
790  *
791  * @param name the name of the variable
792  *
793  * Returns the first value from the config set under the given name.
794  * If there is more than one value, it will issue a warning. Consider using xbt_cfg_get_dynar() instead.
795  */
796 int xbt_cfg_get_int(const char *name)
797 {
798   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_int);
799
800   if (xbt_dynar_length(variable->content) > 1) {
801     XBT_WARN("You asked for the first value of the config element '%s', but there is %lu values",
802          name, xbt_dynar_length(variable->content));
803   }
804
805   return xbt_dynar_get_as(variable->content, 0, int);
806 }
807
808 /** @brief Retrieve a double value of a variable (get a warning if not uniq)
809  *
810  * @param cfg the config set
811  * @param name the name of the variable
812  *
813  * Returns the first value from the config set under the given name.
814  * If there is more than one value, it will issue a warning. Consider using xbt_cfg_get_dynar() instead.
815  */
816 double xbt_cfg_get_double(const char *name)
817 {
818   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_double);
819
820   if (xbt_dynar_length(variable->content) > 1) {
821     XBT_WARN ("You asked for the first value of the config element '%s', but there is %lu values\n",
822          name, xbt_dynar_length(variable->content));
823   }
824
825   return xbt_dynar_get_as(variable->content, 0, double);
826 }
827
828 /** @brief Retrieve a string value of a variable (get a warning if not uniq)
829  *
830  * @param cfg the config set
831  * @param name the name of the variable
832  *
833  * Returns the first value from the config set under the given name.
834  * If there is more than one value, it will issue a warning. Consider using
835  * xbt_cfg_get_dynar() instead. Returns NULL if there is no value.
836  *
837  * \warning the returned value is the actual content of the config set
838  */
839 char *xbt_cfg_get_string(const char *name)
840 {
841   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_string);
842
843   if (xbt_dynar_length(variable->content) > 1) {
844     XBT_WARN("You asked for the first value of the config element '%s', but there is %lu values\n",
845          name, xbt_dynar_length(variable->content));
846   } else if (xbt_dynar_is_empty(variable->content)) {
847     return NULL;
848   }
849
850   return xbt_dynar_get_as(variable->content, 0, char *);
851 }
852
853 /** @brief Retrieve a boolean value of a variable (get a warning if not uniq)
854  *
855  * @param cfg the config set
856  * @param name the name of the variable
857  *
858  * Returns the first value from the config set under the given name.
859  * If there is more than one value, it will issue a warning. Consider using xbt_cfg_get_dynar() instead.
860  */
861 int xbt_cfg_get_boolean(const char *name)
862 {
863   xbt_cfgelm_t variable = xbt_cfgelm_get(simgrid_config, name, xbt_cfgelm_boolean);
864
865   if (xbt_dynar_length(variable->content) > 1) {
866     XBT_WARN("You asked for the first value of the config element '%s', but there is %lu values",
867          name, xbt_dynar_length(variable->content));
868   }
869
870   return xbt_dynar_get_as(variable->content, 0, int);
871 }
872
873 namespace simgrid {
874 namespace config {
875
876 bool parseBool(const char* value)
877 {
878   for (int i = 0; xbt_cfgelm_boolean_values[i].true_val != NULL; i++) {
879     if (std::strcmp(value, xbt_cfgelm_boolean_values[i].true_val) == 0)
880       return true;
881     if (std::strcmp(value, xbt_cfgelm_boolean_values[i].false_val) == 0)
882       return false;
883   }
884   throw std::range_error("not a boolean");
885 }
886
887 double parseDouble(const char* value)
888 {
889   char* end;
890   errno = 0;
891   double res = std::strtod(value, &end);
892   if (errno == ERANGE)
893     throw std::range_error("out of range");
894   else if (errno)
895     xbt_die("Unexpected errno");
896   if (end == value || *end != '\0')
897     throw std::range_error("invalid double");
898   else
899     return res;
900 }
901
902 long int parseLong(const char* value)
903 {
904   char* end;
905   errno = 0;
906   long int res = std::strtol(value, &end, 0);
907   if (errno) {
908     if (res == LONG_MIN && errno == ERANGE)
909       throw std::range_error("underflow");
910     else if (res == LONG_MAX && errno == ERANGE)
911       throw std::range_error("overflow");
912     xbt_die("Unexpected errno");
913   }
914   if (end == value || *end != '\0')
915     throw std::range_error("invalid integer");
916   else
917     return res;
918 }
919
920 void declareFlag(const char* name, const char* description,
921   std::function<void(const char* value)> callback)
922 {
923   xbt_cfg_register(&simgrid_config, name, description, xbt_cfgelm_string, NULL,
924     std::move(callback));
925 }
926
927 }
928 }
929
930 #ifdef SIMGRID_TEST
931
932 #include <string>
933
934 #include "xbt.h"
935 #include "xbt/ex.h"
936
937 #include <xbt/config.hpp>
938
939 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(xbt_cfg);
940
941 XBT_TEST_SUITE("config", "Configuration support");
942
943 static xbt_cfg_t make_set()
944 {
945   xbt_cfg_t set = NULL;
946
947   xbt_log_threshold_set(&_XBT_LOGV(xbt_cfg), xbt_log_priority_critical);
948   xbt_cfg_register_str(&set, "speed:int");
949   xbt_cfg_register_str(&set, "peername:string");
950   xbt_cfg_register_str(&set, "user:string");
951
952   return set;
953 }                               /* end_of_make_set */
954
955 XBT_PUBLIC_DATA(xbt_cfg_t) simgrid_config;
956
957 XBT_TEST_UNIT("memuse", test_config_memuse, "Alloc and free a config set")
958 {
959   simgrid_config = make_set();
960   xbt_test_add("Alloc and free a config set");
961   xbt_cfg_set_parse("peername:veloce user:bidule");
962   xbt_cfg_free(&simgrid_config);
963 }
964
965 XBT_TEST_UNIT("use", test_config_use, "Data retrieving tests")
966 {
967   simgrid_config = make_set();
968   xbt_test_add("Get a single value");
969   {
970     /* get_single_value */
971     int ival;
972
973     xbt_cfg_set_parse("peername:toto:42 speed:42");
974     ival = xbt_cfg_get_int("speed");
975     if (ival != 42)
976       xbt_test_fail("Speed value = %d, I expected 42", ival);
977   }
978
979   xbt_test_add("Access to a non-existant entry");
980   {
981     xbt_ex_t e;
982
983     TRY {
984       xbt_cfg_set_parse("color:blue");
985     } CATCH(e) {
986       if (e.category != not_found_error)
987         xbt_test_exception(e);
988       xbt_ex_free(e);
989     }
990   }
991   xbt_cfg_free(&simgrid_config);
992 }
993
994 XBT_TEST_UNIT("c++flags", test_config_cxx_flags, "C++ flags")
995 {
996   simgrid_config = make_set();
997   xbt_test_add("C++ declaration of flags");
998
999   simgrid::config::Flag<int> int_flag("int", "", 0);
1000   simgrid::config::Flag<std::string> string_flag("string", "", "foo");
1001   simgrid::config::Flag<double> double_flag("double", "", 0.32);
1002   simgrid::config::Flag<bool> bool_flag1("bool1", "", false);
1003   simgrid::config::Flag<bool> bool_flag2("bool2", "", true);
1004
1005   xbt_test_add("Parse values");
1006   xbt_cfg_set_parse("int:42 string:bar double:8.0 bool1:true bool2:false");
1007   xbt_test_assert(int_flag == 42, "Check int flag");
1008   xbt_test_assert(string_flag == "bar", "Check string flag");
1009   xbt_test_assert(double_flag == 8.0, "Check double flag");
1010   xbt_test_assert(bool_flag1, "Check bool1 flag");
1011   xbt_test_assert(!bool_flag2, "Check bool2 flag");
1012
1013   xbt_cfg_free(&simgrid_config);
1014 }
1015
1016 #endif                          /* SIMGRID_TEST */