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

config.c

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

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