config.c

00001 /* 
00002  * Copyright (c) 2005-2012 by KoanLogic s.r.l. - All rights reserved.  
00003  */
00004 
00005 #include <sys/types.h>
00006 #include <stdlib.h>
00007 #include <unistd.h>
00008 #include <string.h>
00009 #include <strings.h>
00010 #include <stdio.h>
00011 #include <fcntl.h>
00012 
00013 #include <toolbox/carpal.h>
00014 #include <toolbox/queue.h>
00015 #include <toolbox/list.h>
00016 #include <toolbox/config.h>
00017 #include <toolbox/misc.h>
00018 #include <toolbox/memory.h>
00019 #include <toolbox/str.h>
00020 
00021 
00022 TAILQ_HEAD(u_config_list_s, u_config_s);
00023 typedef struct u_config_list_s u_config_list_t;
00024 
00025 struct u_config_s
00026 {
00027     TAILQ_ENTRY(u_config_s) np; /* next & prev pointers */
00028     char *key;                  /* config item key name */
00029     char *value;                /* config item value    */
00030     u_config_list_t children;   /* subkeys              */
00031     u_config_t *parent;         /* parent config obj    */
00032 };
00033 
00034 /* get() callback helper struct */
00035 struct u_config_buf_s
00036 {
00037     char *data;
00038     size_t len;
00039 };
00040 
00041 /* file system pre-defined driver */
00042 extern u_config_driver_t u_config_drv_fs;
00043 
00044 static u_config_t *u_config_get_root (u_config_t *c);
00045 static int u_config_do_set_key (u_config_t *c, const char *key, const char *val,
00046         int overwrite, u_config_t **pchild);
00047 static int cs_getline (u_config_gets_t cb, void *arg, u_string_t *ln);
00048 static int u_config_do_load_drv (u_config_t *c, u_config_driver_t *drv, 
00049         void *arg, int overwrite);
00050 static int u_config_include (u_config_t *c, u_config_driver_t *drv, 
00051         u_config_t *inckey, int overwrite);
00052 static void u_config_del_key (u_config_t *c, u_config_t *child);
00053 static int u_config_to_str (u_config_t *c, u_string_t *s);
00054 static char *u_config_buf_gets (void *arg, char *buf, size_t size);
00055 
00056 
00180 int u_config_sort_children (u_config_t *c, 
00181         int (*config_cmp)(u_config_t **, u_config_t **))
00182 {
00183     u_config_t *child;
00184     int i, count;
00185     u_config_t **children = NULL;
00186 
00187     /* count children */
00188     count = 0;
00189     TAILQ_FOREACH(child, &c->children, np)
00190         count++;
00191 
00192     /* create an array of pointers */
00193     children = u_zalloc(count * sizeof(u_config_t *));
00194     dbg_err_if(children == NULL);
00195 
00196     /* populate the array of children */
00197     i = 0;
00198     TAILQ_FOREACH(child, &c->children, np)
00199         children[i++] = child;
00200 
00201     /* sort the list */
00202     qsort(children, count, sizeof(u_config_t *), 
00203             (int(*)(const void *, const void *)) config_cmp);
00204 
00205     /* remove all items from the list */
00206     while((child = TAILQ_FIRST(&c->children)) != NULL)
00207         TAILQ_REMOVE(&c->children, child, np);
00208 
00209     /* reinsert sorted items */
00210     for(i = 0; i < count; ++i)
00211         TAILQ_INSERT_TAIL(&c->children, children[i], np);
00212 
00213     U_FREE(children);
00214 
00215     return 0;
00216 err:
00217     if(children)
00218         u_free(children);
00219     return ~0;
00220 }
00221 
00234 void u_config_print_to_fp (u_config_t *c, FILE *fp, int lev)
00235 {
00236 #define U_CONFIG_INDENT(fp, l)  \
00237     do { int i; for(i = 1; i < l; ++i) fputs("\t", fp); } while (0);
00238 
00239     u_config_t *item;
00240 
00241     U_CONFIG_INDENT(fp, lev);
00242 
00243     /* "include" directives are skipped since that wouldn't give semantical 
00244      * equivalence to the dumped object (included key/val's are already 
00245      * there) */
00246     if (c->key && strcmp(c->key, "include"))
00247     {
00248         fprintf(fp, "%s", c->key);
00249         if (c->value != NULL) 
00250             fprintf(fp, "\t%s", c->value);
00251         fputs("\n", fp);
00252     }
00253 
00254     ++lev;
00255 
00256     if(u_config_has_children(c))
00257     {
00258         if(c->parent)
00259         {
00260             /* align braces to the previous level */
00261             U_CONFIG_INDENT(fp, lev - 1);
00262             fprintf(fp, "{\n");
00263         }
00264 
00265         TAILQ_FOREACH(item, &c->children, np)
00266             u_config_print_to_fp(item, fp, lev);
00267 
00268         if(c->parent)
00269         {
00270             U_CONFIG_INDENT(fp, lev - 1);
00271             fprintf(fp, "}\n");
00272         }
00273     }
00274 #undef U_CONFIG_INDENT
00275 }
00276 
00288 void u_config_print(u_config_t *c, int lev)
00289 {
00290     u_config_print_to_fp(c, stdout, lev);
00291     return;
00292 }
00293 
00308 int u_config_add_child (u_config_t *c, const char *key, u_config_t **pc)
00309 {
00310     u_config_t *child = NULL;
00311 
00312     dbg_err_if(u_config_create(&child));
00313 
00314     child->parent = c;
00315     child->key = u_strdup(key);
00316     dbg_err_if(child->key == NULL);
00317 
00318     TAILQ_INSERT_TAIL(&c->children, child, np);
00319 
00320     *pc = child;
00321 
00322     return 0;
00323 err:
00324     return ~0;
00325 }
00326 
00339 u_config_t *u_config_get_child_n (u_config_t *c, const char *key, int n)
00340 {
00341     u_config_t *item;
00342 
00343     TAILQ_FOREACH(item, &c->children, np)
00344     {
00345         if((key == NULL || strcmp(item->key, key) == 0) && n-- == 0)
00346             return item;  /* found */
00347     }
00348 
00349     return NULL; /* not found */
00350 }
00351 
00362 u_config_t *u_config_get_child (u_config_t *c, const char *key)
00363 {
00364     return u_config_get_child_n(c, key, 0);
00365 }
00366 
00383 int u_config_get_subkey_nth (u_config_t *c, const char *subkey, int n, 
00384         u_config_t **pc)
00385 {
00386     u_config_t *child = NULL;
00387     char *first_key = NULL, *p;
00388 
00389     if ((p = strchr(subkey, '.')) == NULL)
00390     {
00391         if ((child = u_config_get_child_n(c, subkey, n)) != NULL)
00392         {
00393             *pc = child;
00394             return 0;
00395         } 
00396     } 
00397     else 
00398     {
00399         if ((first_key = u_strndup(subkey, p - subkey)) != NULL)
00400         {
00401             child = u_config_get_child(c, first_key);
00402             U_FREE(first_key);
00403         }
00404 
00405         if (child != NULL)
00406             return u_config_get_subkey(child, ++p, pc);
00407     }
00408 
00409     return ~0; /* not found */
00410 }
00411 
00425 int u_config_get_subkey (u_config_t *c, const char *subkey, u_config_t **pc)
00426 {
00427     return u_config_get_subkey_nth(c, subkey, 0, pc);
00428 }
00429 
00441 int u_config_set_value (u_config_t *c, const char *val)
00442 {
00443     u_config_t *root, *ignore;
00444     const char *varval, *vs, *ve, *p;
00445     u_string_t *var = NULL, *value = NULL;
00446 
00447     dbg_err_if(c == NULL);
00448 
00449     /* free() previous value if any */
00450     if(c->value)
00451     {
00452         U_FREE(c->value);
00453         c->value = NULL;
00454     } 
00455 
00456     if(val)
00457     {
00458         dbg_err_if(u_string_create(NULL, 0, &var));
00459         dbg_err_if(u_string_create(NULL, 0, &value));
00460 
00461         root = u_config_get_root(c);
00462         dbg_err_if(root == NULL);
00463 
00464         /* search and replace ${variables} */
00465         vs = ve = val;
00466         for(; vs && *vs && (p = strstr(vs, "${")) != NULL; vs = ++ve)
00467         {   /* variable substituion */
00468             dbg_err_if(u_string_append(value, vs, p-vs));
00469 
00470             /* skip ${ */
00471             vs = p+2;               
00472 
00473             /* look for closing bracket */
00474             ve = strchr(vs, '}');
00475             dbg_err_if(ve == NULL); /* closing bracket missing */
00476 
00477             /* get the variable name/path */
00478             dbg_err_if(u_string_set(var, vs, ve-vs));
00479 
00480             /* first see if the variable can be resolved in the local scope
00481                otherwise resolve it from the root */
00482             root = c->parent;
00483             if(u_config_get_subkey(root, u_string_c(var), &ignore))
00484                 root = u_config_get_root(c);
00485             dbg_err_if(root == NULL);
00486 
00487             /* append the variable value */
00488             varval = u_config_get_subkey_value(root, u_string_c(var));
00489             if(varval != NULL)
00490                 dbg_err_if(u_string_append(value, varval, strlen(varval)));
00491         }
00492         if(ve && *ve)
00493             dbg_err_if(u_string_append(value, ve, strlen(ve)));
00494 
00495         u_string_trim(value); /* remove leading and trailing spaces */
00496 
00497         c->value = u_strdup(u_string_c(value));;
00498         dbg_err_if(c->value == NULL);
00499 
00500         u_string_free(value);
00501         u_string_free(var);
00502     }
00503 
00504     return 0;
00505 err:
00506     if(value)
00507         u_string_free(value);
00508     if(var)
00509         u_string_free(var);
00510     return ~0;
00511 }
00512 
00526 int u_config_add_key (u_config_t *c, const char *key, const char *val)
00527 {
00528     return u_config_do_set_key(c, key, val, 0 /* don't overwrite */, NULL);
00529 }
00530 
00544 int u_config_set_key (u_config_t *c, const char *key, const char *val)
00545 {
00546     return u_config_do_set_key(c, key, val, 1 /* overwrite */, NULL);
00547 }
00548 
00564 int u_config_load_from (u_config_t *c, u_config_gets_t cb, void *arg, 
00565         int overwrite)
00566 {
00567     u_config_driver_t drv = { NULL, NULL, cb, NULL };
00568 
00569     dbg_err_if(u_config_do_load_drv(c, &drv, arg, overwrite));
00570 
00571     return 0;
00572 err:
00573     return ~0;
00574 }
00575 
00592 int u_config_load (u_config_t *c, int fd, int overwrite)
00593 {
00594     FILE *file = NULL;
00595 
00596     /* must dup because fclose() calls close(2) on fd */
00597     file = fdopen(dup(fd), "r");
00598     dbg_err_if(file == NULL);
00599 
00600     dbg_err_if(u_config_do_load_drv(c, &u_config_drv_fs, file, overwrite));
00601 
00602     fclose(file);
00603 
00604     return 0;
00605 err:
00606     U_FCLOSE(file);
00607     return ~0;
00608 }
00609 
00621 int u_config_load_from_file (const char *file, u_config_t **pc)
00622 {
00623     dbg_return_if (file == NULL, ~0);
00624     dbg_return_if (pc == NULL, ~0);
00625 
00626     dbg_err_if (u_config_load_from_drv(file, &u_config_drv_fs, 0, pc));
00627 
00628     return 0;
00629 err:
00630     return ~0;
00631 }
00632 
00644 int u_config_create (u_config_t **pc)
00645 {
00646     u_config_t *c = NULL;
00647 
00648     c = u_zalloc(sizeof(u_config_t));
00649     dbg_err_sif (c == NULL);
00650 
00651     TAILQ_INIT(&c->children);
00652 
00653     *pc = c;
00654 
00655     return 0;
00656 err:
00657     if(c)
00658         u_config_free(c);
00659     return ~0;
00660 }
00661 
00673 int u_config_del_child (u_config_t *c, u_config_t *child)
00674 {
00675     u_config_t *item;
00676 
00677     TAILQ_FOREACH(item, &c->children, np)
00678     {
00679         if(item == child)   /* found */
00680         {   
00681             u_config_del_key(c, child);
00682             dbg_err_if(u_config_free(child));
00683             return 0;
00684         }
00685     }
00686 
00687 err:
00688     return ~0;
00689 }
00690 
00701 int u_config_free (u_config_t *c)
00702 {
00703     u_config_t *child = NULL;
00704 
00705     if (c)
00706     {
00707         /* free all children */
00708         while ((child = TAILQ_FIRST(&c->children)) != NULL)
00709         {
00710             u_config_del_key(c, child);
00711             (void) u_config_free(child);
00712         }
00713 
00714         /* free parent */
00715         U_FREE(c->key);
00716         U_FREE(c->value);
00717         u_free(c);
00718     }
00719 
00720     return 0;
00721 }
00722 
00733 const char *u_config_get_key (u_config_t *c)
00734 {
00735     dbg_return_if (c == NULL, NULL);
00736 
00737     return c->key;
00738 }
00739 
00750 const char *u_config_get_value (u_config_t *c)
00751 {
00752     dbg_return_if (c == NULL, NULL);
00753 
00754     return c->value;
00755 }
00756 
00767 const char *u_config_get_subkey_value (u_config_t *c, const char *subkey)
00768 {
00769     u_config_t *skey;
00770 
00771     nop_err_if(u_config_get_subkey(c, subkey, &skey));
00772 
00773     return u_config_get_value(skey);
00774 err:
00775     return NULL;
00776 }
00777 
00792 int u_config_get_subkey_value_i (u_config_t *c, const char *subkey, int def, 
00793         int *out)
00794 {
00795     const char *v;
00796 
00797     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00798     {
00799         *out = def;
00800         return 0;
00801     }
00802 
00803     dbg_err_if (u_atoi(v, out));
00804 
00805     return 0;
00806 err:
00807     return ~0;
00808 }
00809 
00825 int u_config_get_subkey_value_b (u_config_t *c, const char *subkey, int def, 
00826         int *out)
00827 {
00828     const char *true_words[]  = { "yes", "enable", "1", "on", NULL };
00829     const char *false_words[] = { "no", "disable", "0", "off", NULL };
00830     const char *v, *w;
00831     int i;
00832 
00833     if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00834     {
00835         *out = def;
00836         return 0;
00837     }
00838 
00839     for(i = 0; (w = true_words[i]) != NULL; ++i)
00840     {
00841         if(!strcasecmp(v, w))
00842         {
00843             *out = 1;
00844             return 0; /* ok */
00845         }
00846     }
00847 
00848     for(i = 0; (w = false_words[i]) != NULL; ++i)
00849     {
00850         if(!strcasecmp(v, w))
00851         {
00852             *out = 0;
00853             return 0; /* ok */
00854         }
00855     }
00856 
00857     return ~0; /* not-bool value */
00858 }
00859 
00883 int u_config_load_from_drv (const char *uri, u_config_driver_t *drv, 
00884         int overwrite, u_config_t **pc)
00885 {
00886     u_config_t *c = NULL;
00887     void *arg = NULL;
00888 
00889     dbg_err_if (uri == NULL);
00890     dbg_err_if (pc == NULL);
00891     dbg_err_if (drv == NULL);
00892 
00893     dbg_err_if (drv->gets == NULL);
00894 
00895     dbg_err_if (u_config_create(&c));
00896 
00897     if(drv->open)
00898         dbg_err_if (drv->open(uri, &arg));
00899 
00900     dbg_err_if (u_config_do_load_drv(c, drv, arg, overwrite));
00901 
00902     if(drv->close)
00903         dbg_err_if (drv->close(arg));
00904 
00905     *pc = c;
00906 
00907     return 0;
00908 err:
00909     if(arg && drv->close)
00910         drv->close(arg);
00911     if(c)
00912         u_config_free(c);
00913     return ~0;
00914 }
00915 
00926 int u_config_has_children (u_config_t *c)
00927 {
00928     return (TAILQ_FIRST(&c->children) != NULL);
00929 }
00930 
00945 int u_config_save_to_buf (u_config_t *c, char *buf, size_t size)
00946 {
00947     u_string_t *s = NULL;
00948 
00949     dbg_err_if(u_string_create(NULL, 0, &s));
00950 
00951     dbg_err_if(u_config_to_str(c, s));
00952 
00953     /* buffer too small */
00954     warn_err_if(u_string_len(s) >= size);
00955 
00956     (void) u_strlcpy(buf, u_string_c(s), size);
00957 
00958     u_string_free(s);
00959 
00960     return 0;
00961 err:
00962     if(s)
00963         u_string_free(s);
00964     return ~0;
00965 }
00966 
00981 int u_config_load_from_buf (char *buf, size_t len, u_config_t **pc)
00982 {
00983     u_config_driver_t drv = { NULL, NULL, u_config_buf_gets, NULL };
00984     struct u_config_buf_s arg = { buf, len };
00985     u_config_t *c = NULL;
00986 
00987     dbg_err_if(u_config_create(&c));
00988 
00989     dbg_err_if(u_config_do_load_drv(c, &drv, &arg, 0));
00990 
00991     *pc = c;
00992 
00993     return 0;
00994 err:
00995     if(c)
00996         u_config_free(c);
00997     return ~0;
00998 }
00999 
01014 void u_config_walk (u_config_t *c, u_config_walk_t s, void (*cb)(u_config_t *))
01015 {
01016    int i;
01017    u_config_t *cc;
01018 
01019    for (i = 0; (cc = u_config_get_child_n(c, NULL, i)) != NULL; i++)
01020    {
01021        if (s == U_CONFIG_WALK_PREORDER)
01022            cb(cc);
01023 
01024        if (u_config_has_children(cc))
01025            u_config_walk(cc, s, cb);
01026 
01027        if (s == U_CONFIG_WALK_POSTORDER)
01028            cb(cc);
01029    }
01030 }
01031 
01036 static u_config_t *u_config_get_root (u_config_t *c)
01037 {
01038     while(c->parent)
01039         c = c->parent;
01040     return c;
01041 }
01042 
01043 static int u_config_do_set_key(u_config_t *c, const char *key, const char *val,
01044         int overwrite, u_config_t **pchild)
01045 {
01046     u_config_t *child = NULL;
01047     char *p, *child_key;
01048 
01049     if((p = strchr(key, '.')) == NULL)
01050     {
01051         child = u_config_get_child(c, key);
01052         if(child == NULL || !overwrite)
01053         {   /* there's no such child, add a new child */
01054             dbg_err_if(u_config_add_child(c, key, &child));
01055         } 
01056         dbg_err_if(u_config_set_value(child, val));
01057 
01058         /* return the child we just added/modified */
01059         if(pchild)
01060             *pchild = child;
01061     } else {
01062         child_key = u_strndup(key, p-key);
01063         dbg_err_if(child_key == NULL);
01064         if((child = u_config_get_child(c, child_key)) == NULL)
01065             dbg_err_if(u_config_add_child(c, child_key, &child));
01066         U_FREE(child_key);
01067         return u_config_do_set_key(child, ++p, val, overwrite, NULL);
01068     }
01069     return 0;
01070 err:
01071     return ~0;
01072 }
01073 
01074 static int cs_getline (u_config_gets_t cb, void *arg, u_string_t *ln)
01075 {
01076     enum { BUFSZ = 1024 };
01077     char buf[BUFSZ], *p = NULL;
01078     ssize_t len;
01079 
01080     u_string_clear(ln);
01081 
01082     while((p = cb(arg, buf, BUFSZ)) != NULL)
01083     {
01084         len = strlen(buf);
01085         if(len == 0)
01086             break; /* empty line */
01087         dbg_err_if(u_string_append(ln, buf, --len));
01088         if(!u_isnl(buf[len]))
01089             continue; /* line's longer the bufsz (or eof);get next line chunk */
01090         else
01091             break;
01092     }
01093 
01094     nop_err_if(p == NULL); /* eof */
01095 
01096     return 0;
01097 err:
01098     return ~0;
01099 }
01100 
01101 static int u_config_do_load_drv (u_config_t *c, u_config_driver_t *drv, 
01102         void *arg, int overwrite)
01103 {
01104     enum { MAX_NEST_LEV = 20 };
01105     u_string_t *line = NULL, *key = NULL, *lastkey = NULL, *value = NULL;
01106     const char *ln, *p;
01107     size_t len;
01108     int lineno = 1;
01109     u_config_t *child = NULL;
01110     u_config_t *subkey;
01111 
01112     dbg_err_if(u_string_create(NULL, 0, &line));
01113     dbg_err_if(u_string_create(NULL, 0, &key));
01114     dbg_err_if(u_string_create(NULL, 0, &value));
01115     dbg_err_if(u_string_create(NULL, 0, &lastkey));
01116 
01117     dbg_err_if(drv->gets == NULL);
01118 
01119     for(; cs_getline(drv->gets, arg, line) == 0; u_string_clear(line), ++lineno)
01120     {
01121         /* remove comments if any */
01122         if((p = strchr(u_string_c(line), '#')) != NULL)
01123             dbg_err_if(u_string_set_length(line, p - u_string_c(line)));
01124 
01125         /* remove leading and trailing blanks */
01126         dbg_err_if(u_string_trim(line));
01127 
01128         ln = u_string_c(line);
01129         len = u_string_len(line);
01130 
01131         /* remove trailing nl(s) */
01132         while(len && u_isnl(ln[len-1]))
01133             u_string_set_length(line, --len);
01134 
01135         if(len == 0)
01136             continue; /* empty line */
01137 
01138         /* eat leading blanks */
01139         for(; u_isblank(*ln); ++ln);
01140 
01141         if(ln[0] == '{')
01142         {   /* group config values */
01143             if(u_string_len(lastkey) == 0)
01144                 crit_err("config error [line %d]: { not after a no-value key", 
01145                          lineno);
01146             if(!u_isblank_str(++ln))
01147                 crit_err("config error [line %d]: { or } must be the "
01148                          "only not-blank char in a line", lineno);
01149 
01150             /* modify the existing child (when overwriting) or add a new one */
01151             if(!overwrite || 
01152                (child = u_config_get_child(c, u_string_c(lastkey))) == NULL)
01153             {
01154                 dbg_err_if(u_config_add_child(c, u_string_c(lastkey), &child));
01155             }
01156 
01157             dbg_err_if(u_config_do_load_drv(child, drv, arg, overwrite));
01158 
01159             dbg_err_if(u_string_clear(lastkey));
01160 
01161             continue;
01162         } else if(ln[0] == '}') {
01163             crit_err_ifm(c->parent == NULL,"config error: unmatched '}'");
01164             if(!u_isblank_str(++ln))
01165                 dbg_err("config error [line %d]: { or } must be the "
01166                          "only not-blank char in a line", lineno);
01167             break; /* exit */
01168         }
01169 
01170         /* find the end of the key string */
01171         for(p = ln; *p && !u_isblank(*p); ++p);
01172 
01173         /* set the key */
01174         dbg_err_if(u_string_set(key, ln, p-ln));
01175 
01176         /* set the value */
01177         dbg_err_if(u_string_set(value, p, strlen(p)));
01178         dbg_err_if(u_string_trim(value));
01179 
01180         if(!strcmp(u_string_c(key), "include") ||     /* forced dependency */
01181                 !strcmp(u_string_c(key), "-include")) /* optional dependency */
01182         {
01183             crit_err_ifm(u_string_len(value) == 0, "missing include filename");
01184 
01185             /* add to the include key to the list to resolve ${} vars */
01186             dbg_err_if(u_config_do_set_key(c, u_string_c(key), 
01187                         u_string_c(value), 0, &subkey));
01188 
01189             /* load the included file */
01190             if (u_string_c(key)[0] == '-')  /* failure is not critical */
01191                 dbg_if(u_config_include(c, drv, subkey, overwrite));
01192             else                            /* failure is critical */
01193                 dbg_err_if(u_config_include(c, drv, subkey, overwrite));
01194         } 
01195 
01196         /* if the valus is empty an open bracket will follow, save the key */
01197         if(u_string_len(value) == 0)
01198         {
01199             dbg_err_if(u_string_set(lastkey, ln, p-ln));
01200             continue;
01201         }
01202 
01203         /* add to the var list */
01204         dbg_err_if(u_config_do_set_key(c, 
01205                         u_string_c(key), 
01206                         u_string_len(value) ? u_string_c(value) : NULL, 
01207                         overwrite, NULL));
01208     }
01209     
01210     u_string_free(lastkey);
01211     u_string_free(value);
01212     u_string_free(key);
01213     u_string_free(line);
01214 
01215     return 0;
01216 err:
01217     if(lastkey)
01218         u_string_free(lastkey);
01219     if(key)
01220         u_string_free(key);
01221     if(value)
01222         u_string_free(value);
01223     if(line)
01224         u_string_free(line);
01225     return ~0;
01226 }
01227 
01228 static int u_config_include (u_config_t *c, u_config_driver_t *drv, 
01229         u_config_t *inckey, int overwrite)
01230 {
01231     u_config_t *subkey;
01232     int i;
01233     const char *p, *path;
01234     void *arg = NULL;
01235     char uri[U_FILENAME_MAX + 1];
01236 
01237     dbg_err_if(c == NULL);
01238 
01239     path = u_config_get_value(inckey);
01240     dbg_err_if(path == NULL);
01241 
01242     for(i = 0; u_config_get_subkey_nth(c, "include", i, &subkey) == 0; ++i)
01243     {
01244         if(subkey != inckey && !strcmp(path, u_config_get_value(subkey)))
01245             crit_err("circular dependency error loading %s", path);
01246     }
01247 
01248     /* find the end of the key string */
01249     for(p = path; *p && u_isblank(*p); ++p);
01250 
01251     dbg_err_if(p == NULL);
01252 
01253     crit_err_ifm (drv->open == NULL,
01254         "'include' feature used but the 'open' driver callback is not defined");
01255 
01256     /* resolv the include filename */
01257     if(drv->resolv)
01258     {
01259         dbg_err_if (drv->resolv(p, uri, sizeof(uri)));
01260 
01261         p = uri;
01262     } 
01263 
01264     crit_err_ifm (drv->open(p, &arg),
01265         "unable to access input file: %s", p);
01266 
01267     dbg_err_if (u_config_do_load_drv(c, drv, arg, overwrite));
01268 
01269     if(drv->close)
01270         crit_err_ifm (drv->close(arg),
01271             "unable to close input file: %s", p);
01272     else
01273         u_warn("the 'close' driver callback is not defined, not closing...");
01274 
01275     return 0;
01276 err:
01277     if(arg && drv->close)
01278         drv->close(arg);
01279     return ~0;
01280 }
01281 
01282 /*  Delete a child config object. 'child' must be a child of 'c' */
01283 static void u_config_del_key (u_config_t *c, u_config_t *child)
01284 {
01285     TAILQ_REMOVE(&c->children, child, np);
01286 }
01287 
01288 static int u_config_to_str (u_config_t *c, u_string_t *s)
01289 {
01290     u_config_t *item;
01291 
01292     dbg_err_if(c == NULL);
01293     dbg_err_if(s == NULL);
01294 
01295     if(c->key && strcmp(c->key, "include"))
01296         u_string_aprintf(s, "%s %s\n", c->key, (c->value ? c->value : ""));
01297     
01298     if(u_config_has_children(c))
01299     {
01300         if(c->parent)
01301             u_string_aprintf(s, "%s", "{\n");
01302 
01303         TAILQ_FOREACH(item, &c->children, np)
01304             u_config_to_str(item, s);
01305 
01306         if(c->parent)
01307             u_string_aprintf(s, "%s", "}\n");
01308     }
01309 
01310     return 0;
01311 err:
01312     return ~0;
01313 }
01314 
01315 static char *u_config_buf_gets (void *arg, char *buf, size_t size)
01316 {
01317     struct u_config_buf_s *g = (struct u_config_buf_s*)arg;
01318     char c, *s = buf;
01319 
01320     dbg_err_if(arg == NULL);
01321     dbg_err_if(buf == NULL);
01322     dbg_err_if(size == 0);
01323 
01324     if(g->len == 0 || g->data[0] == '\0')
01325         return NULL;
01326 
01327     size--; /* save a char for the trailing \0 */
01328 
01329     for(; size && g->len; --size)
01330     {
01331         c = *g->data;
01332 
01333         g->data++, g->len--;
01334 
01335         *s++ = c;
01336 
01337         if(c == '\n')
01338             break;
01339     }
01340     *s = '\0';
01341 
01342     /* dbg("ln: [%s]", buf); */
01343 
01344     return buf;
01345 err:
01346     return NULL;
01347 }

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