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.1 2006/11/20 13:38:01 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             dbg_err_if(u_config_add_child(c, u_string_c(lastkey), &child));
00331             dbg_err_if(u_config_do_load(child, cb, arg, overwrite));
00332             dbg_err_if(u_string_clear(lastkey));
00333             continue;
00334         } else if(ln[0] == '}') {
00335             warn_err_ifm(c->parent == NULL,"config error: unmatched '}'");
00336             if(!u_isblank_str(++ln))
00337                 warn_err("config error [line %d]: { or } must be the "
00338                          "only not-blank char in a line", lineno);
00339             break; /* exit */
00340         }
00341 
00342         /* find the end of the key string */
00343         for(p = ln; *p && !u_isblank(*p); ++p);
00344 
00345         /* set the key */
00346         dbg_err_if(u_string_set(key, ln, p-ln));
00347 
00348         /* set the value */
00349         dbg_err_if(u_string_set(value, p, strlen(p)));
00350         dbg_err_if(u_string_trim(value));
00351 
00352         /* if the valus is empty an open bracket will follow, save the key */
00353         if(u_string_len(value) == 0)
00354         {
00355             dbg_err_if(u_string_set(lastkey, ln, p-ln));
00356             continue;
00357         }
00358 
00359         /* add to the var list */
00360         dbg_err_if(u_config_do_set_key(c, 
00361                         u_string_c(key), 
00362                         u_string_len(value) ? u_string_c(value) : NULL, 
00363                         overwrite));
00364     }
00365     
00366     u_string_free(lastkey);
00367     u_string_free(value);
00368     u_string_free(key);
00369     u_string_free(line);
00370 
00371     return 0;
00372 err:
00373     if(lastkey)
00374         u_string_free(lastkey);
00375     if(key)
00376         u_string_free(key);
00377     if(value)
00378         u_string_free(value);
00379     if(line)
00380         u_string_free(line);
00381     return ~0;
00382 }
00383 
00384 int u_config_load_from(u_config_t *c, u_config_gets_t cb, 
00385     void *arg, int overwrite)
00386 {
00387     dbg_err_if(u_config_do_load(c, cb, arg, overwrite));
00388 
00389     return 0;
00390 err:
00391     return ~0;
00392 }
00393 
00394 static char *u_config_fgetline(void* arg, char * buf, size_t size)
00395 {
00396     FILE *f = (FILE*)arg;
00397 
00398     return fgets(buf, size, f);
00399 }
00400 
00416 int u_config_load(u_config_t *c, int fd, int overwrite)
00417 {
00418     FILE *file;
00419 
00420     /* must dup because fclose() calls close(2) on fd */
00421     file = fdopen(dup(fd), "r");
00422     dbg_err_if(file == NULL);
00423 
00424     dbg_err_if(u_config_do_load(c, u_config_fgetline, file, overwrite));
00425 
00426     fclose(file);
00427 
00428     return 0;
00429 err:
00430     U_FCLOSE(file);
00431     return ~0;
00432 }
00433 
00444 int u_config_load_from_file (const char *file, u_config_t **pc)
00445 {
00446     int fd = -1;
00447     u_config_t *c = NULL;
00448 
00449     dbg_return_if (file == NULL, ~0);
00450     dbg_return_if (pc == NULL, ~0);
00451 
00452     warn_err_if (u_config_create(&c));
00453     warn_err_if ((fd = open(file, O_RDONLY)) == -1); 
00454     warn_err_if (u_config_load(c, fd, 0));
00455 
00456     (void) close(fd);
00457 
00458     *pc = c;
00459 
00460     return 0;
00461 err:
00462     (void) u_config_free(c);
00463     U_CLOSE(fd);
00464     return ~0;
00465 }
00466 
00477 int u_config_create(u_config_t **pc)
00478 {
00479     u_config_t *c = NULL;
00480 
00481     c = u_zalloc(sizeof(u_config_t));
00482     dbg_err_if(c == NULL);
00483 
00484     TAILQ_INIT(&c->children);
00485 
00486     *pc = c;
00487 
00488     return 0;
00489 err:
00490     if(c)
00491         u_config_free(c);
00492     return ~0;
00493 }
00494 
00504 static void u_config_del_key(u_config_t *c, u_config_t *child)
00505 {
00506     TAILQ_REMOVE(&c->children, child, np);
00507 }
00508 
00518 int u_config_free(u_config_t *c)
00519 {
00520     u_config_t *child = NULL;
00521     if(c)
00522     {
00523         /* free all children */
00524         while((child = TAILQ_FIRST(&c->children)) != NULL)
00525         {
00526             u_config_del_key(c, child);
00527             dbg_err_if(u_config_free(child));
00528         }
00529         /* free parent */
00530         if(c->key)
00531             U_FREE(c->key);
00532         if(c->value)
00533             U_FREE(c->value);
00534         U_FREE(c);
00535     }
00536     return 0;
00537 err:
00538     return ~0;
00539 }
00540 
00550 const char* u_config_get_key(u_config_t *c)
00551 {
00552     dbg_err_if(!c);
00553 
00554     return c->key;
00555 err:
00556     return NULL;
00557 }
00558 
00568 const char* u_config_get_value(u_config_t *c)
00569 {
00570     dbg_err_if(!c);
00571     
00572     return c->value;
00573 err:
00574     return NULL;
00575 }
00576 
00587 const char* u_config_get_subkey_value(u_config_t *c, const char *subkey)
00588 {
00589     u_config_t *skey;
00590 
00591     nop_err_if(u_config_get_subkey(c, subkey, &skey));
00592 
00593     return u_config_get_value(skey);
00594 err:
00595     return NULL;
00596 }
00597 
00611 int u_config_get_subkey_value_i(u_config_t *c, const char *subkey, int def, 
00612     int *out)
00613 {
00614     const char *v;
00615     char *ep = NULL;
00616     long l;
00617 
00618     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00619     {
00620         *out = def; 
00621         return 0;
00622     }
00623 
00624     l = strtol(v, &ep, 0);
00625     dbg_err_if(*ep != '\0'); /* dirty int value (for ex. 123text) */
00626 
00627     /* same cast used by atoi */
00628     *out = (int)l;
00629 
00630     return 0;
00631 err:
00632     return ~0;
00633 }
00634 
00649 int u_config_get_subkey_value_b(u_config_t *c, const char *subkey, int def,  
00650     int *out)
00651 {
00652     const char *true_words[]  = { "yes", "enable", "1", "on", NULL };
00653     const char *false_words[] = { "no", "disable", "0", "off", NULL };
00654     const char *v, *w;
00655 
00656     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00657     {
00658         *out = def;
00659         return 0;
00660     }
00661 
00662     for(w = *true_words; *w; ++w)
00663     {
00664         if(!strcasecmp(v, w))
00665         {
00666             *out = 1;
00667             return 0; /* ok */
00668         }
00669     }
00670 
00671     for(w = *false_words; *w; ++w)
00672     {
00673         if(!strcasecmp(v, w))
00674         {
00675             *out = 0;
00676             return 0; /* ok */
00677         }
00678     }
00679 
00680     return ~0; /* not-bool value */
00681 }
00682 

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