Logo AND Algorithmique Numérique Distribuée

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