00001
00002
00003
00004
00005 static const char rcsid[] =
00006 "$Id: config.c,v 1.3 2007/02/12 08:32:27 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
00331 if(!overwrite ||
00332 (child = u_config_get_child(c, u_string_c(lastkey))) == NULL)
00333 {
00334 dbg_err_if(u_config_add_child(c, u_string_c(lastkey), &child));
00335 }
00336
00337 dbg_err_if(u_config_do_load(child, cb, arg, overwrite));
00338 dbg_err_if(u_string_clear(lastkey));
00339 continue;
00340 } else if(ln[0] == '}') {
00341 warn_err_ifm(c->parent == NULL,"config error: unmatched '}'");
00342 if(!u_isblank_str(++ln))
00343 warn_err("config error [line %d]: { or } must be the "
00344 "only not-blank char in a line", lineno);
00345 break;
00346 }
00347
00348
00349 for(p = ln; *p && !u_isblank(*p); ++p);
00350
00351
00352 dbg_err_if(u_string_set(key, ln, p-ln));
00353
00354
00355 dbg_err_if(u_string_set(value, p, strlen(p)));
00356 dbg_err_if(u_string_trim(value));
00357
00358
00359 if(u_string_len(value) == 0)
00360 {
00361 dbg_err_if(u_string_set(lastkey, ln, p-ln));
00362 continue;
00363 }
00364
00365
00366 dbg_err_if(u_config_do_set_key(c,
00367 u_string_c(key),
00368 u_string_len(value) ? u_string_c(value) : NULL,
00369 overwrite));
00370 }
00371
00372 u_string_free(lastkey);
00373 u_string_free(value);
00374 u_string_free(key);
00375 u_string_free(line);
00376
00377 return 0;
00378 err:
00379 if(lastkey)
00380 u_string_free(lastkey);
00381 if(key)
00382 u_string_free(key);
00383 if(value)
00384 u_string_free(value);
00385 if(line)
00386 u_string_free(line);
00387 return ~0;
00388 }
00389
00390 int u_config_load_from(u_config_t *c, u_config_gets_t cb,
00391 void *arg, int overwrite)
00392 {
00393 dbg_err_if(u_config_do_load(c, cb, arg, overwrite));
00394
00395 return 0;
00396 err:
00397 return ~0;
00398 }
00399
00400 static char *u_config_fgetline(void* arg, char * buf, size_t size)
00401 {
00402 FILE *f = (FILE*)arg;
00403
00404 return fgets(buf, size, f);
00405 }
00406
00422 int u_config_load(u_config_t *c, int fd, int overwrite)
00423 {
00424 FILE *file;
00425
00426
00427 file = fdopen(dup(fd), "r");
00428 dbg_err_if(file == NULL);
00429
00430 dbg_err_if(u_config_do_load(c, u_config_fgetline, file, overwrite));
00431
00432 fclose(file);
00433
00434 return 0;
00435 err:
00436 U_FCLOSE(file);
00437 return ~0;
00438 }
00439
00450 int u_config_load_from_file (const char *file, u_config_t **pc)
00451 {
00452 int fd = -1;
00453 u_config_t *c = NULL;
00454
00455 dbg_return_if (file == NULL, ~0);
00456 dbg_return_if (pc == NULL, ~0);
00457
00458 warn_err_if (u_config_create(&c));
00459 warn_err_if ((fd = open(file, O_RDONLY)) == -1);
00460 warn_err_if (u_config_load(c, fd, 0));
00461
00462 (void) close(fd);
00463
00464 *pc = c;
00465
00466 return 0;
00467 err:
00468 (void) u_config_free(c);
00469 U_CLOSE(fd);
00470 return ~0;
00471 }
00472
00483 int u_config_create(u_config_t **pc)
00484 {
00485 u_config_t *c = NULL;
00486
00487 c = u_zalloc(sizeof(u_config_t));
00488 dbg_err_if(c == NULL);
00489
00490 TAILQ_INIT(&c->children);
00491
00492 *pc = c;
00493
00494 return 0;
00495 err:
00496 if(c)
00497 u_config_free(c);
00498 return ~0;
00499 }
00500
00510 static void u_config_del_key(u_config_t *c, u_config_t *child)
00511 {
00512 TAILQ_REMOVE(&c->children, child, np);
00513 }
00514
00524 int u_config_free(u_config_t *c)
00525 {
00526 u_config_t *child = NULL;
00527 if(c)
00528 {
00529
00530 while((child = TAILQ_FIRST(&c->children)) != NULL)
00531 {
00532 u_config_del_key(c, child);
00533 dbg_err_if(u_config_free(child));
00534 }
00535
00536 if(c->key)
00537 U_FREE(c->key);
00538 if(c->value)
00539 U_FREE(c->value);
00540 U_FREE(c);
00541 }
00542 return 0;
00543 err:
00544 return ~0;
00545 }
00546
00556 const char* u_config_get_key(u_config_t *c)
00557 {
00558 dbg_err_if(!c);
00559
00560 return c->key;
00561 err:
00562 return NULL;
00563 }
00564
00574 const char* u_config_get_value(u_config_t *c)
00575 {
00576 dbg_err_if(!c);
00577
00578 return c->value;
00579 err:
00580 return NULL;
00581 }
00582
00593 const char* u_config_get_subkey_value(u_config_t *c, const char *subkey)
00594 {
00595 u_config_t *skey;
00596
00597 nop_err_if(u_config_get_subkey(c, subkey, &skey));
00598
00599 return u_config_get_value(skey);
00600 err:
00601 return NULL;
00602 }
00603
00617 int u_config_get_subkey_value_i(u_config_t *c, const char *subkey, int def,
00618 int *out)
00619 {
00620 const char *v;
00621 char *ep = NULL;
00622 long l;
00623
00624 if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00625 {
00626 *out = def;
00627 return 0;
00628 }
00629
00630 l = strtol(v, &ep, 0);
00631 dbg_err_if(*ep != '\0');
00632
00633
00634 *out = (int)l;
00635
00636 return 0;
00637 err:
00638 return ~0;
00639 }
00640
00655 int u_config_get_subkey_value_b(u_config_t *c, const char *subkey, int def,
00656 int *out)
00657 {
00658 const char *true_words[] = { "yes", "enable", "1", "on", NULL };
00659 const char *false_words[] = { "no", "disable", "0", "off", NULL };
00660 const char *v, *w;
00661
00662 if((v = u_config_get_subkey_value(c, subkey)) == NULL)
00663 {
00664 *out = def;
00665 return 0;
00666 }
00667
00668 for(w = *true_words; *w; ++w)
00669 {
00670 if(!strcasecmp(v, w))
00671 {
00672 *out = 1;
00673 return 0;
00674 }
00675 }
00676
00677 for(w = *false_words; *w; ++w)
00678 {
00679 if(!strcasecmp(v, w))
00680 {
00681 *out = 0;
00682 return 0;
00683 }
00684 }
00685
00686 return ~0;
00687 }
00688