Logo AND Algorithmique Numérique Distribuée

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