Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvconf.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *
00005  * Implementation of the WvConfigFile class.
00006  *
00007  * Created:     Sept 12 1997            D. Coombs
00008  *
00009  */
00010 #include "wvconf.h"
00011 #include "wvfile.h"
00012 #include "wvstringtable.h"
00013 #include <string.h>
00014 #include <sys/stat.h>
00015 
00016 
00017 void WvConf::setbool(void *userdata,
00018                      const WvString &, const WvString &,
00019                      const WvString &, const WvString &)
00020 {
00021     *(bool *)userdata = true;
00022 }
00023                      
00024 
00025 
00026 WvConf::WvConf(const WvString &_filename, int _create_mode)
00027         : filename(_filename), log(filename), globalsection("")
00028 {
00029     create_mode = _create_mode;
00030     filename.unique();
00031     dirty = error = loaded_once = false;
00032     load_file();
00033 }
00034 
00035 
00036 static int check_for_bool_string(const char *s)
00037 {
00038     if (strcasecmp(s, "off") == 0
00039      || strcasecmp(s, "false") == 0
00040      || strncasecmp(s, "no", 2) == 0)   // also handles "none"
00041         return (0);
00042 
00043     if (strcasecmp(s, "on") == 0
00044      || strcasecmp(s, "true") == 0
00045      || strcasecmp(s, "yes") == 0)
00046         return (1);
00047 
00048     // not a special bool case, so just return the number
00049     return (atoi(s));
00050 }
00051 
00052 
00053 // This "int" version of get is smart enough to interpret words like on/off,
00054 // true/false, and yes/no.
00055 int WvConf::getint(const WvString &section, const WvString &entry, int def_val)
00056 {
00057     WvString def_str(def_val);
00058     return check_for_bool_string(get(section, entry, def_str));
00059 }
00060 
00061 
00062 // This "int" version of fuzzy_get is smart enough to interpret words like 
00063 // on/off, true/false, and yes/no.
00064 int WvConf::fuzzy_getint(WvStringList &section, WvStringList &entry,
00065                          int def_val)
00066 {
00067     WvString def_str(def_val);
00068     return check_for_bool_string(fuzzy_get(section, entry, def_str));
00069 }
00070 
00071 
00072 // This "int" version of fuzzy_get is smart enough to interpret words like 
00073 // on/off, true/false, and yes/no.
00074 int WvConf::fuzzy_getint(WvStringList &section, const WvString &entry,
00075                          int def_val)
00076 {
00077     WvString def_str(def_val);
00078     return check_for_bool_string(fuzzy_get(section, entry, def_str));
00079 }
00080 
00081 
00082 void WvConf::setint(const WvString &section, const WvString &entry, int value)
00083 {
00084     WvString def_str(value);
00085     set(section, entry, def_str);
00086 }
00087 
00088 
00089 // only set the value if it isn't already in the config file
00090 void WvConf::maybesetint(const WvString &section, const WvString &entry,
00091                          int value)
00092 {
00093     if (!get(section, entry, NULL))
00094         setint(section, entry, value);
00095 }
00096 
00097  
00098 void WvConf::load_file(const WvString &filename)
00099 {
00100     WvFile file;
00101     char *p;
00102     char *from_file;
00103     WvConfigSection *sect = &globalsection;
00104     bool quick_mode = false;
00105 
00106     file.open(filename, O_RDONLY);
00107     if (!file.isok())
00108     {
00109         // Could not open for read.
00110         log(loaded_once ? WvLog::Debug1 : WvLog::Warning,
00111             "Can't read config file %s: %s\n", filename, file.errstr());
00112         if (file.geterr() != ENOENT && !loaded_once)
00113             error = true;
00114         return;
00115     }
00116 
00117     while ((from_file = trim_string(file.getline(0))) != NULL)
00118     {
00119 
00120         if ((p = parse_section(from_file)) != NULL)
00121         {
00122             quick_mode = false;
00123             
00124             // a new section?
00125             if (!p[0])          // blank name: global section
00126                 sect = &globalsection;
00127             else
00128             {
00129                 sect = (*this)[p];
00130                 if (!sect)
00131                 {
00132                     sect = new WvConfigSection(p);
00133                     append(sect, true);
00134                     quick_mode = true;
00135                 }
00136             }
00137         }
00138         else
00139         {
00140             // it must be an element for the current section *sect.
00141             p = parse_value(from_file);
00142             if (!p)
00143                 p = "";         // allow empty entries
00144 
00145             from_file = trim_string(from_file);
00146             if (from_file[0])   // nonblank option name
00147             {
00148                 if (quick_mode)
00149                     sect->quick_set(from_file, p);
00150                 else
00151                     sect->set(from_file, p);
00152             }
00153         }
00154     }
00155     
00156     run_all_callbacks();
00157 
00158     loaded_once = true;
00159 }
00160 
00161 
00162 WvConf::~WvConf()
00163 {
00164     // We don't really have to do anything here.  sections's destructor
00165     // will go through and delete all its entries, so we should be fine.
00166 
00167     flush();
00168 }
00169 
00170 
00171 const char *WvConf::get(const WvString &section, const WvString &entry,
00172                         const char *def_val)
00173 {
00174     WvStringTable cache(5);
00175     WvConfigSection *s;
00176     
00177     for(s = (*this)[section];
00178         s && !cache[s->name];
00179         s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
00180     {
00181         const char *ret = s->get(entry);
00182         if (ret) return ret;
00183         cache.add(&s->name, false);
00184     }
00185 
00186     return globalsection.get(entry, def_val);
00187 }
00188 
00189 
00190 const char *WvConf::fuzzy_get(WvStringList &sections, WvStringList &entries,
00191                               const char *def_val)
00192 {
00193     WvStringList::Iter i(sections), i2(entries);
00194     WvStringTable cache(5);
00195     WvConfigSection *s;
00196 
00197     for (i.rewind(); i.next(); )
00198     {
00199         for (i2.rewind(); i2.next();)
00200         {
00201             for(s = (*this)[i];
00202                 s && !cache[s->name];
00203                 s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
00204             {
00205                 const char *ret = s->get(i2);
00206                 if (ret) return ret;
00207                 cache.add(&s->name, false);
00208             }
00209         }
00210     }
00211     
00212     return def_val;
00213 }
00214 
00215 
00216 const char *WvConf::fuzzy_get(WvStringList &sections, const WvString &entry,
00217                               const char *def_val)
00218 {
00219     WvStringList::Iter i(sections);
00220     WvStringTable cache(5);
00221     WvConfigSection *s;
00222 
00223     for (i.rewind(); i.next(); )
00224     {
00225         for(s = (*this)[i];
00226             s && !cache[s->name];
00227             s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
00228         {
00229             const char *ret = s->get(entry);
00230             if (ret) return ret;
00231             cache.add(&s->name, false);
00232         }
00233     }
00234 
00235     return def_val;
00236 }
00237 
00238 
00239 void WvConf::set(const WvString &section, const WvString &entry,
00240                  const char *value)
00241 {
00242     WvConfigSection *s = (*this)[section];
00243     
00244     // section does not exist yet
00245     if (!s)
00246     {
00247         if (!value || !value[0])
00248             return; // no section, no entry, no problem!
00249         
00250         s = new WvConfigSection(section);
00251         append(s, true);
00252     }
00253     
00254     const char *oldval = s->get(entry, "");
00255     if (!value) value = "";
00256     if (strcmp(oldval, value)) // case sensitive
00257     {
00258         run_callbacks(section, entry, oldval, value);
00259 
00260         /* fprintf(stderr, "cfg.set: set [%s]%s = %s\n",
00261                 (const char *)section, (const char *)entry,
00262                 (const char *)value ?: "!!!"); */
00263     }
00264     
00265     s->set(entry, value);
00266     dirty = true;
00267 }
00268 
00269 
00270 // only set the value if it isn't already in the config file
00271 void WvConf::maybeset(const WvString &section, const WvString &entry,
00272                       const char *value)
00273 {
00274     if (value && !get(section, entry, NULL))
00275         set(section, entry, value);
00276 }
00277 
00278 
00279 WvConfigSection *WvConf::operator[] (const WvString &section)
00280 {
00281     Iter i(*this);
00282 
00283     // otherwise, search the whole list.
00284     for (i.rewind(); i.next(); )
00285     {
00286         if (strcasecmp(i().name, section) == 0)
00287             return &i();
00288     }
00289 
00290     return NULL;
00291 }
00292 
00293 
00294 void WvConf::delete_section(const WvString &section)
00295 {
00296     WvConfigSection *s = (*this)[section];
00297     if (s)
00298         unlink(s);
00299     dirty = true;
00300 }
00301 
00302 
00303 char *WvConf::parse_section(char *s)
00304 {
00305     char *q;
00306 
00307     if (s[0] != '[')
00308         return (NULL);
00309 
00310     q = strchr(s, ']');
00311     if (!q || q[1])
00312         return (NULL);
00313 
00314     *q = 0;
00315     return trim_string(s + 1);
00316 }
00317 
00318 
00319 char *WvConf::parse_value(char *s)
00320 {
00321     char *q;
00322 
00323     q = strchr(s, '=');
00324     if (q == NULL)
00325         return (NULL);
00326 
00327     *q++ = 0;                   // 's' points to option name, 'q' points to value
00328 
00329     return (trim_string(q));
00330 }
00331 
00332 
00333 static WvString follow_links(WvString fname)
00334 {
00335     struct stat st;
00336     WvString cwd;
00337     WvString tmp, tmpdir;
00338     char *cptr;
00339     int linksize;
00340     
00341     cwd.setsize(10240);
00342     getcwd(cwd.edit(), 10240-5);
00343     
00344     if (fname[0] != '/')
00345         fname = WvString("%s/%s", cwd, fname);
00346     
00347     if (lstat(fname, &st))
00348         return fname; // nonexistent file or something... stop here.
00349         
00350     for (;;)
00351     {
00352         // fprintf(stderr, "follow_links: trying '%s'\n", (const char*)fname);
00353         
00354         // not a symlink - done
00355         if (!S_ISLNK(st.st_mode))
00356             return fname;
00357         
00358         // read the link data into tmp
00359         tmp.setsize(st.st_size + 2);
00360         cptr = tmp.edit();
00361         linksize = readlink(fname, cptr, st.st_size + 2);
00362         if (linksize < 1)
00363             return fname; // ugly, but not sure what else to do...
00364         cptr[st.st_size] = 0;
00365         cptr[linksize] = 0;
00366         
00367         // not an absolute link - need to merge current path and new one
00368         if (cptr[0] != '/')
00369         {
00370             // need to copy the current directory name from fname
00371             tmpdir = fname;
00372             cptr = tmpdir.edit();
00373             cptr = strrchr(cptr, '/');
00374             if (cptr)
00375                 *cptr++ = 0;
00376             
00377             WvString x(tmp);
00378             tmp = WvString("%s/%s", tmpdir, x);
00379         }
00380         
00381         if (lstat(tmp, &st))
00382             return fname; // can't read target file... don't use it!
00383         
00384         fname = tmp;
00385     }
00386     
00387     return tmp;
00388 }
00389 
00390 void WvConf::save(const WvString &_filename)
00391 {
00392     if (error || !_filename)
00393         return;
00394     
00395     WvString xfilename(follow_links(_filename));
00396     
00397     // temporary filename has the last char changed to '!' (or '#' if it's
00398     // already '#').  We can't just append a character, because that might
00399     // confuse a dos filesystem.
00400     WvString tmpfilename(xfilename);
00401     char *cptr = strchr(tmpfilename.edit(), 0);
00402     cptr--;
00403     if (*cptr != '!')
00404         *cptr = '!';
00405     else
00406         *cptr = '#';
00407     
00408     ::unlink(tmpfilename);
00409     WvFile fp(tmpfilename, O_WRONLY|O_CREAT|O_TRUNC, create_mode);
00410 
00411     if (!fp.isok())
00412     {
00413         log(xfilename==filename ? WvLog::Error : WvLog::Debug1,
00414             "Can't write to config file %s: %s\n",
00415             tmpfilename, strerror(errno));
00416         if (xfilename == filename)
00417             error = true;
00418         return;
00419     }
00420     
00421     globalsection.dump(fp);
00422     
00423     Iter i(*this);
00424     for (i.rewind(); i.next();)
00425     {
00426         WvConfigSection & sect = i;
00427         fp.print("\n[%s]\n", sect.name);
00428         sect.dump(fp);
00429     }
00430     
00431     ::unlink(xfilename);
00432     ::rename(tmpfilename, xfilename);
00433 }
00434 
00435 
00436 void WvConf::save()
00437 {
00438     save(filename);
00439 }
00440 
00441 
00442 // only save the config file if it's dirty
00443 void WvConf::flush()
00444 {
00445     if (!dirty || error)
00446         return;
00447     
00448     // save under default filename
00449     save(filename);
00450     
00451     dirty = false;
00452 }
00453 
00454 
00455 void WvConf::add_callback(WvConfCallback callback, void *userdata,
00456                           const WvString &section, const WvString &entry)
00457 {
00458     callbacks.append(new WvConfCallbackInfo(callback, userdata,
00459                                             section, entry), true);
00460 }
00461 
00462 
00463 void WvConf::del_callback(WvConfCallback callback, void *userdata,
00464                           const WvString &section, const WvString &entry)
00465 {
00466     WvConfCallbackInfoList::Iter i(callbacks);
00467     
00468     for (i.rewind(); i.next(); )
00469     {
00470         WvConfCallbackInfo &c(i);
00471         
00472         if (c.callback == callback && c.userdata == userdata
00473             && c.section == section && c.entry == entry)
00474         {
00475             i.unlink();
00476             return;
00477         }
00478     }
00479 }
00480 
00481 
00482 void WvConf::run_callbacks(const WvString &section, const WvString &entry,
00483                            const WvString &oldvalue, const WvString &newvalue)
00484 {
00485     WvConfCallbackInfoList::Iter i(callbacks);
00486     
00487     for (i.rewind(); i.next(); )
00488     {
00489         if (!i->section || !strcasecmp(i->section, section))
00490         {
00491             if (!i->entry || !strcasecmp(i->entry, entry))
00492                 i->callback(i->userdata, section, entry,
00493                             oldvalue, newvalue);
00494         }
00495     }
00496 }
00497 
00498 
00499 void WvConf::run_all_callbacks()
00500 {
00501     WvConfCallbackInfoList::Iter i(callbacks);
00502 
00503     for (i.rewind(); i.next(); )
00504         i->callback(i->userdata, "", "", "", "");
00505 }

Generated on Sat Aug 24 23:07:54 2002 for WvStreams by doxygen1.2.15