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*/
34static 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
68static 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
78static 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
87WvConfigEntryEmu *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
108const 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
122void 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
134void WvConfigSectionEmu::quick_set(WvStringParm entry, WvStringParm value)
135{
136 uniconf[entry].setme(value);
137}
138
139
140bool WvConfigSectionEmu::isempty() const
141{
142 return !uniconf.haschildren();
143}
144
145
146WvConfigSectionEmu::Iter::~Iter()
147{
148}
149
150
151void WvConfigSectionEmu::Iter::rewind()
152{
153 iter.rewind();
154 link.data = entry = NULL;
155}
156
157
158WvLink *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
181WvLink *WvConfigSectionEmu::Iter::cur()
182{
183 return &link;
184}
185
186
187WvConfigEntryEmu *WvConfigSectionEmu::Iter::ptr() const
188{
189 return entry;
190}
191
192
193void *WvConfigSectionEmu::Iter::vptr() const
194{
195 return link.data;
196}
197
198
199void 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
221WvConfEmu::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
231WvConfEmu::~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
257void WvConfEmu::zap()
258{
259 uniconf.remove();
260}
261
262
263bool WvConfEmu::isclean() const
264{
265 return isok() && !dirty;
266}
267
268
269bool WvConfEmu::isok() const
270{
271 return !uniconf.isnull();
272}
273
274
275void 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
285void 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
295void WvConfEmu::save()
296{
297 uniconf.commit();
298}
299
300
301void WvConfEmu::flush()
302{
303 uniconf.commit();
304 dirty = false;
305}
306
307
308WvConfigSectionEmu *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
325void 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
358void 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
379void WvConfEmu::add_setbool(bool *b, WvStringParm _section, WvStringParm _key)
380{
381 add_callback(do_setbool, b, _section, _key, b);
382}
383
384
385void WvConfEmu::del_setbool(bool *b, WvStringParm _section, WvStringParm _key)
386{
387 del_callback(_section, _key, b);
388}
389
390
391void WvConfEmu::add_addname(WvStringList *list, WvStringParm sect, WvStringParm ent)
392{
393 add_callback(do_addname, list, sect, ent, list);
394}
395
396
397void WvConfEmu::del_addname(WvStringList *list,
398 WvStringParm sect, WvStringParm ent)
399{
400 del_callback(sect, ent, list);
401}
402
403
404WvString 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
417int 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
426const 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
440int 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
448const 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
470void 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
486void WvConfEmu::setint(WvStringParm section, WvStringParm entry, int value)
487{
488 if (!!entry)
489 uniconf[section][entry].setmeint(value);
490}
491
492
493void 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
507void 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
515void 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
523void WvConfEmu::delete_section(WvStringParm section)
524{
525 uniconf[section].remove();
526 dirty = true;
527}
528
529
530int 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
547void WvConfEmu::Iter::rewind()
548{
549 iter.rewind();
550 link.data = NULL;
551}
552
553
554WvLink *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
569WvConfigSectionEmu *WvConfEmu::Iter::ptr() const
570{
571 return conf[iter->key()];
572}
573
Represents a UniConf key which is a path in a hierarchy structured much like the traditional Unix fil...
Definition uniconfkey.h:39
UniConfKey removefirst(int n=1) const
Returns the path formed by removing the first n segments of this path.
Definition uniconfkey.h:335
UniConfKey first(int n=1) const
Returns the path formed by the n first segments of this path.
Definition uniconfkey.h:314
Represents the root of a hierarhical registry consisting of pairs of UniConfKeys and associated strin...
Definition uniconfroot.h:74
UniConf instances function as handles to subtrees of a UniConf tree and expose a high-level interface...
Definition uniconf.h:51
void commit() const
Commits information about this key recursively.
Definition uniconf.cc:125
void add_callback(void *cookie, const UniConfCallback &callback, bool recurse=true) const
Requests notification when any of the keys covered by the recursive depth specification change by inv...
Definition uniconf.cc:168
void remove() const
Removes this key and all of its children from the registry.
Definition uniconf.h:232
int getmeint(int defvalue=0) const
Fetches the integer value for this key from the registry.
Definition uniconf.cc:77
bool haschildren() const
Returns true if this key has children.
Definition uniconf.cc:56
void setme(WvStringParm value) const
Stores a string value for this key into the registry.
Definition uniconf.cc:83
bool isnull() const
Returns true if the handle is invalid (NULL).
Definition uniconf.h:95
void del_callback(void *cookie, bool recurse=true) const
Cancels notification requested using add_callback().
Definition uniconf.cc:175
void copy(const UniConf &dst, bool force) const
Equivalent to "cp -r" in a standard unix filesystem.
Definition uniconf.cc:103
void setmeint(int value) const
Stores an integer value for this key into the registry.
Definition uniconf.cc:89
WvString getme(WvStringParm defvalue=WvString::null) const
Fetches the string value for this key from the registry.
Definition uniconf.cc:68
Loads and saves ".ini"-style files similar to those used by Windows, but adapted to represent keys an...
Definition uniinigen.h:26
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition wvstring.h:94
const char * cstr() const
return a (const char *) for this string.
Definition wvstring.h:267
bool isempty() const
Quickly determines if the list is empty.
Definition wvlinklist.h:62
The iterator type for linked lists.
Definition wvlinklist.h:351
void append(T *data, bool autofree, const char *id=NULL)
Appends the element to the end of the list.
Definition wvlinklist.h:276
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString is an implementation of a simple and efficient printable-string class.
Definition wvstring.h:330
char * edit()
make the string editable, and return a non-const (char*)
Definition wvstring.h:397
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
Definition strutils.cc:59