WvStreams
wvconfemu.cc
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 *&section,
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 &sect, 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 &sect, 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