00001
00002
00003
00004
00005
00006
00007
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)
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
00049 return (atoi(s));
00050 }
00051
00052
00053
00054
00055 int WvConf::getint(const WvString §ion, 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
00063
00064 int WvConf::fuzzy_getint(WvStringList §ion, 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
00073
00074 int WvConf::fuzzy_getint(WvStringList §ion, 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 §ion, const WvString &entry, int value)
00083 {
00084 WvString def_str(value);
00085 set(section, entry, def_str);
00086 }
00087
00088
00089
00090 void WvConf::maybesetint(const WvString §ion, 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
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
00125 if (!p[0])
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
00141 p = parse_value(from_file);
00142 if (!p)
00143 p = "";
00144
00145 from_file = trim_string(from_file);
00146 if (from_file[0])
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
00165
00166
00167 flush();
00168 }
00169
00170
00171 const char *WvConf::get(const WvString §ion, 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 §ions, 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 §ions, 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 §ion, const WvString &entry,
00240 const char *value)
00241 {
00242 WvConfigSection *s = (*this)[section];
00243
00244
00245 if (!s)
00246 {
00247 if (!value || !value[0])
00248 return;
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))
00257 {
00258 run_callbacks(section, entry, oldval, value);
00259
00260
00261
00262
00263 }
00264
00265 s->set(entry, value);
00266 dirty = true;
00267 }
00268
00269
00270
00271 void WvConf::maybeset(const WvString §ion, 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 §ion)
00280 {
00281 Iter i(*this);
00282
00283
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 §ion)
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;
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;
00349
00350 for (;;)
00351 {
00352
00353
00354
00355 if (!S_ISLNK(st.st_mode))
00356 return fname;
00357
00358
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;
00364 cptr[st.st_size] = 0;
00365 cptr[linksize] = 0;
00366
00367
00368 if (cptr[0] != '/')
00369 {
00370
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;
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
00398
00399
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
00443 void WvConf::flush()
00444 {
00445 if (!dirty || error)
00446 return;
00447
00448
00449 save(filename);
00450
00451 dirty = false;
00452 }
00453
00454
00455 void WvConf::add_callback(WvConfCallback callback, void *userdata,
00456 const WvString §ion, 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 §ion, 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 §ion, 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 }