WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * Basic WvConf emulation layer for UniConf. 00006 */ 00007 #include "wvconfemu.h" 00008 #include "uniinigen.h" 00009 #include "wvstringtable.h" 00010 #include "wvfile.h" 00011 #include "strutils.h" 00012 00013 //#define DEBUG_DEL_CALLBACK 1 00014 #ifdef DEBUG_DEL_CALLBACK 00015 #include <execinfo.h> 00016 #endif 00017 00018 /* 00019 * Parse the WvConf string "request"; pointers to the found section, 00020 * entry, and value fields are stored in *section, *entry, and *value 00021 * respectively, and request[] is modified. 00022 * 00023 * For example, the string: 00024 * [silly]billy=willy 00025 * is parsed into: 00026 * section="silly"; entry="billy"; value="willy"; 00027 * 00028 * Returns 0 on success, -1 if the command is missing the '[', -2 if 00029 * the string is missing a ']', or -3 if the section or entry is 00030 * blank. If a "value" is not found (ie. there is no equal sign 00031 * outside the [] brackets) this does not qualify as an error, but 00032 * *value is set to NULL. 00033 */ 00034 static int parse_wvconf_request(char *request, char *§ion, 00035 char *&entry, char *&value) 00036 { 00037 entry = value = NULL; 00038 00039 section = strchr(request, '['); 00040 if (!section) 00041 return -1; 00042 00043 section++; 00044 00045 entry = strchr(section, ']'); 00046 if (!entry) 00047 return -2; 00048 00049 *entry++ = 0; 00050 00051 value = strchr(entry, '='); 00052 if (value) 00053 { 00054 *value++ = 0; 00055 value = trim_string(value); 00056 } 00057 00058 section = trim_string(section); 00059 entry = trim_string(entry); 00060 00061 if (!*section) 00062 return -3; 00063 00064 return 0; 00065 } 00066 00067 00068 static void do_setbool(void *userdata, 00069 WvStringParm section, WvStringParm key, 00070 WvStringParm oldval, WvStringParm newval) 00071 { 00072 bool* b = static_cast<bool*>(userdata); 00073 00074 *b = true; 00075 } 00076 00077 00078 static void do_addname(void *userdata, 00079 WvStringParm section, WvStringParm key, 00080 WvStringParm oldval, WvStringParm newval) 00081 { 00082 if (!!key) 00083 (*(WvStringList *)userdata).append(new WvString(key), true); 00084 } 00085 00086 00087 WvConfigEntryEmu *WvConfigSectionEmu::operator[] (WvStringParm s) 00088 { 00089 WvConfigEntryEmu* entry = entries[s]; 00090 00091 if (uniconf[s].exists()) 00092 { 00093 if (!entry) 00094 { 00095 entry = new WvConfigEntryEmu(s, uniconf[s].getme()); 00096 entries.add(entry, true); 00097 } 00098 else 00099 entry->value = uniconf[s].getme(); 00100 } 00101 else 00102 entry = NULL; 00103 00104 return entry; 00105 } 00106 00107 00108 const char *WvConfigSectionEmu::get(WvStringParm entry, const char *def_val) 00109 { 00110 if (!entry) 00111 return def_val; 00112 00113 WvString s(uniconf[entry].getme(def_val)); 00114 00115 // look it up in the cache 00116 WvString *sp = values[s]; 00117 if (!sp) values.add(sp = new WvString(s), true); 00118 return sp->cstr(); 00119 } 00120 00121 00122 void WvConfigSectionEmu::set(WvStringParm entry, WvStringParm value) 00123 { 00124 if (!!entry) 00125 { 00126 if (!!value) 00127 uniconf[entry].setme(value); 00128 else 00129 uniconf[entry].setme(WvString::null); 00130 } 00131 } 00132 00133 00134 void WvConfigSectionEmu::quick_set(WvStringParm entry, WvStringParm value) 00135 { 00136 uniconf[entry].setme(value); 00137 } 00138 00139 00140 bool WvConfigSectionEmu::isempty() const 00141 { 00142 return !uniconf.haschildren(); 00143 } 00144 00145 00146 WvConfigSectionEmu::Iter::~Iter() 00147 { 00148 } 00149 00150 00151 void WvConfigSectionEmu::Iter::rewind() 00152 { 00153 iter.rewind(); 00154 link.data = entry = NULL; 00155 } 00156 00157 00158 WvLink *WvConfigSectionEmu::Iter::next() 00159 { 00160 while (iter.next()) 00161 { 00162 // WvConf-enabled code expects all set keys to be non-empty; 00163 // enforce this behaviour 00164 if (!!iter->getme()) 00165 { 00166 /* 00167 * FIXME: if the WvConfEmu is not at the root of the 00168 * UniConf tree, this will give an incorrect result. 00169 */ 00170 entry = sect[iter->fullkey(sect.uniconf)]; 00171 link.data = static_cast<void*>(entry); 00172 assert(entry); 00173 return &link; 00174 } 00175 } 00176 00177 return NULL; 00178 } 00179 00180 00181 WvLink *WvConfigSectionEmu::Iter::cur() 00182 { 00183 return &link; 00184 } 00185 00186 00187 WvConfigEntryEmu *WvConfigSectionEmu::Iter::ptr() const 00188 { 00189 return entry; 00190 } 00191 00192 00193 void *WvConfigSectionEmu::Iter::vptr() const 00194 { 00195 return link.data; 00196 } 00197 00198 00199 void WvConfEmu::notify(const UniConf &_uni, const UniConfKey &_key) 00200 { 00201 WvString section(_key.first()); 00202 WvString key(_key.removefirst()); 00203 00204 if (hold) 00205 return; 00206 00207 WvString value = uniconf[section][key].getme(""); 00208 00209 WvList<CallbackInfo>::Iter i(callbacks); 00210 for (i.rewind(); i.next(); ) 00211 { 00212 if ((!i->section || !strcasecmp(i->section, section)) 00213 && (!i->key || !strcasecmp(i->key, key))) 00214 { 00215 i->callback(i->userdata, section, key, WvString(), value); 00216 } 00217 } 00218 } 00219 00220 00221 WvConfEmu::WvConfEmu(const UniConf &_uniconf) 00222 : sections(42), hold(false), values(420), uniconf(_uniconf) 00223 { 00224 wvauthd = NULL; 00225 uniconf.add_callback(this, wv::bind(&WvConfEmu::notify, this, _1, _2), 00226 true); 00227 dirty = false; 00228 } 00229 00230 00231 WvConfEmu::~WvConfEmu() 00232 { 00233 // things will "work" if you don't empty the callback list before 00234 // deleting the WvConfEmu, but they probably won't work the way you 00235 // think they will. (ie. someone might be using a temporary WvConfEmu 00236 // and think his callbacks will stick around; they won't!) 00237 #ifndef DEBUG_DEL_CALLBACK 00238 assert(callbacks.isempty()); 00239 #else 00240 if (!callbacks.isempty()) 00241 { 00242 WvList<CallbackInfo>::Iter i(callbacks); 00243 00244 fprintf(stderr, " *** leftover callbacks in WvConfEmu ***\n"); 00245 for (i.rewind(); i.next(); ) 00246 { 00247 fprintf(stderr, " - [%s]%s (%p)\n", i->section.cstr(), 00248 i->key.cstr(), i->cookie); 00249 } 00250 } 00251 #endif 00252 00253 uniconf.del_callback(this); 00254 } 00255 00256 00257 void WvConfEmu::zap() 00258 { 00259 uniconf.remove(); 00260 } 00261 00262 00263 bool WvConfEmu::isclean() const 00264 { 00265 return isok() && !dirty; 00266 } 00267 00268 00269 bool WvConfEmu::isok() const 00270 { 00271 return !uniconf.isnull(); 00272 } 00273 00274 00275 void WvConfEmu::load_file(WvStringParm filename) 00276 { 00277 UniConfRoot new_uniconf(WvString("ini:%s", filename)); 00278 00279 hold = true; 00280 new_uniconf.copy(uniconf, true); 00281 hold = false; 00282 } 00283 00284 00285 void WvConfEmu::save(WvStringParm filename, int _create_mode) 00286 { 00287 UniConfRoot tmp_uniconf(new UniIniGen(filename, _create_mode), false); 00288 00289 uniconf.copy(tmp_uniconf, true); 00290 00291 tmp_uniconf.commit(); 00292 } 00293 00294 00295 void WvConfEmu::save() 00296 { 00297 uniconf.commit(); 00298 } 00299 00300 00301 void WvConfEmu::flush() 00302 { 00303 uniconf.commit(); 00304 dirty = false; 00305 } 00306 00307 00308 WvConfigSectionEmu *WvConfEmu::operator[] (WvStringParm sect) 00309 { 00310 if (UniConfKey(sect).numsegments() != 1) 00311 return NULL; 00312 00313 WvConfigSectionEmu* section = sections[sect]; 00314 00315 if (!section && uniconf[sect].exists()) 00316 { 00317 section = new WvConfigSectionEmu(uniconf[sect], sect, &values); 00318 sections.add(section, true); 00319 } 00320 00321 return section; 00322 } 00323 00324 00325 void WvConfEmu::add_callback(WvConfCallback callback, void *userdata, 00326 WvStringParm section, WvStringParm key, 00327 void *cookie) 00328 { 00329 if (!callback) 00330 return; 00331 00332 WvList<CallbackInfo>::Iter i(callbacks); 00333 for (i.rewind(); i.next(); ) 00334 { 00335 if (i->cookie == cookie 00336 && i->section == section 00337 && i->key == key) 00338 return; 00339 } 00340 00341 #ifdef DEBUG_DEL_CALLBACK 00342 void* trace[10]; 00343 int count = backtrace(trace, sizeof(trace)/sizeof(trace[0])); 00344 char** tracedump = backtrace_symbols(trace, count); 00345 fprintf(stderr, "TRACE:add:%s:%s:%p", section.cstr(), key.cstr(), cookie); 00346 for (int i = 0; i < count; ++i) 00347 fprintf(stderr, ":%s", tracedump[i]); 00348 fprintf(stderr, "\n"); 00349 free(tracedump); 00350 #endif 00351 00352 callbacks.append(new CallbackInfo(callback, userdata, section, key, 00353 cookie), 00354 true); 00355 } 00356 00357 00358 void WvConfEmu::del_callback(WvStringParm section, WvStringParm key, void *cookie) 00359 { 00360 WvList<CallbackInfo>::Iter i(callbacks); 00361 00362 assert(cookie); 00363 00364 for (i.rewind(); i.next(); ) 00365 { 00366 if (i->cookie == cookie 00367 && i->section == section 00368 && i->key == key) 00369 { 00370 #ifdef DEBUG_DEL_CALLBACK 00371 fprintf(stderr, "TRACE:del:%s:%s:%p\n", section.cstr(), key.cstr(), cookie); 00372 #endif 00373 i.xunlink(); 00374 } 00375 } 00376 } 00377 00378 00379 void WvConfEmu::add_setbool(bool *b, WvStringParm _section, WvStringParm _key) 00380 { 00381 add_callback(do_setbool, b, _section, _key, b); 00382 } 00383 00384 00385 void WvConfEmu::del_setbool(bool *b, WvStringParm _section, WvStringParm _key) 00386 { 00387 del_callback(_section, _key, b); 00388 } 00389 00390 00391 void WvConfEmu::add_addname(WvStringList *list, WvStringParm sect, WvStringParm ent) 00392 { 00393 add_callback(do_addname, list, sect, ent, list); 00394 } 00395 00396 00397 void WvConfEmu::del_addname(WvStringList *list, 00398 WvStringParm sect, WvStringParm ent) 00399 { 00400 del_callback(sect, ent, list); 00401 } 00402 00403 00404 WvString WvConfEmu::getraw(WvString wvconfstr, int &parse_error) 00405 { 00406 char *section, *entry, *value; 00407 parse_error = parse_wvconf_request(wvconfstr.edit(), 00408 section, entry, value); 00409 00410 if (parse_error) 00411 return WvString(); 00412 00413 return get(section, entry, value); 00414 } 00415 00416 00417 int WvConfEmu::getint(WvStringParm section, WvStringParm entry, int def_val) 00418 { 00419 if (!section || !entry) 00420 return def_val; 00421 00422 return uniconf[section][entry].getmeint(def_val); 00423 } 00424 00425 00426 const char *WvConfEmu::get(WvStringParm section, WvStringParm entry, 00427 const char *def_val) 00428 { 00429 if (!section || !entry) 00430 return def_val; 00431 00432 WvString s(uniconf[section][entry].getme(def_val)); 00433 00434 // look it up in the cache 00435 WvString *sp = values[s]; 00436 if (!sp) values.add(sp = new WvString(s), true); 00437 return sp->cstr(); 00438 } 00439 00440 int WvConfEmu::fuzzy_getint(WvStringList §, WvStringParm entry, 00441 int def_val) 00442 { 00443 WvString def_str(def_val); 00444 return check_for_bool_string(fuzzy_get(sect, entry, def_str)); 00445 } 00446 00447 00448 const char *WvConfEmu::fuzzy_get(WvStringList §, WvStringParm entry, 00449 const char *def_val) 00450 { 00451 WvStringList::Iter i(sect); 00452 WvStringTable cache(5); 00453 WvConfigSectionEmu *s; 00454 00455 for (i.rewind(); i.next(); ) 00456 { 00457 for(s = (*this)[*i]; 00458 s && !cache[s->name]; 00459 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL) 00460 { 00461 const char *ret = s->get(entry); 00462 if (ret) return ret; 00463 cache.add(&s->name, false); 00464 } 00465 } 00466 00467 return def_val; 00468 } 00469 00470 void WvConfEmu::setraw(WvString wvconfstr, const char *&_value, 00471 int &parse_error) 00472 { 00473 char *section, *entry, *value; 00474 parse_error = parse_wvconf_request(wvconfstr.edit(), 00475 section, entry, value); 00476 if (!parse_error) 00477 { 00478 set(section, entry, value); 00479 _value = get(section, entry, value); 00480 } 00481 else 00482 _value = NULL; 00483 } 00484 00485 00486 void WvConfEmu::setint(WvStringParm section, WvStringParm entry, int value) 00487 { 00488 if (!!entry) 00489 uniconf[section][entry].setmeint(value); 00490 } 00491 00492 00493 void WvConfEmu::set(WvStringParm section, WvStringParm entry, 00494 const char *value) 00495 { 00496 if (!!entry) 00497 { 00498 if (value && value[0] != 0) 00499 uniconf[section][entry].setme(value); 00500 else 00501 uniconf[section][entry].setme(WvString::null); 00502 dirty = true; 00503 } 00504 } 00505 00506 00507 void WvConfEmu::maybesetint(WvStringParm section, WvStringParm entry, 00508 int value) 00509 { 00510 if (!!entry && !get(section, entry, NULL)) 00511 setint(section, entry, value); 00512 } 00513 00514 00515 void WvConfEmu::maybeset(WvStringParm section, WvStringParm entry, 00516 const char *value) 00517 { 00518 if (!!entry && get(section, entry, 0) == 0) 00519 set(section, entry, value); 00520 } 00521 00522 00523 void WvConfEmu::delete_section(WvStringParm section) 00524 { 00525 uniconf[section].remove(); 00526 dirty = true; 00527 } 00528 00529 00530 int WvConfEmu::check_for_bool_string(const char *s) 00531 { 00532 if (strcasecmp(s, "off") == 0 00533 || strcasecmp(s, "false") == 0 00534 || strncasecmp(s, "no", 2) == 0) // also handles "none" 00535 return 0; 00536 00537 if (strcasecmp(s, "on") == 0 00538 || strcasecmp(s, "true") == 0 00539 || strcasecmp(s, "yes") == 0) 00540 return 1; 00541 00542 // not a special bool case, so just return the number 00543 return atoi(s); 00544 } 00545 00546 00547 void WvConfEmu::Iter::rewind() 00548 { 00549 iter.rewind(); 00550 link.data = NULL; 00551 } 00552 00553 00554 WvLink *WvConfEmu::Iter::next() 00555 { 00556 link.data = NULL; 00557 while (link.data == NULL && iter.next()) 00558 { 00559 link.data = static_cast<void*>(conf[iter->key()]); 00560 } 00561 if (link.data) 00562 { 00563 return &link; 00564 } 00565 return NULL; 00566 } 00567 00568 00569 WvConfigSectionEmu *WvConfEmu::Iter::ptr() const 00570 { 00571 return conf[iter->key()]; 00572 } 00573