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