1 /********************************************************************************
2 * *
3 * S e t t i n g s C l a s s *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxascii.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXElement.h"
30 #include "FXException.h"
31 #include "FXString.h"
32 #include "FXColors.h"
33 #include "FXFile.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36
37 /*
38 Notes:
39
40 - Format for settings database file:
41
42 [SectionKey]
43 EntryKey=string-with-no-spaces
44 EntryKey="string\nwith a\nnewline in it\n"
45 EntryKey=" string with leading and trailing spaces and \"embedded\" in it "
46 EntryKey=string with no leading or trailing spaces
47
48 - EntryKey may is of the form "ali baba", "ali-baba", "ali_baba", or "ali.baba".
49
50 - Leading/trailing spaces are NOT part of the EntryKey.
51
52 - Escape sequences now allow octal (\377) as well as hex (\xff) codes.
53
54 - EntryKey format should be like values.
55
56 - Extensive error checking in unparseFile() to ensure no settings data is
57 lost when disk is full.
58 */
59
60
61 #define EMPTY ((Entry*)(__settings__empty__+3))
62 #define BSHIFT 5
63
64 using namespace FX;
65
66 /*******************************************************************************/
67
68 namespace FX {
69
70
71 // Empty string dictionary table value
72 extern const FXint __string__empty__[];
73 extern const FXival __stringdictionary__empty__[];
74 extern const FXival __settings__empty__[];
75 const FXival __settings__empty__[6]={1,0,1,(FXival)(__string__empty__+1),(FXival)(__stringdictionary__empty__+3),0};
76
77
78 // Adjust the size of the table
no(FXival n)79 FXbool FXSettings::no(FXival n){
80 FXival m=no();
81 if(__likely(m!=n)){
82 Entry* elbat;
83 void* p;
84
85 // Release old table
86 if(1<m){
87 destructElms(table,m);
88 ::free(((FXival*)table)-3);
89 table=EMPTY;
90 }
91
92 // Allocate new table
93 if(1<n){
94 if(__unlikely((p=::calloc(sizeof(FXival)*3+sizeof(Entry)*n,1))==NULL)) return false;
95 elbat=(Entry*)(((FXival*)p)+3);
96 ((FXival*)elbat)[-3]=n;
97 ((FXival*)elbat)[-2]=0;
98 ((FXival*)elbat)[-1]=n;
99 constructElms(elbat,n);
100 table=elbat;
101 }
102 }
103 return true;
104 }
105
106
107 // Resize the table to the given size; the size must be a power of two
resize(FXival n)108 FXbool FXSettings::resize(FXival n){
109 FXSettings elbat;
110 FXASSERT((n&(n-1))==0); // Power of 2
111 FXASSERT((n-used())>0); // At least one free slot
112 if(elbat.no(n)){
113 if(1<elbat.no() && 1<no()){
114 FXuval p,b,h,x; FXival i;
115 for(i=0; i<no(); ++i){ // Hash existing entries into new table
116 if(!table[i].key.empty()){
117 p=b=h=table[i].hash;
118 while(elbat.table[x=p&(n-1)].hash){ // Locate slot
119 p=(p<<2)+p+b+1;
120 b>>=BSHIFT;
121 }
122 elbat.table[x].key.adopt(table[i].key); // Steal the string buffers
123 elbat.table[x].data.adopt(table[i].data);
124 elbat.table[x].hash=(FXuint)h; // And copy the hash value
125 }
126 }
127 elbat.free(n-used()); // All non-empty slots now free
128 elbat.used(used()); // Used slots not changed
129 elbat.setModified(isModified());
130 }
131 adopt(elbat);
132 return true;
133 }
134 return false;
135 }
136
137
138 // Construct settings database
FXSettings()139 FXSettings::FXSettings():table(EMPTY),modified(false){
140 FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
141 }
142
143
144 // Construct from another settings database
FXSettings(const FXSettings & other)145 FXSettings::FXSettings(const FXSettings& other):table(EMPTY),modified(other.modified){
146 FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
147 if(1<other.no()){
148 if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::FXSettings: out of memory\n"); }
149 copyElms(table,other.table,no());
150 free(other.free());
151 used(other.used());
152 }
153 }
154
155
156 // Assignment operator
operator =(const FXSettings & other)157 FXSettings& FXSettings::operator=(const FXSettings& other){
158 if(__likely(table!=other.table)){
159 if(1<other.no()){
160 if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::operator=: out of memory\n"); }
161 copyElms(table,other.table,no());
162 free(other.free());
163 used(other.used());
164 }
165 else{
166 no(1);
167 }
168 modified=true;
169 }
170 return *this;
171 }
172
173
174 // Adopt settings database from another
adopt(FXSettings & other)175 FXSettings& FXSettings::adopt(FXSettings& other){
176 if(__likely(table!=other.table)){
177 swap(table,other.table);
178 other.clear();
179 modified=true;
180 }
181 return *this;
182 }
183
184
185 // Find position of given key
find(const FXchar * ky) const186 FXival FXSettings::find(const FXchar* ky) const {
187 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::find: null or empty key\n"); }
188 if(__likely(!empty())){
189 FXuval p,b,x,h;
190 p=b=h=FXString::hash(ky);
191 FXASSERT(h);
192 while(table[x=p&(no()-1)].hash){
193 if(table[x].hash==h && table[x].key==ky) return x;
194 p=(p<<2)+p+b+1;
195 b>>=BSHIFT;
196 }
197 }
198 return -1;
199 }
200
201
202 // Return reference to string dictionary assocated with key
at(const FXchar * ky)203 FXStringDictionary& FXSettings::at(const FXchar* ky){
204 FXuval p,b,h,x;
205 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
206 p=b=h=FXString::hash(ky);
207 FXASSERT(h);
208 while(table[x=p&(no()-1)].hash){
209 if(table[x].hash==h && table[x].key==ky) goto x; // Return existing slot
210 p=(p<<2)+p+b+1;
211 b>>=BSHIFT;
212 }
213 if(__unlikely(free()<=1+(no()>>2)) && __unlikely(!resize(no()<<1))){ throw FXMemoryException("FXSettings::at: out of memory\n"); }
214 p=b=h;
215 while(table[x=p&(no()-1)].hash){
216 if(table[x].key.empty()) goto y; // Return voided slot
217 p=(p<<2)+p+b+1;
218 b>>=BSHIFT;
219 }
220 free(free()-1); // Put into empty slot
221 y:used(used()+1);
222 table[x].key=ky;
223 table[x].hash=(FXuint)h;
224 x:modified=true; // Assume its to be written
225 return table[x].data;
226 }
227
228
229 // Return constant reference to string dictionary assocated with key
at(const FXchar * ky) const230 const FXStringDictionary& FXSettings::at(const FXchar* ky) const {
231 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
232 if(__likely(!empty())){
233 FXuval p,b,x,h;
234 p=b=h=FXString::hash(ky);
235 FXASSERT(h);
236 while(table[x=p&(no()-1)].hash){
237 if(table[x].hash==h && table[x].key==ky) return table[x].data;
238 p=(p<<2)+p+b+1;
239 b>>=BSHIFT;
240 }
241 }
242 return EMPTY[0].data;
243 }
244
245
246 // Parse filename
parseFile(const FXString & filename,FXbool mrk)247 FXbool FXSettings::parseFile(const FXString& filename,FXbool mrk){
248 FXTRACE((100,"FXSettings::parseFile(%s,%d)\n",filename.text(),mrk));
249 FXFile file(filename,FXFile::Reading);
250 if(file.isOpen()){
251 FXString string;
252 string.length(file.size());
253 if(file.readBlock(string.text(),string.length())==string.length()){
254 return parse(string,mrk);
255 }
256 }
257 return false;
258 }
259
260
261 // Unparse registry file
unparseFile(const FXString & filename)262 FXbool FXSettings::unparseFile(const FXString& filename){
263 FXTRACE((100,"FXSettings::unparseFile(%s)\n",filename.text()));
264 FXFile file(filename,FXFile::Writing);
265 if(file.isOpen()){
266 FXString string;
267 if(unparse(string)){
268 return file.writeBlock(string.text(),string.length())==string.length();
269 }
270 }
271 return false;
272 }
273
274
275 // Parse single string to populate settings
parse(const FXString & str,FXbool mrk)276 FXbool FXSettings::parse(const FXString& str,FXbool mrk){
277 FXint lineno=1,p=0,b,e;
278 FXString section,name,value;
279
280 // Skip over byte-order mark
281 if(str[p]=='\xef' && str[p+1]=='\xbb' && str[p+2]=='\xbf') p+=3;
282
283 // Parse one line at a time
284 while(str[p]){
285
286 // Skip leading blanks
287 while(Ascii::isBlank(str[p])) ++p;
288
289 // Parse section name
290 if(str[p]=='['){
291
292 b=++p;
293
294 // Scan over section name
295 while(str[p] && str[p]!=']' && str[p]!='\r' && str[p]!='\n'){
296 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in section name.\n",lineno); goto nxt; }
297 ++p;
298 }
299
300 // Check errors
301 if(str[p]!=']'){ fxwarning("%d: expected ']' to close section name.\n",lineno); goto nxt; }
302
303 e=p++;
304
305 // Grab name
306 section=str.mid(b,e-b);
307 }
308
309 // Parse name-value pair
310 else if(str[p]!='#' && str[p]!=';' && str[p]!='\r' && str[p]!='\n'){
311
312 // Should have seen section prior to this
313 if(section.empty()){ fxwarning("%d: entry should follow a section.\n",lineno); goto nxt; }
314
315 b=p;
316
317 // Scan key name
318 while(str[p] && str[p]!='=' && str[p]!='\r' && str[p]!='\n'){
319 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry name.\n",lineno); goto nxt; }
320 ++p;
321 }
322
323 // Check errors
324 if(str[p]!='='){ fxwarning("%d: expected '=' to follow entry name.\n",lineno); goto nxt; }
325
326 e=p++;
327
328 // Remove trailing spaces after name
329 while(b<e && Ascii::isBlank(str[e-1])) e--;
330
331 // Grab name
332 name=str.mid(b,e-b);
333
334 // Skip leading spaces
335 while(Ascii::isBlank(str[p])) p++;
336
337 // Mark value
338 b=p;
339
340 // Scan value
341 while(str[p] && str[p]!='\n' && str[p]!='\r'){
342 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry value.\n",lineno); goto nxt; }
343 ++p;
344 }
345
346 e=p;
347
348 // Remove trailing spaces after value
349 while(b<e && Ascii::isBlank(str[e-1])) e--;
350
351 // Grab the unescaped value
352 value=FXString::unescape(str.mid(b,e-b),'"','"');
353
354 // Add entry to current section
355 at(section).at(name,mrk)=value;
356 }
357
358 // Skip to end of line
359 nxt:while(str[p]){
360 if(str[p++]=='\n'){ lineno++; break; }
361 }
362 }
363 return true;
364 }
365
366
367 // Unparse settings to a single string
unparse(FXString & str) const368 FXbool FXSettings::unparse(FXString& str) const {
369 FXival sec,ent,flg;
370
371 // Clear output
372 str.clear();
373
374 // Any keys at all?
375 if(!empty()){
376
377 // Loop over all sections
378 for(sec=0; sec<no(); ++sec){
379
380 // Get group, if any
381 if(!empty(sec)){
382
383 // Loop over all entries
384 for(ent=flg=0; ent<data(sec).no(); ++ent){
385
386 // Is key-value pair marked?
387 if(!data(sec).empty(ent) && data(sec).mark(ent)){
388
389 // Write section name if not written yet
390 if(!flg){
391 str.append("[");
392 str.append(key(sec)); // FIXME should escape group
393 str.append("]" ENDLINE);
394 flg=1;
395 }
396
397 // Write marked key-value pairs only
398 str.append(data(sec).key(ent));
399 str.append("=");
400 if(FXString::shouldEscape(data(sec).data(ent),'"','"')){
401 str.append(FXString::escape(data(sec).data(ent),'"','"'));
402 }
403 else{
404 str.append(data(sec).data(ent));
405 }
406 str.append(ENDLINE);
407 }
408 }
409
410 // Blank line after end
411 if(flg){
412 str.append(ENDLINE);
413 }
414 }
415 }
416 }
417 return true;
418 }
419
420
421 // Read a formatted registry entry
readFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...) const422 FXint FXSettings::readFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...) const {
423 const FXString& value=at(section).at(name);
424 FXint result=0;
425 if(!value.empty()){
426 va_list args;
427 va_start(args,fmt);
428 result=value.vscan(fmt,args);
429 va_end(args);
430 }
431 return result;
432 }
433
434
435 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...) const436 FXint FXSettings::readFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...) const {
437 const FXString& value=at(section).at(name);
438 FXint result=0;
439 if(!value.empty()){
440 va_list args;
441 va_start(args,fmt);
442 result=value.vscan(fmt,args);
443 va_end(args);
444 }
445 return result;
446 }
447
448
449 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...) const450 FXint FXSettings::readFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...) const {
451 const FXString& value=at(section).at(name);
452 FXint result=0;
453 if(!value.empty()){
454 va_list args;
455 va_start(args,fmt);
456 result=value.vscan(fmt,args);
457 va_end(args);
458 }
459 return result;
460 }
461
462
463 // Write a formatted registry entry
writeFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...)464 FXint FXSettings::writeFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...){
465 FXint result;
466 va_list args;
467 va_start(args,fmt);
468 result=at(section).at(name,true).vformat(fmt,args);
469 va_end(args);
470 return result;
471 }
472
473
474 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...)475 FXint FXSettings::writeFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...){
476 FXint result;
477 va_list args;
478 va_start(args,fmt);
479 result=at(section).at(name,true).vformat(fmt,args);
480 va_end(args);
481 return result;
482 }
483
484
485 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...)486 FXint FXSettings::writeFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...){
487 FXint result;
488 va_list args;
489 va_start(args,fmt);
490 result=at(section).at(name,true).vformat(fmt,args);
491 va_end(args);
492 return result;
493 }
494
495
496 // Read a string-valued registry entry
readStringEntry(const FXchar * section,const FXchar * name,const FXchar * def) const497 const FXchar* FXSettings::readStringEntry(const FXchar* section,const FXchar* name,const FXchar* def) const {
498 const FXStringDictionary& dict=at(section);
499 FXival slot=dict.find(name);
500 if(0<=slot){
501 return dict.data(slot).text(); // Use value at slot even if value was empty string!
502 }
503 return def;
504 }
505
506
507 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXchar * name,const FXchar * def) const508 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXchar* name,const FXchar* def) const {
509 return readStringEntry(section.text(),name,def);
510 }
511
512
513 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXString & name,const FXchar * def) const514 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXString& name,const FXchar* def) const {
515 return readStringEntry(section.text(),name.text(),def);
516 }
517
518
519 // Write a string-valued registry entry
writeStringEntry(const FXchar * section,const FXchar * name,const FXchar * val)520 FXbool FXSettings::writeStringEntry(const FXchar* section,const FXchar* name,const FXchar* val){
521 at(section).at(name,true)=val;
522 return true;
523 }
524
525
526 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXchar * name,const FXchar * val)527 FXbool FXSettings::writeStringEntry(const FXString& section,const FXchar* name,const FXchar* val){
528 return writeStringEntry(section.text(),name,val);
529 }
530
531
532 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXString & name,const FXchar * val)533 FXbool FXSettings::writeStringEntry(const FXString& section,const FXString& name,const FXchar* val){
534 return writeStringEntry(section.text(),name.text(),val);
535 }
536
537
538 // Read a int-valued registry entry
readIntEntry(const FXchar * section,const FXchar * name,FXint def) const539 FXint FXSettings::readIntEntry(const FXchar* section,const FXchar* name,FXint def) const {
540 const FXString& value=at(section).at(name);
541 if(!value.empty()){
542 FXint result;
543 FXbool ok;
544 result=value.toInt(10,&ok);
545 if(ok) return result;
546 }
547 return def;
548 }
549
550
551 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXchar * name,FXint def) const552 FXint FXSettings::readIntEntry(const FXString& section,const FXchar* name,FXint def) const {
553 return readIntEntry(section.text(),name,def);
554 }
555
556
557 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXString & name,FXint def) const558 FXint FXSettings::readIntEntry(const FXString& section,const FXString& name,FXint def) const {
559 return readIntEntry(section.text(),name.text(),def);
560 }
561
562
563 // Write a int-valued registry entry
writeIntEntry(const FXchar * section,const FXchar * name,FXint val)564 FXbool FXSettings::writeIntEntry(const FXchar* section,const FXchar* name,FXint val){
565 at(section).at(name,true).fromInt(val);
566 return true;
567 }
568
569
570 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXchar * name,FXint val)571 FXbool FXSettings::writeIntEntry(const FXString& section,const FXchar* name,FXint val){
572 return writeIntEntry(section.text(),name,val);
573 }
574
575
576 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXString & name,FXint val)577 FXbool FXSettings::writeIntEntry(const FXString& section,const FXString& name,FXint val){
578 return writeIntEntry(section.text(),name.text(),val);
579 }
580
581
582 // Read a unsigned int-valued registry entry
readUIntEntry(const FXchar * section,const FXchar * name,FXuint def) const583 FXuint FXSettings::readUIntEntry(const FXchar* section,const FXchar* name,FXuint def) const {
584 const FXString& value=at(section).at(name);
585 if(!value.empty()){
586 FXuint result;
587 FXbool ok;
588 result=value.toUInt(10,&ok);
589 if(ok) return result;
590 }
591 return def;
592 }
593
594
595 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXchar * name,FXuint def) const596 FXuint FXSettings::readUIntEntry(const FXString& section,const FXchar* name,FXuint def) const {
597 return readUIntEntry(section.text(),name,def);
598 }
599
600
601 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXString & name,FXuint def) const602 FXuint FXSettings::readUIntEntry(const FXString& section,const FXString& name,FXuint def) const {
603 return readUIntEntry(section.text(),name.text(),def);
604 }
605
606
607 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXchar * section,const FXchar * name,FXuint val)608 FXbool FXSettings::writeUIntEntry(const FXchar* section,const FXchar* name,FXuint val){
609 at(section).at(name,true).fromUInt(val);
610 return true;
611 }
612
613 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXchar * name,FXuint val)614 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXchar* name,FXuint val){
615 return writeUIntEntry(section.text(),name,val);
616 }
617
618
619 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXString & name,FXuint val)620 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXString& name,FXuint val){
621 return writeUIntEntry(section.text(),name.text(),val);
622 }
623
624
625 // Read a 64-bit long integer registry entry
readLongEntry(const FXchar * section,const FXchar * name,FXlong def) const626 FXlong FXSettings::readLongEntry(const FXchar* section,const FXchar* name,FXlong def) const {
627 const FXString& value=at(section).at(name);
628 if(!value.empty()){
629 FXlong result;
630 FXbool ok;
631 result=value.toLong(10,&ok);
632 if(ok) return result;
633 }
634 return def;
635 }
636
637
638 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXchar * name,FXlong def) const639 FXlong FXSettings::readLongEntry(const FXString& section,const FXchar* name,FXlong def) const {
640 return readLongEntry(section.text(),name,def);
641 }
642
643
644 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXString & name,FXlong def) const645 FXlong FXSettings::readLongEntry(const FXString& section,const FXString& name,FXlong def) const {
646 return readLongEntry(section.text(),name.text(),def);
647 }
648
649
650 // Write a 64-bit long integer registry entry
writeLongEntry(const FXchar * section,const FXchar * name,FXlong val)651 FXbool FXSettings::writeLongEntry(const FXchar* section,const FXchar* name,FXlong val){
652 at(section).at(name,true).fromLong(val);
653 return true;
654 }
655
656
657 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXchar * name,FXlong val)658 FXbool FXSettings::writeLongEntry(const FXString& section,const FXchar* name,FXlong val){
659 return writeLongEntry(section.text(),name,val);
660 }
661
662
663 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXString & name,FXlong val)664 FXbool FXSettings::writeLongEntry(const FXString& section,const FXString& name,FXlong val){
665 return writeLongEntry(section.text(),name.text(),val);
666 }
667
668
669 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXchar * section,const FXchar * name,FXulong def) const670 FXulong FXSettings::readULongEntry(const FXchar* section,const FXchar* name,FXulong def) const {
671 const FXString& value=at(section).at(name);
672 if(!value.empty()){
673 FXulong result;
674 FXbool ok;
675 result=value.toULong(10,&ok);
676 if(ok) return result;
677 }
678 return def;
679 }
680
681
682 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXchar * name,FXulong def) const683 FXulong FXSettings::readULongEntry(const FXString& section,const FXchar* name,FXulong def) const {
684 return readULongEntry(section.text(),name,def);
685 }
686
687
688 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXString & name,FXulong def) const689 FXulong FXSettings::readULongEntry(const FXString& section,const FXString& name,FXulong def) const {
690 return readULongEntry(section.text(),name.text(),def);
691 }
692
693
694 // Write a 64-bit long integer registry entry
writeULongEntry(const FXchar * section,const FXchar * name,FXulong val)695 FXbool FXSettings::writeULongEntry(const FXchar* section,const FXchar* name,FXulong val){
696 at(section).at(name,true).fromULong(val);
697 return true;
698 }
699
700
701 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXchar * name,FXulong val)702 FXbool FXSettings::writeULongEntry(const FXString& section,const FXchar* name,FXulong val){
703 return writeULongEntry(section.text(),name,val);
704 }
705
706
707 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXString & name,FXulong val)708 FXbool FXSettings::writeULongEntry(const FXString& section,const FXString& name,FXulong val){
709 return writeULongEntry(section.text(),name.text(),val);
710 }
711
712
713 // Read a double-valued registry entry
readRealEntry(const FXchar * section,const FXchar * name,FXdouble def) const714 FXdouble FXSettings::readRealEntry(const FXchar* section,const FXchar* name,FXdouble def) const {
715 const FXString& value=at(section).at(name);
716 if(!value.empty()){
717 FXdouble result;
718 FXbool ok;
719 result=value.toDouble(&ok);
720 if(ok) return result;
721 }
722 return def;
723 }
724
725 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXchar * name,FXdouble def) const726 FXdouble FXSettings::readRealEntry(const FXString& section,const FXchar* name,FXdouble def) const {
727 return readRealEntry(section.text(),name,def);
728 }
729
730
731 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXString & name,FXdouble def) const732 FXdouble FXSettings::readRealEntry(const FXString& section,const FXString& name,FXdouble def) const {
733 return readRealEntry(section.text(),name.text(),def);
734 }
735
736
737 // Write a double-valued registry entry
writeRealEntry(const FXchar * section,const FXchar * name,FXdouble val)738 FXbool FXSettings::writeRealEntry(const FXchar* section,const FXchar* name,FXdouble val){
739 at(section).at(name,true).fromDouble(val,16,2);
740 return true;
741 }
742
743
744 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXchar * name,FXdouble val)745 FXbool FXSettings::writeRealEntry(const FXString& section,const FXchar* name,FXdouble val){
746 return writeRealEntry(section.text(),name,val);
747 }
748
749
750 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXString & name,FXdouble val)751 FXbool FXSettings::writeRealEntry(const FXString& section,const FXString& name,FXdouble val){
752 return writeRealEntry(section.text(),name.text(),val);
753 }
754
755
756 // Read a color registry entry
readColorEntry(const FXchar * section,const FXchar * name,FXColor def) const757 FXColor FXSettings::readColorEntry(const FXchar* section,const FXchar* name,FXColor def) const {
758 const FXString& value=at(section).at(name);
759 if(!value.empty()){
760 return colorFromName(value);
761 }
762 return def;
763 }
764
765
766 // Read a color registry entry
readColorEntry(const FXString & section,const FXchar * name,FXColor def) const767 FXColor FXSettings::readColorEntry(const FXString& section,const FXchar* name,FXColor def) const {
768 return readColorEntry(section.text(),name,def);
769 }
770
771
772 // Read a color registry entry
readColorEntry(const FXString & section,const FXString & name,FXColor def) const773 FXColor FXSettings::readColorEntry(const FXString& section,const FXString& name,FXColor def) const {
774 return readColorEntry(section.text(),name.text(),def);
775 }
776
777
778 // Write a color registry entry
writeColorEntry(const FXchar * section,const FXchar * name,FXColor val)779 FXbool FXSettings::writeColorEntry(const FXchar* section,const FXchar* name,FXColor val){
780 at(section).at(name,true)=nameFromColor(val);
781 return true;
782 }
783
784
785 // Write a color registry entry
writeColorEntry(const FXString & section,const FXchar * name,FXColor val)786 FXbool FXSettings::writeColorEntry(const FXString& section,const FXchar* name,FXColor val){
787 return writeColorEntry(section.text(),name,val);
788 }
789
790
791 // Write a color registry entry
writeColorEntry(const FXString & section,const FXString & name,FXColor val)792 FXbool FXSettings::writeColorEntry(const FXString& section,const FXString& name,FXColor val){
793 return writeColorEntry(section.text(),name.text(),val);
794 }
795
796
797 // Read a boolean registry entry
readBoolEntry(const FXchar * section,const FXchar * name,FXbool def) const798 FXbool FXSettings::readBoolEntry(const FXchar* section,const FXchar* name,FXbool def) const {
799 const FXString& value=at(section).at(name);
800 if(!value.empty()){
801 if(comparecase(value,"true")==0) return true;
802 else if(comparecase(value,"false")==0) return false;
803 else if(comparecase(value,"yes")==0) return true;
804 else if(comparecase(value,"no")==0) return false;
805 else if(comparecase(value,"on")==0) return true;
806 else if(comparecase(value,"off")==0) return false;
807 else if(comparecase(value,"1")==0) return true;
808 else if(comparecase(value,"0")==0) return false;
809 }
810 return def;
811 }
812
813
814 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXchar * name,FXbool def) const815 FXbool FXSettings::readBoolEntry(const FXString& section,const FXchar* name,FXbool def) const {
816 return readBoolEntry(section.text(),name,def);
817 }
818
819
820 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXString & name,FXbool def) const821 FXbool FXSettings::readBoolEntry(const FXString& section,const FXString& name,FXbool def) const {
822 return readBoolEntry(section.text(),name.text(),def);
823 }
824
825
826 // Write a boolean registry entry
writeBoolEntry(const FXchar * section,const FXchar * name,FXbool val)827 FXbool FXSettings::writeBoolEntry(const FXchar* section,const FXchar* name,FXbool val){
828 at(section).at(name,true)=val?"true":"false";
829 return true;
830 }
831
832
833 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXchar * name,FXbool val)834 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXchar* name,FXbool val){
835 return writeBoolEntry(section.text(),name,val);
836 }
837
838
839 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXString & name,FXbool val)840 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXString& name,FXbool val){
841 return writeBoolEntry(section.text(),name.text(),val);
842 }
843
844
845 // See if entry exists
existingEntry(const FXchar * section,const FXchar * name) const846 FXbool FXSettings::existingEntry(const FXchar* section,const FXchar* name) const {
847 return 0<=at(section).find(name);
848 }
849
850
851 // See if entry exists
existingEntry(const FXString & section,const FXchar * name) const852 FXbool FXSettings::existingEntry(const FXString& section,const FXchar* name) const {
853 return existingEntry(section.text(),name);
854 }
855
856
857 // See if entry exists
existingEntry(const FXString & section,const FXString & name) const858 FXbool FXSettings::existingEntry(const FXString& section,const FXString& name) const {
859 return existingEntry(section.text(),name.text());
860 }
861
862
863 // See if section exists
existingSection(const FXchar * section) const864 FXbool FXSettings::existingSection(const FXchar* section) const {
865 return 0<=find(section);
866 }
867
868
869 // See if section exists
existingSection(const FXString & section) const870 FXbool FXSettings::existingSection(const FXString& section) const {
871 return existingSection(section.text());
872 }
873
874
875 // Delete a registry entry
deleteEntry(const FXchar * section,const FXchar * name)876 void FXSettings::deleteEntry(const FXchar* section,const FXchar* name){
877 at(section).remove(name);
878 }
879
880
881 // Delete a registry entry
deleteEntry(const FXString & section,const FXchar * name)882 void FXSettings::deleteEntry(const FXString& section,const FXchar* name){
883 deleteEntry(section.text(),name);
884 }
885
886
887 // Delete a registry entry
deleteEntry(const FXString & section,const FXString & name)888 void FXSettings::deleteEntry(const FXString& section,const FXString& name){
889 deleteEntry(section.text(),name.text());
890 }
891
892
893 // Delete section
deleteSection(const FXchar * section)894 void FXSettings::deleteSection(const FXchar* section){
895 if(__unlikely(!section || !*section)){ throw FXRangeException("FXSettings::deleteSection: null or empty key\n"); }
896 if(__likely(!empty())){
897 FXuval p,b,h,x;
898 p=b=h=FXString::hash(section);
899 FXASSERT(h);
900 while(table[x=p&(no()-1)].hash!=h || table[x].key!=section){
901 if(!table[x].hash) return;
902 p=(p<<2)+p+b+1;
903 b>>=BSHIFT;
904 }
905 table[x].key.clear(); // Void the slot (not empty!)
906 table[x].data.clear();
907 used(used()-1);
908 modified=true;
909 if(__unlikely(used()<=(no()>>2))) resize(no()>>1);
910 }
911 }
912
913
914 // Delete section
deleteSection(const FXString & section)915 void FXSettings::deleteSection(const FXString& section){
916 deleteSection(section.text());
917 }
918
919
920 // Clear all sections
clear()921 void FXSettings::clear(){
922 modified=true;
923 no(1);
924 }
925
926
927 // Clean up
~FXSettings()928 FXSettings::~FXSettings(){
929 clear();
930 }
931
932 }
933