00001
00002
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;
00028 char *key;
00029 char *value;
00030 u_config_list_t children;
00031 u_config_t *parent;
00032 };
00033
00034
00035 struct u_config_buf_s
00036 {
00037 char *data;
00038 size_t len;
00039 };
00040
00041
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
00188 count = 0;
00189 TAILQ_FOREACH(child, &c->children, np)
00190 count++;
00191
00192
00193 children = u_zalloc(count * sizeof(u_config_t *));
00194 dbg_err_if(children == NULL);
00195
00196
00197 i = 0;
00198 TAILQ_FOREACH(child, &c->children, np)
00199 children[i++] = child;
00200
00201
00202 qsort(children, count, sizeof(u_config_t *),
00203 (int(*)(const void *, const void *)) config_cmp);
00204
00205
00206 while((child = TAILQ_FIRST(&c->children)) != NULL)
00207 TAILQ_REMOVE(&c->children, child, np);
00208
00209
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
00244
00245
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
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;
00347 }
00348
00349 return NULL;
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;
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
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
00465 vs = ve = val;
00466 for(; vs && *vs && (p = strstr(vs, "${")) != NULL; vs = ++ve)
00467 {
00468 dbg_err_if(u_string_append(value, vs, p-vs));
00469
00470
00471 vs = p+2;
00472
00473
00474 ve = strchr(vs, '}');
00475 dbg_err_if(ve == NULL);
00476
00477
00478 dbg_err_if(u_string_set(var, vs, ve-vs));
00479
00480
00481
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
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);
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 , 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 , 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
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)
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
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
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;
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;
00854 }
00855 }
00856
00857 return ~0;
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
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 {
01054 dbg_err_if(u_config_add_child(c, key, &child));
01055 }
01056 dbg_err_if(u_config_set_value(child, val));
01057
01058
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;
01087 dbg_err_if(u_string_append(ln, buf, --len));
01088 if(!u_isnl(buf[len]))
01089 continue;
01090 else
01091 break;
01092 }
01093
01094 nop_err_if(p == NULL);
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
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
01126 dbg_err_if(u_string_trim(line));
01127
01128 ln = u_string_c(line);
01129 len = u_string_len(line);
01130
01131
01132 while(len && u_isnl(ln[len-1]))
01133 u_string_set_length(line, --len);
01134
01135 if(len == 0)
01136 continue;
01137
01138
01139 for(; u_isblank(*ln); ++ln);
01140
01141 if(ln[0] == '{')
01142 {
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
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;
01168 }
01169
01170
01171 for(p = ln; *p && !u_isblank(*p); ++p);
01172
01173
01174 dbg_err_if(u_string_set(key, ln, p-ln));
01175
01176
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") ||
01181 !strcmp(u_string_c(key), "-include"))
01182 {
01183 crit_err_ifm(u_string_len(value) == 0, "missing include filename");
01184
01185
01186 dbg_err_if(u_config_do_set_key(c, u_string_c(key),
01187 u_string_c(value), 0, &subkey));
01188
01189
01190 if (u_string_c(key)[0] == '-')
01191 dbg_if(u_config_include(c, drv, subkey, overwrite));
01192 else
01193 dbg_err_if(u_config_include(c, drv, subkey, overwrite));
01194 }
01195
01196
01197 if(u_string_len(value) == 0)
01198 {
01199 dbg_err_if(u_string_set(lastkey, ln, p-ln));
01200 continue;
01201 }
01202
01203
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
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
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
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--;
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
01343
01344 return buf;
01345 err:
01346 return NULL;
01347 }