Main Page | Modules | Data Structures | Directories | File List | Data Fields

config.c

00001 /* 
00002  * Copyright (c) 2005-2007 by KoanLogic s.r.l. - All rights reserved.  
00003  */
00004 
00005 static const char rcsid[] =
00006     "$Id: config.c,v 1.3 2007/02/12 08:32:27 tho Exp $";
00007 
00008 #include <sys/types.h>
00009 #include <stdlib.h>
00010 #include <unistd.h>
00011 #include <string.h>
00012 #include <stdio.h>
00013 #include <fcntl.h>
00014 
00015 #include <toolbox/carpal.h>
00016 #include <toolbox/queue.h>
00017 #include <toolbox/config.h>
00018 #include <toolbox/misc.h>
00019 #include <toolbox/memory.h>
00020 #include <toolbox/str.h>
00021 
00034 TAILQ_HEAD(u_config_list_s, u_config_s);
00035 typedef struct u_config_list_s u_config_list_t;
00036 
00037 struct u_config_s
00038 {
00039     TAILQ_ENTRY(u_config_s) np; /* next & prev pointers */
00040     char *key;                  /* config item key name */
00041     char *value;                /* config item value    */
00042     u_config_list_t children;   /* subkeys              */
00043     u_config_t *parent;         /* parent config obj    */
00044 };
00045 
00057 void u_config_print(u_config_t *c, int lev)
00058 {
00059     u_config_t *item;
00060     int i;
00061 
00062     for(i = 0; i < lev; ++i)
00063         printf("  ");
00064     if (c->key)
00065         printf("%s: %s\n", c->key, c->value == NULL ? "" : c->value);
00066 
00067     ++lev;
00068     TAILQ_FOREACH(item, &c->children, np)
00069         u_config_print(item, lev);
00070 }
00071 
00072 int u_config_add_child(u_config_t *c, const char *key, u_config_t **pc)
00073 {
00074     u_config_t *child = NULL;
00075 
00076     dbg_err_if(u_config_create(&child));
00077 
00078     child->parent = c;
00079     child->key = u_strdup(key);
00080     dbg_err_if(child->key == NULL);
00081 
00082     TAILQ_INSERT_TAIL(&c->children, child, np);
00083 
00084     *pc = child;
00085 
00086     return 0;
00087 err:
00088     return ~0;
00089 }
00090 
00091 /* get n-th child item called key */
00092 u_config_t* u_config_get_child_n(u_config_t *c, const char *key, int n)
00093 {
00094     u_config_t *item;
00095 
00096     TAILQ_FOREACH(item, &c->children, np)
00097     {
00098         if(strcmp(item->key, key) == 0 && n-- == 0)
00099             return item;  /* found */
00100     }
00101 
00102     return NULL; /* not found */
00103 }
00104 
00105 u_config_t* u_config_get_child(u_config_t *c, const char *key)
00106 {
00107     return u_config_get_child_n(c, key, 0);
00108 }
00109 
00110 int u_config_get_subkey_nth(u_config_t *c, const char *subkey, int n, 
00111     u_config_t **pc)
00112 {
00113     u_config_t *child = NULL;
00114     char *first_key = NULL, *p;
00115 
00116     if((p = strchr(subkey, '.')) == NULL)
00117     {
00118         if((child = u_config_get_child_n(c, subkey, n)) != NULL)
00119         {
00120             *pc = child;
00121             return 0;
00122         } 
00123     } else {
00124         if((first_key = u_strndup(subkey, p-subkey)) != NULL)
00125         {
00126             child = u_config_get_child(c, first_key);
00127             U_FREE(first_key);
00128         }
00129         if(child != NULL)
00130             return u_config_get_subkey(child, ++p, pc);
00131     }
00132     return ~0; /* not found */
00133 
00134 }
00135 
00136 int u_config_get_subkey(u_config_t *c, const char *subkey, u_config_t **pc)
00137 {
00138     return u_config_get_subkey_nth(c, subkey, 0, pc);
00139 }
00140 
00141 static u_config_t* u_config_get_root(u_config_t *c)
00142 {
00143     while(c->parent)
00144         c = c->parent;
00145     return c;
00146 }
00147 
00148 static int u_config_set_value(u_config_t *c, const char *val)
00149 {
00150     u_config_t *root, *ignore;
00151     const char *varval, *vs, *ve, *p;
00152     u_string_t *var = NULL, *value = NULL;
00153 
00154     dbg_err_if(c == NULL);
00155 
00156     /* free() previous value if any */
00157     if(c->value)
00158     {
00159         U_FREE(c->value);
00160         c->value = NULL;
00161     } 
00162 
00163     if(val)
00164     {
00165         dbg_err_if(u_string_create(NULL, 0, &var));
00166         dbg_err_if(u_string_create(NULL, 0, &value));
00167 
00168         root = u_config_get_root(c);
00169         dbg_err_if(root == NULL);
00170 
00171         /* search and replace ${variables} */
00172         vs = ve = val;
00173         for(; vs && *vs && (p = strstr(vs, "${")) != NULL; vs = ++ve)
00174         {   /* variable substituion */
00175             dbg_err_if(u_string_append(value, vs, p-vs));
00176 
00177             /* skip ${ */
00178             vs = p+2;               
00179 
00180             /* look for closig bracket */
00181             ve = strchr(vs, '}');
00182             dbg_err_if(ve == NULL); /* closing bracket missing */
00183 
00184             /* get the variable name/path */
00185             dbg_err_if(u_string_set(var, vs, ve-vs));
00186 
00187             /* first see if the variable can be resolved in the local scope
00188                otherwise resolve it from the root */
00189             root = c->parent;
00190             if(u_config_get_subkey(root, u_string_c(var), &ignore))
00191                 root = u_config_get_root(c);
00192             dbg_err_if(root == NULL);
00193 
00194             /* append the variable value */
00195             varval = u_config_get_subkey_value(root, u_string_c(var));
00196             if(varval != NULL)
00197                 dbg_err_if(u_string_append(value, varval, strlen(varval)));
00198         }
00199         if(ve && *ve)
00200             dbg_err_if(u_string_append(value, ve, strlen(ve)));
00201 
00202         u_string_trim(value); /* remove leading and trailing spaces */
00203 
00204         c->value = u_strdup(u_string_c(value));;
00205         dbg_err_if(c->value == NULL);
00206 
00207         u_string_free(value);
00208         u_string_free(var);
00209     }
00210 
00211     return 0;
00212 err:
00213     if(value)
00214         u_string_free(value);
00215     if(var)
00216         u_string_free(var);
00217     return ~0;
00218 }
00219 
00220 static int u_config_do_set_key(u_config_t *c, const char *key, const char *val, 
00221     int overwrite)
00222 {
00223     u_config_t *child = NULL;
00224     char *p, *child_key;
00225 
00226     if((p = strchr(key, '.')) == NULL)
00227     {
00228         child = u_config_get_child(c, key);
00229         if(child == NULL || !overwrite)
00230         {   /* there's no such child, add a new child */
00231             dbg_err_if(u_config_add_child(c, key, &child));
00232         } 
00233         dbg_err_if(u_config_set_value(child, val));
00234     } else {
00235         child_key = u_strndup(key, p-key);
00236         dbg_err_if(child_key == NULL);
00237         if((child = u_config_get_child(c, child_key)) == NULL)
00238             dbg_err_if(u_config_add_child(c, child_key, &child));
00239         U_FREE(child_key);
00240         return u_config_do_set_key(child, ++p, val, overwrite);
00241     }
00242     return 0;
00243 err:
00244     return ~0;
00245 }
00246 
00247 int u_config_add_key(u_config_t *c, const char *key, const char *val)
00248 {
00249     return u_config_do_set_key(c, key, val, 0 /* don't overwrite */);
00250 }
00251 
00252 int u_config_set_key(u_config_t *c, const char *key, const char *val)
00253 {
00254     return u_config_do_set_key(c, key, val, 1 /* overwrite */);
00255 }
00256 
00257 static int cs_getline(u_config_gets_t cb, void *arg, u_string_t *ln)
00258 {
00259     enum { BUFSZ = 1024 };
00260     char buf[BUFSZ], *p = NULL;
00261     ssize_t len;
00262 
00263     u_string_clear(ln);
00264 
00265     while((p = cb(arg, buf, BUFSZ)) != NULL)
00266     {
00267         len = strlen(buf);
00268         if(len == 0)
00269             break; /* empty line */
00270         dbg_err_if(u_string_append(ln, buf, --len));
00271         if(!u_isnl(buf[len]))
00272             continue; /* line's longer the bufsz (or eof);get next line chunk */
00273         else
00274             break;
00275     }
00276 
00277     dbg_err_if(p == NULL);
00278 
00279     return 0;
00280 err:
00281     return ~0;
00282 }
00283 
00284 static int u_config_do_load(u_config_t *c, u_config_gets_t cb, void *arg, 
00285     int overwrite)
00286 {
00287     enum { MAX_NEST_LEV = 20 };
00288     u_string_t *line = NULL, *key = NULL, *lastkey = NULL, *value = NULL;
00289     const char *ln, *p;
00290     size_t len;
00291     int lineno = 1;
00292     u_config_t *child = NULL;
00293 
00294     dbg_err_if(u_string_create(NULL, 0, &line));
00295     dbg_err_if(u_string_create(NULL, 0, &key));
00296     dbg_err_if(u_string_create(NULL, 0, &value));
00297     dbg_err_if(u_string_create(NULL, 0, &lastkey));
00298 
00299     for(; cs_getline(cb, arg, line) == 0; u_string_clear(line), ++lineno)
00300     {
00301         /* remove comments if any */
00302         if((p = strchr(u_string_c(line), '#')) != NULL)
00303             dbg_err_if(u_string_set_length(line, p - u_string_c(line)));
00304 
00305         /* remove leading and trailing blanks */
00306         dbg_err_if(u_string_trim(line));
00307 
00308         ln = u_string_c(line);
00309         len = u_string_len(line);
00310 
00311         /* remove trailing nl(s) */
00312         while(len && u_isnl(ln[len-1]))
00313             u_string_set_length(line, --len);
00314 
00315         if(len == 0)
00316             continue; /* empty line */
00317 
00318         /* eat leading blanks */
00319         for(; u_isblank(*ln); ++ln);
00320 
00321         if(ln[0] == '{')
00322         {   /* group config values */
00323             if(u_string_len(lastkey) == 0)
00324                 warn_err("config error [line %d]: { not after a no-value key", 
00325                          lineno);
00326             if(!u_isblank_str(++ln))
00327                 warn_err("config error [line %d]: { or } must be the "
00328                          "only not-blank char in a line", lineno);
00329 
00330             /* modify the existing child (when overwriting) or add a new one */
00331             if(!overwrite || 
00332                (child = u_config_get_child(c, u_string_c(lastkey))) == NULL)
00333             {
00334                 dbg_err_if(u_config_add_child(c, u_string_c(lastkey), &child));
00335             }
00336 
00337             dbg_err_if(u_config_do_load(child, cb, arg, overwrite));
00338             dbg_err_if(u_string_clear(lastkey));
00339             continue;
00340         } else if(ln[0] == '}') {
00341             warn_err_ifm(c->parent == NULL,"config error: unmatched '}'");
00342             if(!u_isblank_str(++ln))
00343                 warn_err("config error [line %d]: { or } must be the "
00344                          "only not-blank char in a line", lineno);
00345             break; /* exit */
00346         }
00347 
00348         /* find the end of the key string */
00349         for(p = ln; *p && !u_isblank(*p); ++p);
00350 
00351         /* set the key */
00352         dbg_err_if(u_string_set(key, ln, p-ln));
00353 
00354         /* set the value */
00355         dbg_err_if(u_string_set(value, p, strlen(p)));
00356         dbg_err_if(u_string_trim(value));
00357 
00358         /* if the valus is empty an open bracket will follow, save the key */
00359         if(u_string_len(value) == 0)
00360         {
00361             dbg_err_if(u_string_set(lastkey, ln, p-ln));
00362             continue;
00363         }
00364 
00365         /* add to the var list */
00366         dbg_err_if(u_config_do_set_key(c, 
00367                         u_string_c(key), 
00368                         u_string_len(value) ? u_string_c(value) : NULL, 
00369                         overwrite));
00370     }
00371     
00372     u_string_free(lastkey);
00373     u_string_free(value);
00374     u_string_free(key);
00375     u_string_free(line);
00376 
00377     return 0;
00378 err:
00379     if(lastkey)
00380         u_string_free(lastkey);
00381     if(key)
00382         u_string_free(key);
00383     if(value)
00384         u_string_free(value);
00385     if(line)
00386         u_string_free(line);
00387     return ~0;
00388 }
00389 
00390 int u_config_load_from(u_config_t *c, u_config_gets_t cb, 
00391     void *arg, int overwrite)
00392 {
00393     dbg_err_if(u_config_do_load(c, cb, arg, overwrite));
00394 
00395     return 0;
00396 err:
00397     return ~0;
00398 }
00399 
00400 static char *u_config_fgetline(void* arg, char * buf, size_t size)
00401 {
00402     FILE *f = (FILE*)arg;
00403 
00404     return fgets(buf, size, f);
00405 }
00406 
00422 int u_config_load(u_config_t *c, int fd, int overwrite)
00423 {
00424     FILE *file;
00425 
00426     /* must dup because fclose() calls close(2) on fd */
00427     file = fdopen(dup(fd), "r");
00428     dbg_err_if(file == NULL);
00429 
00430     dbg_err_if(u_config_do_load(c, u_config_fgetline, file, overwrite));
00431 
00432     fclose(file);
00433 
00434     return 0;
00435 err:
00436     U_FCLOSE(file);
00437     return ~0;
00438 }
00439 
00450 int u_config_load_from_file (const char *file, u_config_t **pc)
00451 {
00452     int fd = -1;
00453     u_config_t *c = NULL;
00454 
00455     dbg_return_if (file == NULL, ~0);
00456     dbg_return_if (pc == NULL, ~0);
00457 
00458     warn_err_if (u_config_create(&c));
00459     warn_err_if ((fd = open(file, O_RDONLY)) == -1); 
00460     warn_err_if (u_config_load(c, fd, 0));
00461 
00462     (void) close(fd);
00463 
00464     *pc = c;
00465 
00466     return 0;
00467 err:
00468     (void) u_config_free(c);
00469     U_CLOSE(fd);
00470     return ~0;
00471 }
00472 
00483 int u_config_create(u_config_t **pc)
00484 {
00485     u_config_t *c = NULL;
00486 
00487     c = u_zalloc(sizeof(u_config_t));
00488     dbg_err_if(c == NULL);
00489 
00490     TAILQ_INIT(&c->children);
00491 
00492     *pc = c;
00493 
00494     return 0;
00495 err:
00496     if(c)
00497         u_config_free(c);
00498     return ~0;
00499 }
00500 
00510 static void u_config_del_key(u_config_t *c, u_config_t *child)
00511 {
00512     TAILQ_REMOVE(&c->children, child, np);
00513 }
00514 
00524 int u_config_free(u_config_t *c)
00525 {
00526     u_config_t *child = NULL;
00527     if(c)
00528     {
00529         /* free all children */
00530         while((child = TAILQ_FIRST(&c->children)) != NULL)
00531         {
00532             u_config_del_key(c, child);
00533             dbg_err_if(u_config_free(child));
00534         }
00535         /* free parent */
00536         if(c->key)
00537             U_FREE(c->key);
00538         if(c->value)
00539             U_FREE(c->value);
00540         U_FREE(c);
00541     }
00542     return 0;
00543 err:
00544     return ~0;
00545 }
00546 
00556 const char* u_config_get_key(u_config_t *c)
00557 {
00558     dbg_err_if(!c);
00559 
00560     return c->key;
00561 err:
00562     return NULL;
00563 }
00564 
00574 const char* u_config_get_value(u_config_t *c)
00575 {
00576     dbg_err_if(!c);
00577     
00578     return c->value;
00579 err:
00580     return NULL;
00581 }
00582 
00593 const char* u_config_get_subkey_value(u_config_t *c, const char *subkey)
00594 {
00595     u_config_t *skey;
00596 
00597     nop_err_if(u_config_get_subkey(c, subkey, &skey));
00598 
00599     return u_config_get_value(skey);
00600 err:
00601     return NULL;
00602 }
00603 
00617 int u_config_get_subkey_value_i(u_config_t *c, const char *subkey, int def, 
00618     int *out)
00619 {
00620     const char *v;
00621     char *ep = NULL;
00622     long l;
00623 
00624     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00625     {
00626         *out = def; 
00627         return 0;
00628     }
00629 
00630     l = strtol(v, &ep, 0);
00631     dbg_err_if(*ep != '\0'); /* dirty int value (for ex. 123text) */
00632 
00633     /* same cast used by atoi */
00634     *out = (int)l;
00635 
00636     return 0;
00637 err:
00638     return ~0;
00639 }
00640 
00655 int u_config_get_subkey_value_b(u_config_t *c, const char *subkey, int def,  
00656     int *out)
00657 {
00658     const char *true_words[]  = { "yes", "enable", "1", "on", NULL };
00659     const char *false_words[] = { "no", "disable", "0", "off", NULL };
00660     const char *v, *w;
00661 
00662     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00663     {
00664         *out = def;
00665         return 0;
00666     }
00667 
00668     for(w = *true_words; *w; ++w)
00669     {
00670         if(!strcasecmp(v, w))
00671         {
00672             *out = 1;
00673             return 0; /* ok */
00674         }
00675     }
00676 
00677     for(w = *false_words; *w; ++w)
00678     {
00679         if(!strcasecmp(v, w))
00680         {
00681             *out = 0;
00682             return 0; /* ok */
00683         }
00684     }
00685 
00686     return ~0; /* not-bool value */
00687 }
00688 

←Products
© 2005-2007 - KoanLogic S.r.l. - All rights reserved