blob: 2cec7c80bb55a87a7ed0e4cc9a58b1f7ab6b2b03 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#include <cc++/export.h>
41#include <cc++/file.h>
42#include <cc++/misc.h>
43#include <sys/stat.h>
44#include "private.h"
45#include <cstdlib>
46#include <cstdio>
47
48#ifndef CAPE_REGISTRY_APPLICATIONS
49#define CAPE_REGISTRY_APPLICATIONS "SOFTWARE\\CAPE Applications"
50#define CAPE_REGISTRY_USERSETTINGS "Software\\CAPE Applications"
51#endif
52
53#ifdef WIN32
54class Registry
55{
56public:
57 const char *configdir;
58 Registry();
59};
60
61Registry::Registry()
62{
63 configdir = getenv("SystemRoot");
64 if(!configdir || !*configdir)
65 configdir = getenv("windir");
66 if(!configdir || !*configdir)
67 configdir = "C:\\WINDOWS";
68}
69
70static Registry registry;
71#endif
72
73#ifdef CCXX_NAMESPACES
74namespace ost {
75using namespace std;
76#endif
77
78int Keydata::count = 0;
79int Keydata::sequence = 1;
80ifstream *Keydata::cfgFile = NULL;
81char Keydata::lastpath[KEYDATA_PATH_SIZE + 1];
82
83void endKeydata();
84
85static unsigned bitsize(void)
86{
87 if(sizeof(void *) > 4)
88 return 2;
89 return 1;
90}
91
92Keydata::Keydata() :
93MemPager(KEYDATA_PAGER_SIZE * bitsize())
94{
95 link = 0;
96 memset(&keys, 0, sizeof(keys));
97}
98
99Keydata::Keydata(const char *path) :
100MemPager(KEYDATA_PAGER_SIZE * bitsize())
101{
102 link = 0;
103 memset(&keys, 0, sizeof(keys));
104 load(path);
105}
106
107Keydata::Keydata(Define *pairs, const char *path) :
108MemPager(KEYDATA_PAGER_SIZE * bitsize())
109{
110 link = 0;
111 memset(&keys, 0, sizeof(keys));
112 load(pairs);
113 if(path)
114 load(path);
115}
116
117Keydata::~Keydata()
118{
119 clean();
120 unlink();
121 if(count < 1)
122 endKeydata();
123}
124
125Keydata::Keysym *Keydata::getSymbol(const char *sym, bool create)
126{
127 unsigned path = getIndex(sym);
128 size_t len = strlen(sym) + 1;
129 Keysym *key = keys[path];
130
131 while(key) {
132 if(!stricmp(sym, key->sym))
133 return key;
134 key = key->next;
135 }
136 if(!create)
137 return NULL;
138
139 // note: keysym has 1 byte for null character already
140 key = (Keysym *)alloc(sizeof(Keysym) + len - 1);
141 setString(key->sym, len, sym);
142
143 key->count = 0;
144 key->next = keys[path];
145 key->data = NULL;
146 key->list = NULL;
147 keys[path] = key;
148 return key;
149}
150
151unsigned Keydata::getIndex(const char *str)
152{
153 unsigned key = 0;
154
155 while(*str)
156 key = (key << 1) ^ (*(str++) & 0x1f);
157
158 return key % KEYDATA_INDEX_SIZE;
159}
160
161int Keydata::getCount(const char *sym)
162{
163 Keysym *key = getSymbol(sym, false);
164 if(!key)
165 return 0;
166
167 return key->count;
168}
169
170const char *Keydata::getFirst(const char *sym)
171{
172 Keysym *key = getSymbol(sym, false);
173 Keyval *val;
174
175 if(!key)
176 return NULL;
177
178 val = key->data;
179 if(!val)
180 return NULL;
181
182 while(val->next)
183 val = val->next;
184
185 return val->val;
186}
187
188const char *Keydata::getString(const char *sym, const char *def)
189{
190 const char *cp = getLast(sym);
191
192 if(!cp)
193 return def;
194
195 return cp;
196}
197
198long Keydata::getLong(const char *sym, long def)
199{
200 const char *cp = getLast(sym);
201
202 if(!cp)
203 return def;
204
205 return atol(cp);
206}
207
208double Keydata::getDouble(const char *sym, double def)
209{
210 const char *cp = getLast(sym);
211
212 if(!cp)
213 return def;
214
215 return atof(cp);
216}
217
218bool Keydata::getBool(const char *sym)
219{
220 const char *cp = getLast(sym);
221
222 if(!cp)
223 return false;
224
225 switch(*cp) {
226 case 'y':
227 case 'Y':
228 case 't':
229 case 'T':
230 return true;
231 default:
232 return false;
233 }
234}
235
236bool Keydata::isKey(const char *sym)
237{
238 if(getLast(sym))
239 return true;
240
241 return false;
242}
243
244const char *Keydata::getLast(const char *sym)
245{
246 Keysym *key = getSymbol(sym, false);
247 if(!key)
248 return NULL;
249
250 if(!key->data)
251 return NULL;
252
253 return key->data->val;
254}
255
256unsigned Keydata::getCount(void)
257{
258 unsigned icount = 0;
259 Keysym *key;
260 int idx;
261
262 for(idx = 0; idx < KEYDATA_INDEX_SIZE; ++idx) {
263 key = keys[idx];
264 while(key) {
265 ++icount;
266 key = key->next;
267 }
268 }
269 return icount;
270}
271
272unsigned Keydata::getIndex(char **data, unsigned max)
273{
274 int idx;
275 Keysym *key;
276 unsigned icount = 0;
277
278 for(idx = 0; idx < KEYDATA_INDEX_SIZE; ++idx) {
279 if(icount >= max)
280 break;
281 key = keys[idx];
282 while(key && icount < max) {
283 *(data++) = key->sym;
284 ++icount;
285 key = key->next;
286 }
287 }
288 *data = NULL;
289 return icount;
290}
291
292void Keydata::setValue(const char *sym, const char *data)
293{
294 size_t len = strlen(data) + 1;
295 Keysym *key = getSymbol(sym, true);
296 Keyval *val;
297
298 if(!data)
299 data = "";
300
301 // note keyval has 1 byte for null already
302 val = (Keyval *)alloc(sizeof(Keyval) + len - 1);
303 ++key->count;
304 key->list = NULL;
305 val->next = key->data;
306 key->data = val;
307 setString(val->val, len, data);
308}
309
310const char *const *Keydata::getList(const char *sym)
311{
312 int icount;
313 Keysym *key = getSymbol(sym, false);
314 Keyval *data;
315
316 if(!key)
317 return NULL;
318
319 icount = key->count;
320 if(!icount)
321 return NULL;
322
323 ++icount;
324 if(!key->list) {
325 key->list =(const char **)first(sizeof(const char**) * icount);
326 key->list[--icount] = NULL;
327 data = key->data;
328 while(icount && data) {
329 key->list[--icount] = data->val;
330 data = data->next;
331 }
332 while(icount)
333 key->list[--icount] = "";
334 }
335 return key->list;
336}
337
338void Keydata::clrValue(const char *sym)
339{
340 Keysym *key = getSymbol(sym, false);
341 if(!key)
342 return;
343
344 key->count = 0;
345 key->list = NULL;
346 key->data = NULL;
347}
348
349void Keydata::load(Define *defs)
350{
351 Keysym *key;
352
353 while(defs->keyword) {
354 key = getSymbol(defs->keyword, true);
355 if(!key->data)
356 setValue(defs->keyword, defs->value);
357 ++defs;
358 }
359}
360
361void Keydata::load(const char *keypath)
362{
363 loadPrefix(NULL, keypath);
364}
365
366void Keydata::loadPrefix(const char *pre, const char *keypath)
367{
368 // FIXME: use string of dinamic allocation
369 char path[KEYDATA_PATH_SIZE];
370 char seek[33];
371 const char *prefix = NULL;
372 const char *ext;
373#ifdef WIN32
374 const char *ccp;
375#endif
376 char *cp;
377 bool etcpath = false, etctest = false;
378#ifndef WIN32
379 struct stat ino;
380#endif
381
382 path[0] = 0;
383
384#ifdef WIN32
385 HKEY key;
386 LONG value;
387 DWORD keynamelen, keytextlen;
388 TCHAR keyname[256];
389 TCHAR keytext[256];
390 DWORD keyindex = 0;
391 char *regprefix = getenv("CONFIG_REGISTRY");
392
393 if(!regprefix)
394 regprefix="";
395
396 ccp = keypath;
397 if(*ccp == '~') {
398 ++ccp;
399 if(*ccp == '/')
400 ++ccp;
401 snprintf(path, sizeof(path), CAPE_REGISTRY_USERSETTINGS "/%s%s/", regprefix, ccp);
402 }
403 else {
404 if(*ccp == '/')
405 ++ccp;
406 snprintf(path, sizeof(path), CAPE_REGISTRY_APPLICATIONS "/%s%s/", regprefix, ccp);
407 }
408
409 cp = path;
410 while(NULL != (cp = strchr(cp, '/')))
411 *cp = '\\';
412
413 if(*keypath == '~')
414 value = RegOpenKey(HKEY_CURRENT_USER, path, &key);
415 else
416 value = RegOpenKey(HKEY_LOCAL_MACHINE, path, &key);
417
418 // if registry key path is found, then we use registry values
419 // and ignore .ini files...
420
421 if(value == ERROR_SUCCESS) {
422 for(;;) {
423 keynamelen = 256;
424 value = RegEnumKeyEx(key, keyindex, keyname, &keynamelen, NULL, NULL, NULL, NULL);
425 if(value != ERROR_SUCCESS)
426 break;
427
428 keytextlen = 256;
429 keytext[0] = '\0';
430 value = RegEnumValue(key, keyindex++, keytext, &keytextlen, NULL, NULL, NULL, NULL);
431 if(value != ERROR_SUCCESS)
432 continue;
433
434 if(pre) {
435 snprintf(path, sizeof(path), "%s.%s", pre, keyname);
436 setValue(path, keytext);
437 }
438 else
439 setValue(keyname, keytext);
440 }
441 RegCloseKey(key);
442 return;
443 }
444
445 // windows will not support subdir .ini tree; now if not in
446 // registry, then assume unavailable
447
448 ccp = strchr(keypath + 3, '/');
449 if(ccp)
450 ccp = strchr(ccp, '/');
451
452 if(ccp)
453 return;
454
455 if(*keypath == '~') {
456 prefix = getenv("USERPROFILE");
457 if(!prefix)
458 return;
459
460 setString(path, sizeof(path) - 8, prefix);
461 addString(path, sizeof(path), "\\");
462 ++keypath;
463 cp = path;
464 while(NULL != (cp = strchr(cp, '\\')))
465 *cp = '/';
466 }
467
468#else
469
470 if(*keypath == '~') {
471 prefix = getenv("HOME");
472 if(!prefix)
473 return;
474
475 setString(path, sizeof(path) - 8, prefix);
476 addString(path, sizeof(path), "/.");
477 ++keypath;
478 }
479
480#endif
481
482 if(!prefix) {
483#ifdef WIN32
484 if(!prefix || !*prefix)
485 prefix = registry.configdir;
486#else
487
488retry:
489#ifdef ETC_CONFDIR
490 if(!prefix || !*prefix) {
491 if(etcpath)
492 prefix = ETC_PREFIX;
493 else
494 prefix = ETC_CONFDIR;
495 etctest = true;
496 if(!stricmp(ETC_PREFIX, ETC_CONFDIR))
497 etcpath = true;
498 }
499#else
500
501 if(!prefix || !*prefix) {
502 etctest = true;
503 prefix = ETC_PREFIX;
504 }
505#endif
506
507#endif
508
509 setString(path, sizeof(path) - 8, prefix);
510
511#ifdef WIN32
512 cp = path;
513 while(NULL != (cp = strchr(cp, '\\')))
514 *cp = '/';
515
516 cp = path + strlen(path) - 1;
517 if(*cp != '/') {
518 *(++cp) = '/';
519 *(++cp) = 0;
520 }
521#endif
522 prefix = NULL;
523 }
524
525 if(*keypath == '/' || *keypath == '\\')
526 ++keypath;
527
528 addString(path, sizeof(path), keypath);
529 cp = strrchr(path, '/');
530 setString(seek, sizeof(seek), cp + 1);
531 *cp = 0;
532
533 ext = strrchr(path, '/');
534 if(ext)
535 ext = strrchr(ext + 2, '.');
536 else
537 ext = strrchr(path + 1, '.');
538
539#ifdef WIN32
540 if(!ext)
541 addString(path, sizeof(path), ".ini");
542#else
543 if(!prefix && !ext)
544 addString(path, sizeof(path), ".conf");
545 else if(prefix && !ext)
546 addString(path, sizeof(path), "rc");
547
548 ino.st_uid = (unsigned)-1;
549
550 if(stat(path, &ino) < 0 && etctest && !etcpath) {
551 etcpath = true;
552 goto retry;
553 }
554
555 // if root, make sure root owned config...
556
557 if(!geteuid() && ino.st_uid)
558 return;
559
560 // if root, make sure from a etc path only...
561
562 if(!geteuid() && !etctest)
563 return;
564
565#endif
566 loadFile(path, seek, pre);
567}
568
569void Keydata::loadFile(const char *path, const char *keys, const char *pre)
570{
571 char seek[33];
572 char find[33];
573 char line[256];
574 char buffer[256];
575 char *cp, *ep;
576 int fpos;
577
578 if(keys)
579 setString(seek, sizeof(seek), keys);
580 else
581 seek[0] = 0;
582
583 if(strcmp(path, lastpath)) {
584 endKeydata();
585 if(canAccess(path))
586 cfgFile->open(path, ios::in);
587 else
588 return;
589 if(!cfgFile->is_open())
590 return;
591 setString(lastpath, sizeof(lastpath), path);
592 }
593
594 if(link != sequence) {
595 link = sequence;
596 ++count;
597 }
598
599 find[0] = 0;
600 cfgFile->seekg(0);
601 while(keys && stricmp(seek, find)) {
602 cfgFile->getline(line, sizeof(line) - 1);
603 if(cfgFile->eof()) {
604 lastpath[0] = 0;
605 cfgFile->close();
606 cfgFile->clear();
607 return;
608 }
609
610 cp = line;
611 while(*cp == ' ' || *cp == '\n' || *cp == '\t')
612 ++cp;
613
614 if(*cp != '[')
615 continue;
616
617 ep = strchr(cp, ']');
618 if(ep)
619 *ep = 0;
620 else
621 continue;
622
623 setString(find, 32, ++cp);
624 }
625
626 for(;;) {
627 if(cfgFile->eof()) {
628 lastpath[0] = 0;
629 cfgFile->close();
630 cfgFile->clear();
631 return;
632 }
633
634 cfgFile->getline(line, sizeof(line) - 1);
635
636 cp = line;
637 while(*cp == ' ' || *cp == '\t' || *cp == '\n')
638 ++cp;
639
640 if(!*cp || *cp == '#' || *cp == ';' || *cp == '!')
641 continue;
642
643 if(*cp == '[')
644 return;
645
646 fpos = 0;
647 while(*cp && *cp != '=') {
648 if(*cp == ' ' || *cp == '\t') {
649 ++cp;
650 continue;
651 }
652 find[fpos] = *(cp++);
653 if(fpos < 32)
654 ++fpos;
655 }
656 find[fpos] = 0;
657 if(*cp != '=')
658 continue;
659
660 ++cp;
661 while(*cp == ' ' || *cp == '\t' || *cp == '\n')
662 ++cp;
663
664 ep = cp + strlen(cp);
665 while((--ep) > cp) {
666 if(*ep == ' ' || *ep == '\t' || *ep == '\n')
667 *ep = 0;
668 else
669 break;
670 }
671
672 if(*cp == *ep && (*cp == '\'' || *cp == '\"')) {
673 ++cp;
674 *ep = 0;
675 }
676
677 if(pre) {
678#ifdef HAVE_SNPRINTF
679 snprintf(buffer, 256, "%s.%s", pre, find);
680#else
681 setString(buffer, 256, pre);
682 addString(buffer, 256, ".");
683 addString(buffer, 256, find);
684#endif
685 setValue(buffer, cp);
686 }
687 else
688 setValue(find, cp);
689 }
690}
691
692void Keydata::unlink(void)
693{
694 if(link != sequence) {
695 link = 0;
696 return;
697 }
698
699 link = 0;
700 --count;
701}
702
703void Keydata::end(void)
704{
705 Keydata::count = 0;
706 ++Keydata::sequence;
707 if(!Keydata::sequence)
708 ++Keydata::sequence;
709
710 Keydata::lastpath[0] = 0;
711 if(!Keydata::cfgFile)
712 Keydata::cfgFile = new std::ifstream();
713 else if(Keydata::cfgFile->is_open()) {
714 Keydata::cfgFile->close();
715 Keydata::cfgFile->clear();
716 }
717}
718
719#ifdef CCXX_NAMESPACES
720}
721#endif
722
723/** EMACS **
724 * Local variables:
725 * mode: c++
726 * c-basic-offset: 4
727 * End:
728 */