3 /* config - Dictionnary where the type of each cell is provided. */
5 /* This is useful to build named structs, like option or property sets. */
7 /* Authors: Martin Quinson */
8 /* Copyright (C) 2001,2002,2003,2004 the OURAGAN project. */
10 /* This program is free software; you can redistribute it and/or modify it
11 under the terms of the license (GNU LGPL) which comes with this package. */
13 #include "gras_private.h" /* prototypes of this module */
15 GRAS_LOG_NEW_DEFAULT_SUBCATEGORY(config,gros,"configuration support");
17 /* gras_cfgelm_t: the typedef corresponding to a config cell.
19 Both data and DTD are mixed, but fixing it now would prevent me to ever
23 /* Allowed type of the cell */
24 gras_cfgelm_type_t type;
28 (cannot be an union because type host uses both str and i) */
29 gras_dynar_t *content;
32 static const char *gras_cfgelm_type_name[gras_cfgelm_type_count]=
33 {"int","double","string","host"};
35 /* Internal stuff used in cache to free a cell */
36 static void gras_cfgelm_free(void *data);
38 /* Retrieve the cell we'll modify */
39 static gras_error_t gras_cfgelm_get(gras_cfg_t *cfg, const char *name,
40 gras_cfgelm_type_t type,
41 /* OUT */ gras_cfgelm_t **whereto);
43 void gras_cfg_str_free(void *d);
44 void gras_cfg_host_free(void *d);
46 void gras_cfg_str_free(void *d){
47 gras_free(*(void**)d);
49 void gras_cfg_host_free(void *d){
50 gras_host_t *h=(gras_host_t*) *(void**)d;
52 if (h->name) gras_free(h->name);
57 /*----[ Memory management ]-----------------------------------------------*/
64 * Initialise an config set
68 gras_error_t gras_cfg_new(gras_cfg_t **whereto) {
69 return gras_dict_new((gras_dict_t**)whereto);
75 * @whereto: the config set to be created
76 * @tocopy: the source data
81 gras_cfg_cpy(gras_cfg_t **whereto, gras_cfg_t *tocopy) {
82 gras_dict_cursor_t *cursor=NULL;
83 gras_cfgelm_t *cell=NULL;
88 gras_assert0(tocopy,"cannot copy NULL config");
90 gras_dict_foreach((gras_dict_t*)tocopy,cursor,name,cell) {
91 if ((errcode=gras_cfg_register(*whereto, name, cell->type,
92 cell->min, cell->max)) != no_error) {
93 if (cursor) gras_dict_cursor_free(cursor);
94 if (*whereto) gras_cfg_free(whereto);
102 void gras_cfg_free(gras_cfg_t **cfg) {
103 gras_dict_free((gras_dict_t**)cfg);
108 * @name: The name to give to this config set
109 * @indent: what to write at the begining of each line (right number of spaces)
110 * @cfg: the config set
112 * Dumps a config set for debuging purpose
115 gras_cfg_dump(const char *name,const char *indent,gras_cfg_t *cfg) {
116 gras_dict_t *dict = (gras_dict_t*) cfg;
117 gras_dict_cursor_t *cursor=NULL;
118 gras_cfgelm_t *cell=NULL;
121 gras_error_t errcode;
129 printf("%s>> Dumping of the config set '%s':\n",indent,name);
130 gras_dict_foreach(dict,cursor,key,cell) {
132 printf("%s %s:",indent,key);
134 size = gras_dynar_length(cell->content);
135 printf("%d_to_%d_%s. Actual size=%d. List of values:\n",
136 cell->min,cell->max,gras_cfgelm_type_name[cell->type],
139 switch (cell->type) {
141 case gras_cfgelm_int:
142 for (i=0; i<size; i++) {
143 gras_dynar_get(cell->content,i,&ival);
144 printf ("%s %d\n",indent,ival);
148 case gras_cfgelm_double:
149 for (i=0; i<size; i++) {
150 gras_dynar_get(cell->content,i,&dval);
151 printf ("%s %f\n",indent,dval);
155 case gras_cfgelm_string:
156 for (i=0; i<size; i++) {
157 gras_dynar_get(cell->content,i,&sval);
158 printf ("%s %s\n",indent,sval);
162 case gras_cfgelm_host:
163 for (i=0; i<size; i++) {
164 gras_dynar_get(cell->content,i,&hval);
165 printf ("%s %s:%d\n",indent,hval.name,hval.port);
170 printf("%s Invalid type!!\n",indent);
175 if (name) printf("%s<< End of the config set '%s'\n",indent,name);
178 gras_dict_cursor_free(cursor);
185 * @data: the data to be freed (typed as void* to be usable as free funct in dict)
187 * free an config element
190 void gras_cfgelm_free(void *data) {
191 gras_cfgelm_t *c=(gras_cfgelm_t *)data;
194 gras_dynar_free(c->content);
198 /*----[ Registering stuff ]-----------------------------------------------*/
202 * @cfg: the config set
203 * @type: the type of the config element
207 * register an element within a config set
211 gras_cfg_register(gras_cfg_t *cfg,
212 const char *name, gras_cfgelm_type_t type,
215 gras_error_t errcode;
217 DEBUG4("Register cfg elm %s (%d to %d %s)",name,min,max,gras_cfgelm_type_name[type]);
218 TRYCATCH(mismatch_error,gras_dict_get((gras_dict_t*)cfg,name,(void**)&res));
220 if (errcode != mismatch_error) {
221 WARN1("Config elem %s registered twice.",name);
222 /* Will be removed by the insertion of the new one */
225 res=gras_new(gras_cfgelm_t,1);
234 case gras_cfgelm_int:
235 TRY(gras_dynar_new(&(res->content), sizeof(int), NULL));
238 case gras_cfgelm_double:
239 TRY(gras_dynar_new(&(res->content), sizeof(double), NULL));
242 case gras_cfgelm_string:
243 TRY(gras_dynar_new(&(res->content),sizeof(char*),&gras_cfg_str_free));
246 case gras_cfgelm_host:
247 TRY(gras_dynar_new(&(res->content),sizeof(gras_host_t*),&gras_cfg_host_free));
251 ERROR1("%d is an invalide type code",type);
254 return gras_dict_set((gras_dict_t*)cfg,name,res,&gras_cfgelm_free);
258 * gras_cfg_unregister:
260 * @cfg: the config set
261 * @name: the name of the elem to be freed
263 * unregister an element from a config set.
264 * Note that it removes both the DTD and the actual content.
268 gras_cfg_unregister(gras_cfg_t *cfg,const char *name) {
269 return gras_dict_remove((gras_dict_t*)cfg,name);
273 * gras_cfg_register_str:
275 * @cfg: the config set
276 * @entry: a string describing the element to register
278 * Parse a string and register the stuff described.
282 gras_cfg_register_str(gras_cfg_t *cfg,const char *entry) {
283 char *entrycpy=strdup(entry);
287 gras_cfgelm_type_t type;
289 gras_error_t errcode;
291 tok=strchr(entrycpy, ':');
294 "Invalid config element descriptor: ",entry,
295 "; Should be <name>:<min nb>_to_<max nb>_<type>");
301 min=strtol(tok, &tok, 10);
303 ERROR1("Invalid minimum in config element descriptor %s",entry);
308 if (!strcmp(tok,"_to_")){
310 "Invalid config element descriptor: ",entry,
311 "; Should be <name>:<min nb>_to_<max nb>_<type>");
315 tok += strlen("_to_");
317 max=strtol(tok, &tok, 10);
319 ERROR1("Invalid maximum in config element descriptor %s",entry);
326 "Invalid config element descriptor: ",entry,
327 "; Should be <name>:<min nb>_to_<max nb>_<type>");
333 type<gras_cfgelm_type_count && strcmp(tok,gras_cfgelm_type_name[type]);
335 if (type == gras_cfgelm_type_count) {
337 "Invalid type in config element descriptor: ",entry,
338 "; Should be one of 'string', 'int', 'host' or 'double'.");
343 TRYCLEAN(gras_cfg_register(cfg,entrycpy,type,min,max),
344 gras_free(entrycpy));
346 gras_free(entrycpy); /* strdup'ed by dict mechanism, but cannot be const */
353 * @cfg: the config set
355 * Check the config set
359 gras_cfg_check(gras_cfg_t *cfg) {
360 gras_dict_cursor_t *cursor;
365 gras_assert0(cfg,"NULL config set.");
367 gras_dict_foreach((gras_dict_t*)cfg,cursor,name,cell) {
368 size = gras_dynar_length(cell->content);
369 if (cell->min > size) {
370 ERROR4("Config elem %s needs at least %d %s, but there is only %d values.",
373 gras_cfgelm_type_name[cell->type],
375 gras_dict_cursor_free(cursor);
376 return mismatch_error;
379 if (cell->max < size) {
380 ERROR4("Config elem %s accepts at most %d %s, but there is %d values.",
383 gras_cfgelm_type_name[cell->type],
385 gras_dict_cursor_free(cursor);
386 return mismatch_error;
391 gras_dict_cursor_free(cursor);
395 static gras_error_t gras_cfgelm_get(gras_cfg_t *cfg,
397 gras_cfgelm_type_t type,
398 /* OUT */ gras_cfgelm_t **whereto){
400 gras_error_t errcode = gras_dict_get((gras_dict_t*)cfg,name,
403 if (errcode == mismatch_error) {
404 ERROR1("No registered cell %s in this config set",
406 return mismatch_error;
408 if (errcode != no_error)
411 gras_assert3((*whereto)->type == type,
412 "You tried to access to the config element %s as an %s, but its type is %s.",
414 gras_cfgelm_type_name[type],
415 gras_cfgelm_type_name[(*whereto)->type]);
423 * @cfg: the config set
424 * @name: the name of the element
427 * Give the type of the config element
431 gras_cfg_get_type(gras_cfg_t *cfg, const char *name,
432 /* OUT */gras_cfgelm_type_t *type) {
435 gras_error_t errcode;
437 TRYCATCH(mismatch_error,gras_dict_get((gras_dict_t*)cfg,name,(void**)&cell));
439 if (errcode == mismatch_error) {
440 ERROR1("Can't get the type of '%s' since this cell does not exist",
442 return mismatch_error;
450 /*----[ Setting ]---------------------------------------------------------*/
452 * gras_cfg_set_vargs():
453 * @cfg: config set to fill
454 * @varargs: NULL-terminated list of pairs {(const char*)key, value}
456 * Add some values to the config set.
457 * @warning: if the list isn't NULL terminated, it will segfault.
460 gras_cfg_set_vargs(gras_cfg_t *cfg, va_list pa) {
464 gras_cfgelm_type_t type;
466 gras_error_t errcode;
468 while ((name=va_arg(pa,char *))) {
470 if (!gras_cfg_get_type(cfg,name,&type)) {
471 ERROR1("Can't set the property '%s' since it's not registered",name);
472 return mismatch_error;
476 case gras_cfgelm_host:
477 str = va_arg(pa, char *);
479 TRY(gras_cfg_set_host(cfg,name,str,i));
482 case gras_cfgelm_string:
483 str=va_arg(pa, char *);
484 TRY(gras_cfg_set_string(cfg, name, str));
487 case gras_cfgelm_int:
489 TRY(gras_cfg_set_int(cfg,name,i));
492 case gras_cfgelm_double:
494 TRY(gras_cfg_set_double(cfg,name,d));
498 RAISE1(unknown_error,"Config element cell %s not valid.",name);
506 * @cfg: config set to fill
507 * @varargs: NULL-terminated list of pairs {(const char*)key, value}
509 * Add some values to the config set.
510 * @warning: if the list isn't NULL terminated, it will segfault.
512 gras_error_t gras_cfg_set(gras_cfg_t *cfg, ...) {
514 gras_error_t errcode;
517 errcode=gras_cfg_set_vargs(cfg,pa);
523 * gras_cfg_set_parse():
524 * @cfg: config set to fill
525 * @options: a string containing the content to add to the config set. This
526 * is a '\t',' ' or '\n' separated list of cells. Each individual cell is
527 * like "[name]:[value]" where [name] is the name of an already registred
528 * cell, and [value] conforms to the data type under which this cell was
531 * Add the cells described in a string to a config set.
535 gras_cfg_set_parse(gras_cfg_t *cfg, const char *options) {
541 char *optionlist_cpy;
542 char *option, *name,*val;
545 gras_error_t errcode;
548 if (!options || !strlen(options)) { /* nothing to do */
551 optionlist_cpy=strdup(options);
553 DEBUG1("List to parse and set:'%s'",options);
554 option=optionlist_cpy;
555 while (1) { /* breaks in the code */
561 DEBUG3("Still to parse and set: '%s'. len=%d; option-name=%d",name,len,option-name);
564 while (option-name<=(len-1) && *option != ' ' && *option != '\n' && *option != '\t') {
565 //fprintf(stderr,"Take %c.\n",*option);
568 if (option-name == len) {
569 //fprintf(stderr,"Boundary=EOL\n");
570 option=NULL; /* don't do next iteration */
573 //fprintf(stderr,"Boundary on '%c'. len=%d;option-name=%d\n",*option,len,option-name);
575 /* Pass the following blank chars */
577 while (option-name<(len-1) && (*option == ' ' || *option == '\n' || *option == '\t')) {
578 // fprintf(stderr,"Ignore a blank char.\n");
581 if (option-name == len-1)
582 option=NULL; /* don't do next iteration */
584 DEBUG2("parse now:'%s'; parse later:'%s'",name,option);
586 if (name[0] == ' ' || name[0] == '\n' || name[0] == '\t')
591 val=strchr(name,':');
593 gras_free(optionlist_cpy);
595 "Malformated option: '%s'; Should be of the form 'name:value'",
600 DEBUG2("name='%s';val='%s'",name,val);
602 errcode=gras_dict_get((gras_dict_t*)cfg,name,(void**)&cell);
607 ERROR1("No registrated cell corresponding to '%s'.",name);
608 gras_free(optionlist_cpy);
609 return mismatch_error;
612 gras_free(optionlist_cpy);
616 switch (cell->type) {
617 case gras_cfgelm_string:
618 TRYCLEAN(gras_cfg_set_string(cfg, name, val),
619 gras_free(optionlist_cpy));
622 case gras_cfgelm_int:
623 i=strtol(val, &val, 0);
625 gras_free(optionlist_cpy);
627 "Value of option %s not valid. Should be an integer",
631 TRYCLEAN(gras_cfg_set_int(cfg,name,i),
632 gras_free(optionlist_cpy));
635 case gras_cfgelm_double:
638 gras_free(optionlist_cpy);
640 "Value of option %s not valid. Should be a double",
644 TRYCLEAN(gras_cfg_set_double(cfg,name,d),
645 gras_free(optionlist_cpy));
648 case gras_cfgelm_host:
652 gras_free(optionlist_cpy);
654 "Value of option %s not valid. Should be an host (machine:port)",
659 i=strtol(val, &val, 0);
661 gras_free(optionlist_cpy);
663 "Value of option %s not valid. Should be an host (machine:port)",
667 TRYCLEAN(gras_cfg_set_host(cfg,name,str,i),
668 gras_free(optionlist_cpy));
672 gras_free(optionlist_cpy);
673 RAISE1(unknown_error,"Type of config element %s is not valid.",name);
677 gras_free(optionlist_cpy);
684 * @cfg: the config set
685 * @name: the name of the cell
686 * @val: the value of the cell
688 * Set the value of the cell @name in @cfg with the provided value.
691 gras_cfg_set_int(gras_cfg_t *cfg,const char*name, int val) {
693 gras_error_t errcode;
695 VERB2("Configuration setting: %s=%d",name,val);
696 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
699 return gras_dynar_push(cell->content,&val);
701 return gras_dynar_set(cell->content,0,&val);
706 * gras_cfg_set_double:
707 * @cfg: the config set
708 * @name: the name of the cell
709 * @val: the doule to set
711 * Set the value of the cell @name in @cfg with the provided value.
715 gras_cfg_set_double(gras_cfg_t *cfg,const char*name, double val) {
717 gras_error_t errcode;
719 VERB2("Configuration setting: %s=%f",name,val);
720 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
723 return gras_dynar_push(cell->content,&val);
725 return gras_dynar_set(cell->content,0,&val);
730 * gras_cfg_set_string:
732 * @cfg: the config set
733 * @name: the name of the cell
734 * @val: the value to be added
736 * Set the value of the cell @name in @cfg with the provided value.
740 gras_cfg_set_string(gras_cfg_t *cfg,const char*name, const char*val) {
742 gras_error_t errcode;
743 char *newval = strdup(val);
745 VERB2("Configuration setting: %s=%s",name,val);
746 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
749 return gras_dynar_push(cell->content,&newval);
751 return gras_dynar_set(cell->content,0,&newval);
758 * @cfg: the config set
759 * @name: the name of the cell
761 * @port: the port number
763 * Set the value of the cell @name in @cfg with the provided value
764 * on the given @host to the given @port
768 gras_cfg_set_host(gras_cfg_t *cfg,const char*name,
769 const char *host,int port) {
771 gras_error_t errcode;
772 gras_host_t *val=gras_new(gras_host_t,1);
774 VERB3("Configuration setting: %s=%s:%d",name,host,port);
777 val->name = strdup(name);
780 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
783 return gras_dynar_push(cell->content,&val);
785 return gras_dynar_set(cell->content,0,&val);
789 /* ---- [ Removing ] ---- */
794 * @cfg: the config set
795 * @name: the name of the cell
796 * @val: the value to be removed
798 * Remove the provided @val from the cell @name in @cfg.
800 gras_error_t gras_cfg_rm_int (gras_cfg_t *cfg,const char*name, int val) {
804 gras_error_t errcode;
806 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
808 gras_dynar_foreach(cell->content,cpt,seen) {
810 gras_dynar_cursor_rm(cell->content,&cpt);
815 ERROR2("Can't remove the value %d of config element %s: value not found.",
817 return mismatch_error;
821 * gras_cfg_rm_double:
823 * @cfg: the config set
824 * @name: the name of the cell
825 * @val: the value to be removed
827 * Remove the provided @val from the cell @name in @cfg.
830 gras_error_t gras_cfg_rm_double(gras_cfg_t *cfg,const char*name, double val) {
834 gras_error_t errcode;
836 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
838 gras_dynar_foreach(cell->content,cpt,seen) {
840 gras_dynar_cursor_rm(cell->content,&cpt);
845 ERROR2("Can't remove the value %f of config element %s: value not found.",
847 return mismatch_error;
851 * gras_cfg_rm_string:
853 * @cfg: the config set
854 * @name: the name of the cell
855 * @val: the value of the string which will be removed
857 * Remove the provided @val from the cell @name in @cfg.
860 gras_cfg_rm_string(gras_cfg_t *cfg,const char*name, const char *val) {
864 gras_error_t errcode;
866 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
868 gras_dynar_foreach(cell->content,cpt,seen) {
869 if (!strcpy(seen,val)) {
870 gras_dynar_cursor_rm(cell->content,&cpt);
875 ERROR2("Can't remove the value %s of config element %s: value not found.",
877 return mismatch_error;
883 * @cfg: the config set
884 * @name: the name of the cell
885 * @host: the hostname
886 * @port: the port number
888 * Remove the provided @host:@port from the cell @name in @cfg.
892 gras_cfg_rm_host (gras_cfg_t *cfg,const char*name, const char *host,int port) {
896 gras_error_t errcode;
898 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
900 gras_dynar_foreach(cell->content,cpt,seen) {
901 if (!strcpy(seen->name,host) && seen->port == port) {
902 gras_dynar_cursor_rm(cell->content,&cpt);
907 ERROR3("Can't remove the value %s:%d of config element %s: value not found.",
909 return mismatch_error;
917 * @cfg: the config set
918 * @name: the name of the cell
924 gras_cfg_empty(gras_cfg_t *cfg,const char*name) {
927 gras_error_t errcode;
929 TRYCATCH(mismatch_error,
930 gras_dict_get((gras_dict_t*)cfg,name,(void**)&cell));
931 if (errcode == mismatch_error) {
932 ERROR1("Can't empty '%s' since this config element does not exist",
934 return mismatch_error;
938 gras_dynar_reset(cell->content);
943 /*----[ Getting ]---------------------------------------------------------*/
947 * @cfg: the config set
948 * @name: the name of the cell
949 * @val: the wanted value
951 * Returns the first value from the config set under the given name.
952 * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar()
955 * @warning the returned value is the actual content of the config set
958 gras_cfg_get_int (gras_cfg_t *cfg,
962 gras_error_t errcode;
964 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_int,&cell));
966 if (gras_dynar_length(cell->content) > 1) {
967 WARN2("You asked for the first value of the config element '%s', but there is %d values",
968 name, gras_dynar_length(cell->content));
971 gras_dynar_get(cell->content, 0, (void*)val);
976 * gras_cfg_get_double:
977 * @cfg: the config set
978 * @name: the name of the cell
979 * @val: the wanted value
981 * Returns the first value from the config set under the given name.
982 * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar()
985 * @warning the returned value is the actual content of the config set
989 gras_cfg_get_double(gras_cfg_t *cfg,
993 gras_error_t errcode;
995 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_double,&cell));
997 if (gras_dynar_length(cell->content) > 1) {
998 WARN2("You asked for the first value of the config element '%s', but there is %d values\n",
999 name, gras_dynar_length(cell->content));
1002 gras_dynar_get(cell->content, 0, (void*)val);
1007 * gras_cfg_get_string:
1009 * @th: the config set
1010 * @name: the name of the cell
1011 * @val: the wanted value
1013 * Returns the first value from the config set under the given name.
1014 * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar()
1017 * @warning the returned value is the actual content of the config set
1020 gras_error_t gras_cfg_get_string(gras_cfg_t *cfg,
1023 gras_cfgelm_t *cell;
1024 gras_error_t errcode;
1028 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_string,&cell));
1030 if (gras_dynar_length(cell->content) > 1) {
1031 WARN2("You asked for the first value of the config element '%s', but there is %d values\n",
1032 name, gras_dynar_length(cell->content));
1035 gras_dynar_get(cell->content, 0, (void*)val);
1040 * gras_cfg_get_host:
1042 * @cfg: the config set
1043 * @name: the name of the cell
1045 * @port: the port number
1047 * Returns the first value from the config set under the given name.
1048 * If there is more than one value, it will issue a warning. Consider using gras_cfg_get_dynar()
1051 * @warning the returned value is the actual content of the config set
1054 gras_error_t gras_cfg_get_host (gras_cfg_t *cfg,
1058 gras_cfgelm_t *cell;
1059 gras_error_t errcode;
1062 TRY (gras_cfgelm_get(cfg,name,gras_cfgelm_host,&cell));
1064 if (gras_dynar_length(cell->content) > 1) {
1065 WARN2("You asked for the first value of the config element '%s', but there is %d values\n",
1066 name, gras_dynar_length(cell->content));
1069 gras_dynar_get(cell->content, 0, (void*)val);
1077 * gras_cfg_get_dynar:
1078 * @cfg: where to search in
1079 * @name: what to search for
1082 * Get the data stored in the config bag.
1084 * @warning the returned value is the actual content of the config set
1086 gras_error_t gras_cfg_get_dynar (gras_cfg_t *cfg,
1088 gras_dynar_t **dynar) {
1089 gras_cfgelm_t *cell;
1090 gras_error_t errcode = gras_dict_get((gras_dict_t*)cfg,name,
1093 if (errcode == mismatch_error) {
1094 ERROR1("No registered cell %s in this config set",
1096 return mismatch_error;
1098 if (errcode != no_error)
1101 *dynar = cell->content;