00001
00002
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;
00040 char *key;
00041 char *value;
00042 u_config_list_t children;
00043 u_config_t *parent;
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
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;
00100 }
00101
00102 return NULL;
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;
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
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
00172 vs = ve = val;
00173 for(; vs && *vs && (p = strstr(vs, "${")) != NULL; vs = ++ve)
00174 {
00175 dbg_err_if(u_string_append(value, vs, p-vs));
00176
00177
00178 vs = p+2;
00179
00180
00181 ve = strchr(vs, '}');
00182 dbg_err_if(ve == NULL);
00183
00184
00185 dbg_err_if(u_string_set(var, vs, ve-vs));
00186
00187
00188
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
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);
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 {
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 );
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 );
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;
00270 dbg_err_if(u_string_append(ln, buf, --len));
00271 if(!u_isnl(buf[len]))
00272 continue;
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
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
00306 dbg_err_if(u_string_trim(line));
00307
00308 ln = u_string_c(line);
00309 len = u_string_len(line);
00310
00311
00312 while(len && u_isnl(ln[len-1]))
00313 u_string_set_length(line, --len);
00314
00315 if(len == 0)
00316 continue;
00317
00318
00319 for(; u_isblank(*ln); ++ln);
00320
00321 if(ln[0] == '{')
00322 {
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;
00340 }
00341
00342
00343 for(p = ln; *p && !u_isblank(*p); ++p);
00344
00345
00346 dbg_err_if(u_string_set(key, ln, p-ln));
00347
00348
00349 dbg_err_if(u_string_set(value, p, strlen(p)));
00350 dbg_err_if(u_string_trim(value));
00351
00352
00353 if(u_string_len(value) == 0)
00354 {
00355 dbg_err_if(u_string_set(lastkey, ln, p-ln));
00356 continue;
00357 }
00358
00359
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
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
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
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');
00626
00627
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;
00668 }
00669 }
00670
00671 for(w = *false_words; *w; ++w)
00672 {
00673 if(!strcasecmp(v, w))
00674 {
00675 *out = 0;
00676 return 0;
00677 }
00678 }
00679
00680 return ~0;
00681 }
00682