WvStreams
wvconfemu.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * Basic WvConf emulation layer for UniConf.
6  */
7 #include "wvconfemu.h"
8 #include "uniinigen.h"
9 #include "wvstringtable.h"
10 #include "wvfile.h"
11 #include "strutils.h"
12 
13 //#define DEBUG_DEL_CALLBACK 1
14 #ifdef DEBUG_DEL_CALLBACK
15 #include <execinfo.h>
16 #endif
17 
18 /*
19  * Parse the WvConf string "request"; pointers to the found section,
20  * entry, and value fields are stored in *section, *entry, and *value
21  * respectively, and request[] is modified.
22  *
23  * For example, the string:
24  * [silly]billy=willy
25  * is parsed into:
26  * section="silly"; entry="billy"; value="willy";
27  *
28  * Returns 0 on success, -1 if the command is missing the '[', -2 if
29  * the string is missing a ']', or -3 if the section or entry is
30  * blank. If a "value" is not found (ie. there is no equal sign
31  * outside the [] brackets) this does not qualify as an error, but
32  * *value is set to NULL.
33 */
34 static int parse_wvconf_request(char *request, char *&section,
35  char *&entry, char *&value)
36 {
37  entry = value = NULL;
38 
39  section = strchr(request, '[');
40  if (!section)
41  return -1;
42 
43  section++;
44 
45  entry = strchr(section, ']');
46  if (!entry)
47  return -2;
48 
49  *entry++ = 0;
50 
51  value = strchr(entry, '=');
52  if (value)
53  {
54  *value++ = 0;
55  value = trim_string(value);
56  }
57 
58  section = trim_string(section);
59  entry = trim_string(entry);
60 
61  if (!*section)
62  return -3;
63 
64  return 0;
65 }
66 
67 
68 static void do_setbool(void *userdata,
69  WvStringParm section, WvStringParm key,
70  WvStringParm oldval, WvStringParm newval)
71 {
72  bool* b = static_cast<bool*>(userdata);
73 
74  *b = true;
75 }
76 
77 
78 static void do_addname(void *userdata,
79  WvStringParm section, WvStringParm key,
80  WvStringParm oldval, WvStringParm newval)
81 {
82  if (!!key)
83  (*(WvStringList *)userdata).append(new WvString(key), true);
84 }
85 
86 
87 WvConfigEntryEmu *WvConfigSectionEmu::operator[] (WvStringParm s)
88 {
89  WvConfigEntryEmu* entry = entries[s];
90 
91  if (uniconf[s].exists())
92  {
93  if (!entry)
94  {
95  entry = new WvConfigEntryEmu(s, uniconf[s].getme());
96  entries.add(entry, true);
97  }
98  else
99  entry->value = uniconf[s].getme();
100  }
101  else
102  entry = NULL;
103 
104  return entry;
105 }
106 
107 
108 const char *WvConfigSectionEmu::get(WvStringParm entry, const char *def_val)
109 {
110  if (!entry)
111  return def_val;
112 
113  WvString s(uniconf[entry].getme(def_val));
114 
115  // look it up in the cache
116  WvString *sp = values[s];
117  if (!sp) values.add(sp = new WvString(s), true);
118  return sp->cstr();
119 }
120 
121 
122 void WvConfigSectionEmu::set(WvStringParm entry, WvStringParm value)
123 {
124  if (!!entry)
125  {
126  if (!!value)
127  uniconf[entry].setme(value);
128  else
129  uniconf[entry].setme(WvString::null);
130  }
131 }
132 
133 
134 void WvConfigSectionEmu::quick_set(WvStringParm entry, WvStringParm value)
135 {
136  uniconf[entry].setme(value);
137 }
138 
139 
140 bool WvConfigSectionEmu::isempty() const
141 {
142  return !uniconf.haschildren();
143 }
144 
145 
146 WvConfigSectionEmu::Iter::~Iter()
147 {
148 }
149 
150 
151 void WvConfigSectionEmu::Iter::rewind()
152 {
153  iter.rewind();
154  link.data = entry = NULL;
155 }
156 
157 
158 WvLink *WvConfigSectionEmu::Iter::next()
159 {
160  while (iter.next())
161  {
162  // WvConf-enabled code expects all set keys to be non-empty;
163  // enforce this behaviour
164  if (!!iter->getme())
165  {
166  /*
167  * FIXME: if the WvConfEmu is not at the root of the
168  * UniConf tree, this will give an incorrect result.
169  */
170  entry = sect[iter->fullkey(sect.uniconf)];
171  link.data = static_cast<void*>(entry);
172  assert(entry);
173  return &link;
174  }
175  }
176 
177  return NULL;
178 }
179 
180 
181 WvLink *WvConfigSectionEmu::Iter::cur()
182 {
183  return &link;
184 }
185 
186 
187 WvConfigEntryEmu *WvConfigSectionEmu::Iter::ptr() const
188 {
189  return entry;
190 }
191 
192 
193 void *WvConfigSectionEmu::Iter::vptr() const
194 {
195  return link.data;
196 }
197 
198 
199 void WvConfEmu::notify(const UniConf &_uni, const UniConfKey &_key)
200 {
201  WvString section(_key.first());
202  WvString key(_key.removefirst());
203 
204  if (hold)
205  return;
206 
207  WvString value = uniconf[section][key].getme("");
208 
209  WvList<CallbackInfo>::Iter i(callbacks);
210  for (i.rewind(); i.next(); )
211  {
212  if ((!i->section || !strcasecmp(i->section, section))
213  && (!i->key || !strcasecmp(i->key, key)))
214  {
215  i->callback(i->userdata, section, key, WvString(), value);
216  }
217  }
218 }
219 
220 
221 WvConfEmu::WvConfEmu(const UniConf &_uniconf)
222  : sections(42), hold(false), values(420), uniconf(_uniconf)
223 {
224  wvauthd = NULL;
225  uniconf.add_callback(this, wv::bind(&WvConfEmu::notify, this, _1, _2),
226  true);
227  dirty = false;
228 }
229 
230 
231 WvConfEmu::~WvConfEmu()
232 {
233  // things will "work" if you don't empty the callback list before
234  // deleting the WvConfEmu, but they probably won't work the way you
235  // think they will. (ie. someone might be using a temporary WvConfEmu
236  // and think his callbacks will stick around; they won't!)
237 #ifndef DEBUG_DEL_CALLBACK
238  assert(callbacks.isempty());
239 #else
240  if (!callbacks.isempty())
241  {
242  WvList<CallbackInfo>::Iter i(callbacks);
243 
244  fprintf(stderr, " *** leftover callbacks in WvConfEmu ***\n");
245  for (i.rewind(); i.next(); )
246  {
247  fprintf(stderr, " - [%s]%s (%p)\n", i->section.cstr(),
248  i->key.cstr(), i->cookie);
249  }
250  }
251 #endif
252 
253  uniconf.del_callback(this);
254 }
255 
256 
257 void WvConfEmu::zap()
258 {
259  uniconf.remove();
260 }
261 
262 
263 bool WvConfEmu::isclean() const
264 {
265  return isok() && !dirty;
266 }
267 
268 
269 bool WvConfEmu::isok() const
270 {
271  return !uniconf.isnull();
272 }
273 
274 
275 void WvConfEmu::load_file(WvStringParm filename)
276 {
277  UniConfRoot new_uniconf(WvString("ini:%s", filename));
278 
279  hold = true;
280  new_uniconf.copy(uniconf, true);
281  hold = false;
282 }
283 
284 
285 void WvConfEmu::save(WvStringParm filename, int _create_mode)
286 {
287  UniConfRoot tmp_uniconf(new UniIniGen(filename, _create_mode), false);
288 
289  uniconf.copy(tmp_uniconf, true);
290 
291  tmp_uniconf.commit();
292 }
293 
294 
295 void WvConfEmu::save()
296 {
297  uniconf.commit();
298 }
299 
300 
301 void WvConfEmu::flush()
302 {
303  uniconf.commit();
304  dirty = false;
305 }
306 
307 
308 WvConfigSectionEmu *WvConfEmu::operator[] (WvStringParm sect)
309 {
310  if (UniConfKey(sect).numsegments() != 1)
311  return NULL;
312 
313  WvConfigSectionEmu* section = sections[sect];
314 
315  if (!section && uniconf[sect].exists())
316  {
317  section = new WvConfigSectionEmu(uniconf[sect], sect, &values);
318  sections.add(section, true);
319  }
320 
321  return section;
322 }
323 
324 
325 void WvConfEmu::add_callback(WvConfCallback callback, void *userdata,
326  WvStringParm section, WvStringParm key,
327  void *cookie)
328 {
329  if (!callback)
330  return;
331 
332  WvList<CallbackInfo>::Iter i(callbacks);
333  for (i.rewind(); i.next(); )
334  {
335  if (i->cookie == cookie
336  && i->section == section
337  && i->key == key)
338  return;
339  }
340 
341 #ifdef DEBUG_DEL_CALLBACK
342  void* trace[10];
343  int count = backtrace(trace, sizeof(trace)/sizeof(trace[0]));
344  char** tracedump = backtrace_symbols(trace, count);
345  fprintf(stderr, "TRACE:add:%s:%s:%p", section.cstr(), key.cstr(), cookie);
346  for (int i = 0; i < count; ++i)
347  fprintf(stderr, ":%s", tracedump[i]);
348  fprintf(stderr, "\n");
349  free(tracedump);
350 #endif
351 
352  callbacks.append(new CallbackInfo(callback, userdata, section, key,
353  cookie),
354  true);
355 }
356 
357 
358 void WvConfEmu::del_callback(WvStringParm section, WvStringParm key, void *cookie)
359 {
360  WvList<CallbackInfo>::Iter i(callbacks);
361 
362  assert(cookie);
363 
364  for (i.rewind(); i.next(); )
365  {
366  if (i->cookie == cookie
367  && i->section == section
368  && i->key == key)
369  {
370 #ifdef DEBUG_DEL_CALLBACK
371  fprintf(stderr, "TRACE:del:%s:%s:%p\n", section.cstr(), key.cstr(), cookie);
372 #endif
373  i.xunlink();
374  }
375  }
376 }
377 
378 
379 void WvConfEmu::add_setbool(bool *b, WvStringParm _section, WvStringParm _key)
380 {
381  add_callback(do_setbool, b, _section, _key, b);
382 }
383 
384 
385 void WvConfEmu::del_setbool(bool *b, WvStringParm _section, WvStringParm _key)
386 {
387  del_callback(_section, _key, b);
388 }
389 
390 
391 void WvConfEmu::add_addname(WvStringList *list, WvStringParm sect, WvStringParm ent)
392 {
393  add_callback(do_addname, list, sect, ent, list);
394 }
395 
396 
397 void WvConfEmu::del_addname(WvStringList *list,
398  WvStringParm sect, WvStringParm ent)
399 {
400  del_callback(sect, ent, list);
401 }
402 
403 
404 WvString WvConfEmu::getraw(WvString wvconfstr, int &parse_error)
405 {
406  char *section, *entry, *value;
407  parse_error = parse_wvconf_request(wvconfstr.edit(),
408  section, entry, value);
409 
410  if (parse_error)
411  return WvString();
412 
413  return get(section, entry, value);
414 }
415 
416 
417 int WvConfEmu::getint(WvStringParm section, WvStringParm entry, int def_val)
418 {
419  if (!section || !entry)
420  return def_val;
421 
422  return uniconf[section][entry].getmeint(def_val);
423 }
424 
425 
426 const char *WvConfEmu::get(WvStringParm section, WvStringParm entry,
427  const char *def_val)
428 {
429  if (!section || !entry)
430  return def_val;
431 
432  WvString s(uniconf[section][entry].getme(def_val));
433 
434  // look it up in the cache
435  WvString *sp = values[s];
436  if (!sp) values.add(sp = new WvString(s), true);
437  return sp->cstr();
438 }
439 
440 int WvConfEmu::fuzzy_getint(WvStringList &sect, WvStringParm entry,
441  int def_val)
442 {
443  WvString def_str(def_val);
444  return check_for_bool_string(fuzzy_get(sect, entry, def_str));
445 }
446 
447 
448 const char *WvConfEmu::fuzzy_get(WvStringList &sect, WvStringParm entry,
449  const char *def_val)
450 {
451  WvStringList::Iter i(sect);
452  WvStringTable cache(5);
454 
455  for (i.rewind(); i.next(); )
456  {
457  for(s = (*this)[*i];
458  s && !cache[s->name];
459  s = (*s)["Inherits"] ? (*this)[(*s)["Inherits"]->value] : NULL)
460  {
461  const char *ret = s->get(entry);
462  if (ret) return ret;
463  cache.add(&s->name, false);
464  }
465  }
466 
467  return def_val;
468 }
469 
470 void WvConfEmu::setraw(WvString wvconfstr, const char *&_value,
471  int &parse_error)
472 {
473  char *section, *entry, *value;
474  parse_error = parse_wvconf_request(wvconfstr.edit(),
475  section, entry, value);
476  if (!parse_error)
477  {
478  set(section, entry, value);
479  _value = get(section, entry, value);
480  }
481  else
482  _value = NULL;
483 }
484 
485 
486 void WvConfEmu::setint(WvStringParm section, WvStringParm entry, int value)
487 {
488  if (!!entry)
489  uniconf[section][entry].setmeint(value);
490 }
491 
492 
493 void WvConfEmu::set(WvStringParm section, WvStringParm entry,
494  const char *value)
495 {
496  if (!!entry)
497  {
498  if (value && value[0] != 0)
499  uniconf[section][entry].setme(value);
500  else
501  uniconf[section][entry].setme(WvString::null);
502  dirty = true;
503  }
504 }
505 
506 
507 void WvConfEmu::maybesetint(WvStringParm section, WvStringParm entry,
508  int value)
509 {
510  if (!!entry && !get(section, entry, NULL))
511  setint(section, entry, value);
512 }
513 
514 
515 void WvConfEmu::maybeset(WvStringParm section, WvStringParm entry,
516  const char *value)
517 {
518  if (!!entry && get(section, entry, 0) == 0)
519  set(section, entry, value);
520 }
521 
522 
523 void WvConfEmu::delete_section(WvStringParm section)
524 {
525  uniconf[section].remove();
526  dirty = true;
527 }
528 
529 
530 int WvConfEmu::check_for_bool_string(const char *s)
531 {
532  if (strcasecmp(s, "off") == 0
533  || strcasecmp(s, "false") == 0
534  || strncasecmp(s, "no", 2) == 0) // also handles "none"
535  return 0;
536 
537  if (strcasecmp(s, "on") == 0
538  || strcasecmp(s, "true") == 0
539  || strcasecmp(s, "yes") == 0)
540  return 1;
541 
542  // not a special bool case, so just return the number
543  return atoi(s);
544 }
545 
546 
547 void WvConfEmu::Iter::rewind()
548 {
549  iter.rewind();
550  link.data = NULL;
551 }
552 
553 
554 WvLink *WvConfEmu::Iter::next()
555 {
556  link.data = NULL;
557  while (link.data == NULL && iter.next())
558  {
559  link.data = static_cast<void*>(conf[iter->key()]);
560  }
561  if (link.data)
562  {
563  return &link;
564  }
565  return NULL;
566 }
567 
568 
569 WvConfigSectionEmu *WvConfEmu::Iter::ptr() const
570 {
571  return conf[iter->key()];
572 }
573 
WvString::edit
char * edit()
make the string editable, and return a non-const (char*)
Definition: wvstring.h:397
WvConfigSectionEmu
Definition: wvconfemu.h:45
WvList::append
void append(T *data, bool autofree, const char *id=NULL)
Appends the element to the end of the list.
Definition: wvlinklist.h:276
UniConfKey::removefirst
UniConfKey removefirst(int n=1) const
Returns the path formed by removing the first n segments of this path.
Definition: uniconfkey.h:335
UniConf::commit
void commit() const
Commits information about this key recursively.
Definition: uniconf.cc:125
UniConf::setmeint
void setmeint(int value) const
Stores an integer value for this key into the registry.
Definition: uniconf.cc:89
UniConf::del_callback
void del_callback(void *cookie, bool recurse=true) const
Cancels notification requested using add_callback().
Definition: uniconf.cc:175
UniConf::remove
void remove() const
Removes this key and all of its children from the registry.
Definition: uniconf.h:232
UniConf
UniConf instances function as handles to subtrees of a UniConf tree and expose a high-level interface...
Definition: uniconf.h:50
UniIniGen
Loads and saves ".ini"-style files similar to those used by Windows, but adapted to represent keys an...
Definition: uniinigen.h:25
trim_string
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition: strutils.cc:59
UniConf::getme
WvString getme(WvStringParm defvalue=WvString::null) const
Fetches the string value for this key from the registry.
Definition: uniconf.cc:68
WvStringTable
Definition: wvstringtable.h:17
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvListBase::isempty
bool isempty() const
Quickly determines if the list is empty.
Definition: wvlinklist.h:62
get
Interface * get(IObject *aObj)
XPLC equivalent to dynamic_cast.
Definition: utils.h:184
WvFastString::cstr
const char * cstr() const
return a (const char *) for this string.
Definition: wvstring.h:267
UniConfKey
Represents a UniConf key which is a path in a hierarchy structured much like the traditional Unix fil...
Definition: uniconfkey.h:38
UniConf::copy
void copy(const UniConf &dst, bool force) const
Equivalent to "cp -r" in a standard unix filesystem.
Definition: uniconf.cc:103
UniConfKey::first
UniConfKey first(int n=1) const
Returns the path formed by the n first segments of this path.
Definition: uniconfkey.h:314
UniConf::getmeint
int getmeint(int defvalue=0) const
Fetches the integer value for this key from the registry.
Definition: uniconf.cc:77
UniConf::setme
void setme(WvStringParm value) const
Stores a string value for this key into the registry.
Definition: uniconf.cc:83
UniConf::haschildren
bool haschildren() const
Returns true if this key has children.
Definition: uniconf.cc:56
WvConfigEntryEmu
Definition: wvconfemu.h:31
UniConfRoot
Represents the root of a hierarhical registry consisting of pairs of UniConfKeys and associated strin...
Definition: uniconfroot.h:73
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
WvList
A linked list container class.
Definition: wvlinklist.h:197
UniConf::isnull
bool isnull() const
Returns true if the handle is invalid (NULL).
Definition: uniconf.h:95