1 /********************************************************************************
2 *                                                                               *
3 *                          S e t t i n g s   C l a s s                          *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2020 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 NOMEMORY  ((Entry*)(((FXival*)NULL)+3))
63 #define BSHIFT    5
64 
65 using namespace FX;
66 
67 /*******************************************************************************/
68 
69 namespace FX {
70 
71 
72 // Empty string dictionary table value
73 extern const FXint __string__empty__[];
74 extern const FXival __stringdictionary__empty__[];
75 extern const FXival __settings__empty__[];
76 const FXival __settings__empty__[6]={1,0,1,(FXival)(__string__empty__+1),(FXival)(__stringdictionary__empty__+3),0};
77 
78 
79 // Adjust the size of the table
no(FXival n)80 FXbool FXSettings::no(FXival n){
81   FXival m=no();
82   if(__likely(m!=n)){
83     Entry *elbat;
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((elbat=(Entry*)(((FXival*)::calloc(sizeof(FXival)*3+sizeof(Entry)*n,1))+3))==NOMEMORY) return false;
95       ((FXival*)elbat)[-3]=n;
96       ((FXival*)elbat)[-2]=0;
97       ((FXival*)elbat)[-1]=n;
98       constructElms(elbat,n);
99       table=elbat;
100       }
101     }
102   return true;
103   }
104 
105 
106 // Resize the table to the given size; the size must be a power of two
resize(FXival n)107 FXbool FXSettings::resize(FXival n){
108   FXSettings elbat;
109   FXASSERT((n&(n-1))==0);       // Power of 2
110   FXASSERT((n-used())>0);       // At least one free slot
111   if(elbat.no(n)){
112     if(1<elbat.no() && 1<no()){
113       FXuval p,b,h,x; FXival i;
114       for(i=0; i<no(); ++i){                  // Hash existing entries into new table
115         if(!table[i].key.empty()){
116           p=b=h=table[i].hash;
117           while(elbat.table[x=p&(n-1)].hash){ // Locate slot
118             p=(p<<2)+p+b+1;
119             b>>=BSHIFT;
120             }
121           elbat.table[x].key.adopt(table[i].key);       // Steal the string buffers
122           elbat.table[x].data.adopt(table[i].data);
123           elbat.table[x].hash=(FXuint)h;                // And copy the hash value
124           }
125         }
126       elbat.free(n-used());     // All non-empty slots now free
127       elbat.used(used());       // Used slots not changed
128       elbat.setModified(isModified());
129       }
130     adopt(elbat);
131     return true;
132     }
133   return false;
134   }
135 
136 
137 // Construct settings database
FXSettings()138 FXSettings::FXSettings():table(EMPTY),modified(false){
139   FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
140   }
141 
142 
143 // Construct from another string dictionary
FXSettings(const FXSettings & other)144 FXSettings::FXSettings(const FXSettings& other):table(EMPTY),modified(other.modified){
145   FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
146   if(1<other.no()){
147     if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::FXSettings: out of memory\n"); }
148     copyElms(table,other.table,no());
149     free(other.free());
150     used(other.used());
151     }
152   }
153 
154 
155 // Assignment operator
operator =(const FXSettings & other)156 FXSettings& FXSettings::operator=(const FXSettings& other){
157   if(__likely(table!=other.table)){
158     if(1<other.no()){
159       if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::operator=: out of memory\n"); }
160       copyElms(table,other.table,no());
161       free(other.free());
162       used(other.used());
163       }
164     else{
165       no(1);
166       }
167     modified=true;
168     }
169   return *this;
170   }
171 
172 
173 // Adopt string dictionary from another
adopt(FXSettings & other)174 FXSettings& FXSettings::adopt(FXSettings& other){
175   if(__likely(table!=other.table)){
176     swap(table,other.table);
177     other.clear();
178     modified=true;
179     }
180   return *this;
181   }
182 
183 
184 // Find position of given key
find(const FXchar * ky) const185 FXival FXSettings::find(const FXchar* ky) const {
186   if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::find: null or empty key\n"); }
187   if(__likely(!empty())){
188     FXuval p,b,x,h;
189     p=b=h=FXString::hash(ky);
190     FXASSERT(h);
191     while(table[x=p&(no()-1)].hash){
192       if(table[x].hash==h && table[x].key==ky) return x;
193       p=(p<<2)+p+b+1;
194       b>>=BSHIFT;
195       }
196     }
197   return -1;
198   }
199 
200 
201 // Return reference to string assocated with key
at(const FXchar * ky)202 FXStringDictionary& FXSettings::at(const FXchar* ky){
203   FXuval p,b,h,x;
204   if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
205   p=b=h=FXString::hash(ky);
206   FXASSERT(h);
207   while(table[x=p&(no()-1)].hash){
208     if(table[x].hash==h && table[x].key==ky) goto x;   // Return existing slot
209     p=(p<<2)+p+b+1;
210     b>>=BSHIFT;
211     }
212   if(__unlikely(free()<=1+(no()>>2)) && __unlikely(!resize(no()<<1))){ throw FXMemoryException("FXSettings::at: out of memory\n"); }
213   p=b=h;
214   while(table[x=p&(no()-1)].hash){
215     if(table[x].key.empty()) goto y;                    // Return voided slot
216     p=(p<<2)+p+b+1;
217     b>>=BSHIFT;
218     }
219   free(free()-1);                                       // Put into empty slot
220 y:used(used()+1);
221   table[x].key=ky;
222   table[x].hash=(FXuint)h;
223 x:modified=true;                                        // Assume its to be written
224   return table[x].data;
225   }
226 
227 
228 // Return constant reference to string assocated with key
at(const FXchar * ky) const229 const FXStringDictionary& FXSettings::at(const FXchar* ky) const {
230   if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
231   if(__likely(!empty())){
232     FXuval p,b,x,h;
233     p=b=h=FXString::hash(ky);
234     FXASSERT(h);
235     while(table[x=p&(no()-1)].hash){
236       if(table[x].hash==h && table[x].key==ky) return table[x].data;
237       p=(p<<2)+p+b+1;
238       b>>=BSHIFT;
239       }
240     }
241   return EMPTY[0].data;
242   }
243 
244 
245 // Parse filename
parseFile(const FXString & filename,FXbool mrk)246 FXbool FXSettings::parseFile(const FXString& filename,FXbool mrk){
247   FXTRACE((100,"FXSettings::parseFile(%s,%d)\n",filename.text(),mrk));
248   FXFile file(filename,FXFile::Reading);
249   if(file.isOpen()){
250     FXString string;
251     string.length(file.size());
252     if(file.readBlock(string.text(),string.length())==string.length()){
253       return parse(string,mrk);
254       }
255     }
256   return false;
257   }
258 
259 
260 // Unparse registry file
unparseFile(const FXString & filename)261 FXbool FXSettings::unparseFile(const FXString& filename){
262   FXTRACE((100,"FXSettings::unparseFile(%s)\n",filename.text()));
263   FXFile file(filename,FXFile::Writing);
264   if(file.isOpen()){
265     FXString string;
266     if(unparse(string)){
267       return file.writeBlock(string.text(),string.length())==string.length();
268       }
269     }
270   return false;
271   }
272 
273 
274 // Parse single string to populate settings
parse(const FXString & str,FXbool mrk)275 FXbool FXSettings::parse(const FXString& str,FXbool mrk){
276   FXint lineno=1,p=0,b,e;
277   FXString section,name,value;
278 
279   // Skip over byte-order mark
280   if(str[p]=='\xef' && str[p+1]=='\xbb' && str[p+2]=='\xbf') p+=3;
281 
282   // Parse one line at a time
283   while(str[p]){
284 
285     // Skip leading blanks
286     while(Ascii::isBlank(str[p])) ++p;
287 
288     // Parse section name
289     if(str[p]=='['){
290 
291       b=++p;
292 
293       // Scan over section name
294       while(str[p] && str[p]!=']' && str[p]!='\r' && str[p]!='\n'){
295         if(Ascii::isControl(str[p])){ fxwarning("%d: control character in section name.\n",lineno); goto nxt; }
296         ++p;
297         }
298 
299       // Check errors
300       if(str[p]!=']'){ fxwarning("%d: expected ']' to close section name.\n",lineno); goto nxt; }
301 
302       e=p++;
303 
304       // Grab name
305       section=str.mid(b,e-b);
306       }
307 
308     // Parse name-value pair
309     else if(str[p]!='#' && str[p]!=';' && str[p]!='\r' && str[p]!='\n'){
310 
311       // Should have seen section prior to this
312       if(section.empty()){ fxwarning("%d: entry should follow a section.\n",lineno); goto nxt; }
313 
314       b=p;
315 
316       // Scan key name
317       while(str[p] && str[p]!='=' && str[p]!='\r' && str[p]!='\n'){
318         if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry name.\n",lineno); goto nxt; }
319         ++p;
320         }
321 
322       // Check errors
323       if(str[p]!='='){ fxwarning("%d: expected '=' to follow entry name.\n",lineno); goto nxt; }
324 
325       e=p++;
326 
327       // Remove trailing spaces after name
328       while(b<e && Ascii::isBlank(str[e-1])) e--;
329 
330       // Grab name
331       name=str.mid(b,e-b);
332 
333       // Skip leading spaces
334       while(Ascii::isBlank(str[p])) p++;
335 
336       // Mark value
337       b=p;
338 
339       // Scan value
340       while(str[p] && str[p]!='\n' && str[p]!='\r'){
341         if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry value.\n",lineno); goto nxt; }
342         ++p;
343         }
344 
345       e=p;
346 
347       // Remove trailing spaces after value
348       while(b<e && Ascii::isBlank(str[e-1])) e--;
349 
350       // Grab the unescaped value
351       value=FXString::unescape(str.mid(b,e-b),'"','"');
352 
353       // Add entry to current section
354       at(section).at(name,mrk)=value;
355       }
356 
357     // Skip to end of line
358 nxt:while(str[p]){
359       if(str[p++]=='\n'){ lineno++; break; }
360       }
361     }
362   return true;
363   }
364 
365 
366 // Unparse settings to a single string
unparse(FXString & str) const367 FXbool FXSettings::unparse(FXString& str) const {
368   FXival sec,ent,flg;
369 
370   // Clear output
371   str.clear();
372 
373   // Any keys at all?
374   if(!empty()){
375 
376     // Loop over all sections
377     for(sec=0; sec<no(); ++sec){
378 
379       // Get group, if any
380       if(!empty(sec)){
381 
382         // Loop over all entries
383         for(ent=flg=0; ent<data(sec).no(); ++ent){
384 
385           // Is key-value pair marked?
386           if(!data(sec).empty(ent) && data(sec).mark(ent)){
387 
388             // Write section name if not written yet
389             if(!flg){
390               str.append("[");
391               str.append(key(sec));      // FIXME should escape group
392               str.append("]" ENDLINE);
393               flg=1;
394               }
395 
396             // Write marked key-value pairs only
397             str.append(data(sec).key(ent));
398             str.append("=");
399             if(FXString::shouldEscape(data(sec).data(ent),'"','"')){
400               str.append(FXString::escape(data(sec).data(ent),'"','"'));
401               }
402             else{
403               str.append(data(sec).data(ent));
404               }
405             str.append(ENDLINE);
406             }
407           }
408 
409         // Blank line after end
410         if(flg){
411           str.append(ENDLINE);
412           }
413         }
414       }
415     }
416   return true;
417   }
418 
419 
420 // Read a formatted registry entry
readFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...) const421 FXint FXSettings::readFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...) const {
422   const FXString& value=at(section).at(name);
423   FXint result=0;
424   if(!value.empty()){
425     va_list args;
426     va_start(args,fmt);
427     result=value.vscan(fmt,args);
428     va_end(args);
429     }
430   return result;
431   }
432 
433 
434 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...) const435 FXint FXSettings::readFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...) const {
436   const FXString& value=at(section).at(name);
437   FXint result=0;
438   if(!value.empty()){
439     va_list args;
440     va_start(args,fmt);
441     result=value.vscan(fmt,args);
442     va_end(args);
443     }
444   return result;
445   }
446 
447 
448 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...) const449 FXint FXSettings::readFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...) const {
450   const FXString& value=at(section).at(name);
451   FXint result=0;
452   if(!value.empty()){
453     va_list args;
454     va_start(args,fmt);
455     result=value.vscan(fmt,args);
456     va_end(args);
457     }
458   return result;
459   }
460 
461 
462 // Write a formatted registry entry
writeFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...)463 FXint FXSettings::writeFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...){
464   FXint result;
465   va_list args;
466   va_start(args,fmt);
467   result=at(section).at(name,true).vformat(fmt,args);
468   va_end(args);
469   return result;
470   }
471 
472 
473 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...)474 FXint FXSettings::writeFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...){
475   FXint result;
476   va_list args;
477   va_start(args,fmt);
478   result=at(section).at(name,true).vformat(fmt,args);
479   va_end(args);
480   return result;
481   }
482 
483 
484 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...)485 FXint FXSettings::writeFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...){
486   FXint result;
487   va_list args;
488   va_start(args,fmt);
489   result=at(section).at(name,true).vformat(fmt,args);
490   va_end(args);
491   return result;
492   }
493 
494 
495 // Read a string-valued registry entry
readStringEntry(const FXchar * section,const FXchar * name,const FXchar * def) const496 const FXchar* FXSettings::readStringEntry(const FXchar* section,const FXchar* name,const FXchar* def) const {
497   const FXStringDictionary& dict=at(section);
498   FXival slot=dict.find(name);
499   if(0<=slot){
500     return dict.data(slot).text();      // Use value at slot even if value was empty string!
501     }
502   return def;
503   }
504 
505 
506 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXchar * name,const FXchar * def) const507 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXchar* name,const FXchar* def) const {
508   return readStringEntry(section.text(),name,def);
509   }
510 
511 
512 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXString & name,const FXchar * def) const513 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXString& name,const FXchar* def) const {
514   return readStringEntry(section.text(),name.text(),def);
515   }
516 
517 
518 // Write a string-valued registry entry
writeStringEntry(const FXchar * section,const FXchar * name,const FXchar * val)519 FXbool FXSettings::writeStringEntry(const FXchar* section,const FXchar* name,const FXchar* val){
520   at(section).at(name,true)=val;
521   return true;
522   }
523 
524 
525 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXchar * name,const FXchar * val)526 FXbool FXSettings::writeStringEntry(const FXString& section,const FXchar* name,const FXchar* val){
527   return writeStringEntry(section.text(),name,val);
528   }
529 
530 
531 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXString & name,const FXchar * val)532 FXbool FXSettings::writeStringEntry(const FXString& section,const FXString& name,const FXchar* val){
533   return writeStringEntry(section.text(),name.text(),val);
534   }
535 
536 
537 // Read a int-valued registry entry
readIntEntry(const FXchar * section,const FXchar * name,FXint def) const538 FXint FXSettings::readIntEntry(const FXchar* section,const FXchar* name,FXint def) const {
539   const FXString& value=at(section).at(name);
540   if(!value.empty()){
541     FXint result;
542     FXbool ok;
543     result=value.toInt(10,&ok);
544     if(ok) return result;
545     }
546   return def;
547   }
548 
549 
550 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXchar * name,FXint def) const551 FXint FXSettings::readIntEntry(const FXString& section,const FXchar* name,FXint def) const {
552   return readIntEntry(section.text(),name,def);
553   }
554 
555 
556 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXString & name,FXint def) const557 FXint FXSettings::readIntEntry(const FXString& section,const FXString& name,FXint def) const {
558   return readIntEntry(section.text(),name.text(),def);
559   }
560 
561 
562 // Write a int-valued registry entry
writeIntEntry(const FXchar * section,const FXchar * name,FXint val)563 FXbool FXSettings::writeIntEntry(const FXchar* section,const FXchar* name,FXint val){
564   at(section).at(name,true).fromInt(val);
565   return true;
566   }
567 
568 
569 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXchar * name,FXint val)570 FXbool FXSettings::writeIntEntry(const FXString& section,const FXchar* name,FXint val){
571   return writeIntEntry(section.text(),name,val);
572   }
573 
574 
575 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXString & name,FXint val)576 FXbool FXSettings::writeIntEntry(const FXString& section,const FXString& name,FXint val){
577   return writeIntEntry(section.text(),name.text(),val);
578   }
579 
580 
581 // Read a unsigned int-valued registry entry
readUIntEntry(const FXchar * section,const FXchar * name,FXuint def) const582 FXuint FXSettings::readUIntEntry(const FXchar* section,const FXchar* name,FXuint def) const {
583   const FXString& value=at(section).at(name);
584   if(!value.empty()){
585     FXuint result;
586     FXbool ok;
587     result=value.toUInt(10,&ok);
588     if(ok) return result;
589     }
590   return def;
591   }
592 
593 
594 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXchar * name,FXuint def) const595 FXuint FXSettings::readUIntEntry(const FXString& section,const FXchar* name,FXuint def) const {
596   return readUIntEntry(section.text(),name,def);
597   }
598 
599 
600 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXString & name,FXuint def) const601 FXuint FXSettings::readUIntEntry(const FXString& section,const FXString& name,FXuint def) const {
602   return readUIntEntry(section.text(),name.text(),def);
603   }
604 
605 
606 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXchar * section,const FXchar * name,FXuint val)607 FXbool FXSettings::writeUIntEntry(const FXchar* section,const FXchar* name,FXuint val){
608   at(section).at(name,true).fromUInt(val);
609   return true;
610   }
611 
612 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXchar * name,FXuint val)613 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXchar* name,FXuint val){
614   return writeUIntEntry(section.text(),name,val);
615   }
616 
617 
618 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXString & name,FXuint val)619 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXString& name,FXuint val){
620   return writeUIntEntry(section.text(),name.text(),val);
621   }
622 
623 
624 // Read a 64-bit long integer registry entry
readLongEntry(const FXchar * section,const FXchar * name,FXlong def) const625 FXlong FXSettings::readLongEntry(const FXchar* section,const FXchar* name,FXlong def) const {
626   const FXString& value=at(section).at(name);
627   if(!value.empty()){
628     FXlong result;
629     FXbool ok;
630     result=value.toLong(10,&ok);
631     if(ok) return result;
632     }
633   return def;
634   }
635 
636 
637 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXchar * name,FXlong def) const638 FXlong FXSettings::readLongEntry(const FXString& section,const FXchar* name,FXlong def) const {
639   return readLongEntry(section.text(),name,def);
640   }
641 
642 
643 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXString & name,FXlong def) const644 FXlong FXSettings::readLongEntry(const FXString& section,const FXString& name,FXlong def) const {
645   return readLongEntry(section.text(),name.text(),def);
646   }
647 
648 
649 // Write a 64-bit long integer registry entry
writeLongEntry(const FXchar * section,const FXchar * name,FXlong val)650 FXbool FXSettings::writeLongEntry(const FXchar* section,const FXchar* name,FXlong val){
651   at(section).at(name,true).fromLong(val);
652   return true;
653   }
654 
655 
656 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXchar * name,FXlong val)657 FXbool FXSettings::writeLongEntry(const FXString& section,const FXchar* name,FXlong val){
658   return writeLongEntry(section.text(),name,val);
659   }
660 
661 
662 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXString & name,FXlong val)663 FXbool FXSettings::writeLongEntry(const FXString& section,const FXString& name,FXlong val){
664   return writeLongEntry(section.text(),name.text(),val);
665   }
666 
667 
668 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXchar * section,const FXchar * name,FXulong def) const669 FXulong FXSettings::readULongEntry(const FXchar* section,const FXchar* name,FXulong def) const {
670   const FXString& value=at(section).at(name);
671   if(!value.empty()){
672     FXulong result;
673     FXbool ok;
674     result=value.toULong(10,&ok);
675     if(ok) return result;
676     }
677   return def;
678   }
679 
680 
681 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXchar * name,FXulong def) const682 FXulong FXSettings::readULongEntry(const FXString& section,const FXchar* name,FXulong def) const {
683   return readULongEntry(section.text(),name,def);
684   }
685 
686 
687 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXString & name,FXulong def) const688 FXulong FXSettings::readULongEntry(const FXString& section,const FXString& name,FXulong def) const {
689   return readULongEntry(section.text(),name.text(),def);
690   }
691 
692 
693 // Write a 64-bit long integer registry entry
writeULongEntry(const FXchar * section,const FXchar * name,FXulong val)694 FXbool FXSettings::writeULongEntry(const FXchar* section,const FXchar* name,FXulong val){
695   at(section).at(name,true).fromULong(val);
696   return true;
697   }
698 
699 
700 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXchar * name,FXulong val)701 FXbool FXSettings::writeULongEntry(const FXString& section,const FXchar* name,FXulong val){
702   return writeULongEntry(section.text(),name,val);
703   }
704 
705 
706 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXString & name,FXulong val)707 FXbool FXSettings::writeULongEntry(const FXString& section,const FXString& name,FXulong val){
708   return writeULongEntry(section.text(),name.text(),val);
709   }
710 
711 
712 // Read a double-valued registry entry
readRealEntry(const FXchar * section,const FXchar * name,FXdouble def) const713 FXdouble FXSettings::readRealEntry(const FXchar* section,const FXchar* name,FXdouble def) const {
714   const FXString& value=at(section).at(name);
715   if(!value.empty()){
716     FXdouble result;
717     FXbool ok;
718     result=value.toDouble(&ok);
719     if(ok) return result;
720     }
721   return def;
722   }
723 
724 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXchar * name,FXdouble def) const725 FXdouble FXSettings::readRealEntry(const FXString& section,const FXchar* name,FXdouble def) const {
726   return readRealEntry(section.text(),name,def);
727   }
728 
729 
730 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXString & name,FXdouble def) const731 FXdouble FXSettings::readRealEntry(const FXString& section,const FXString& name,FXdouble def) const {
732   return readRealEntry(section.text(),name.text(),def);
733   }
734 
735 
736 // Write a double-valued registry entry
writeRealEntry(const FXchar * section,const FXchar * name,FXdouble val)737 FXbool FXSettings::writeRealEntry(const FXchar* section,const FXchar* name,FXdouble val){
738   at(section).at(name,true).fromDouble(val,16,2);
739   return true;
740   }
741 
742 
743 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXchar * name,FXdouble val)744 FXbool FXSettings::writeRealEntry(const FXString& section,const FXchar* name,FXdouble val){
745   return writeRealEntry(section.text(),name,val);
746   }
747 
748 
749 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXString & name,FXdouble val)750 FXbool FXSettings::writeRealEntry(const FXString& section,const FXString& name,FXdouble val){
751   return writeRealEntry(section.text(),name.text(),val);
752   }
753 
754 
755 // Read a color registry entry
readColorEntry(const FXchar * section,const FXchar * name,FXColor def) const756 FXColor FXSettings::readColorEntry(const FXchar* section,const FXchar* name,FXColor def) const {
757   const FXString& value=at(section).at(name);
758   if(!value.empty()){
759     return colorFromName(value);
760     }
761   return def;
762   }
763 
764 
765 // Read a color registry entry
readColorEntry(const FXString & section,const FXchar * name,FXColor def) const766 FXColor FXSettings::readColorEntry(const FXString& section,const FXchar* name,FXColor def) const {
767   return readColorEntry(section.text(),name,def);
768   }
769 
770 // Read a color registry entry
readColorEntry(const FXString & section,const FXString & name,FXColor def) const771 FXColor FXSettings::readColorEntry(const FXString& section,const FXString& name,FXColor def) const {
772   return readColorEntry(section.text(),name.text(),def);
773   }
774 
775 
776 // Write a color registry entry
writeColorEntry(const FXchar * section,const FXchar * name,FXColor val)777 FXbool FXSettings::writeColorEntry(const FXchar* section,const FXchar* name,FXColor val){
778   at(section).at(name,true)=nameFromColor(val);
779   return true;
780   }
781 
782 
783 // Write a color registry entry
writeColorEntry(const FXString & section,const FXchar * name,FXColor val)784 FXbool FXSettings::writeColorEntry(const FXString& section,const FXchar* name,FXColor val){
785   return writeColorEntry(section.text(),name,val);
786   }
787 
788 
789 // Write a color registry entry
writeColorEntry(const FXString & section,const FXString & name,FXColor val)790 FXbool FXSettings::writeColorEntry(const FXString& section,const FXString& name,FXColor val){
791   return writeColorEntry(section.text(),name.text(),val);
792   }
793 
794 
795 // Read a boolean registry entry
readBoolEntry(const FXchar * section,const FXchar * name,FXbool def) const796 FXbool FXSettings::readBoolEntry(const FXchar* section,const FXchar* name,FXbool def) const {
797   const FXString& value=at(section).at(name);
798   if(!value.empty()){
799     if(comparecase(value,"true")==0) return true;
800     else if(comparecase(value,"false")==0) return false;
801     else if(comparecase(value,"yes")==0) return true;
802     else if(comparecase(value,"no")==0) return false;
803     else if(comparecase(value,"on")==0) return true;
804     else if(comparecase(value,"off")==0) return false;
805     else if(comparecase(value,"1")==0) return true;
806     else if(comparecase(value,"0")==0) return false;
807     }
808   return def;
809   }
810 
811 
812 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXchar * name,FXbool def) const813 FXbool FXSettings::readBoolEntry(const FXString& section,const FXchar* name,FXbool def) const {
814   return readBoolEntry(section.text(),name,def);
815   }
816 
817 
818 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXString & name,FXbool def) const819 FXbool FXSettings::readBoolEntry(const FXString& section,const FXString& name,FXbool def) const {
820   return readBoolEntry(section.text(),name.text(),def);
821   }
822 
823 
824 // Write a boolean registry entry
writeBoolEntry(const FXchar * section,const FXchar * name,FXbool val)825 FXbool FXSettings::writeBoolEntry(const FXchar* section,const FXchar* name,FXbool val){
826   at(section).at(name,true)=val?"true":"false";
827   return true;
828   }
829 
830 
831 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXchar * name,FXbool val)832 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXchar* name,FXbool val){
833   return writeBoolEntry(section.text(),name,val);
834   }
835 
836 
837 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXString & name,FXbool val)838 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXString& name,FXbool val){
839   return writeBoolEntry(section.text(),name.text(),val);
840   }
841 
842 
843 // See if entry exists
existingEntry(const FXchar * section,const FXchar * name) const844 FXbool FXSettings::existingEntry(const FXchar* section,const FXchar* name) const {
845   return 0<=at(section).find(name);
846   }
847 
848 
849 // See if entry exists
existingEntry(const FXString & section,const FXchar * name) const850 FXbool FXSettings::existingEntry(const FXString& section,const FXchar* name) const {
851   return existingEntry(section.text(),name);
852   }
853 
854 
855 // See if entry exists
existingEntry(const FXString & section,const FXString & name) const856 FXbool FXSettings::existingEntry(const FXString& section,const FXString& name) const {
857   return existingEntry(section.text(),name.text());
858   }
859 
860 
861 // See if section exists
existingSection(const FXchar * section) const862 FXbool FXSettings::existingSection(const FXchar* section) const {
863   return 0<=find(section);
864   }
865 
866 
867 // See if section exists
existingSection(const FXString & section) const868 FXbool FXSettings::existingSection(const FXString& section) const {
869   return existingSection(section.text());
870   }
871 
872 
873 // Delete a registry entry
deleteEntry(const FXchar * section,const FXchar * name)874 void FXSettings::deleteEntry(const FXchar* section,const FXchar* name){
875   at(section).remove(name);
876   }
877 
878 
879 // Delete a registry entry
deleteEntry(const FXString & section,const FXchar * name)880 void FXSettings::deleteEntry(const FXString& section,const FXchar* name){
881   deleteEntry(section.text(),name);
882   }
883 
884 
885 // Delete a registry entry
deleteEntry(const FXString & section,const FXString & name)886 void FXSettings::deleteEntry(const FXString& section,const FXString& name){
887   deleteEntry(section.text(),name.text());
888   }
889 
890 
891 // Delete section
deleteSection(const FXchar * section)892 void FXSettings::deleteSection(const FXchar* section){
893   if(__unlikely(!section || !*section)){ throw FXRangeException("FXSettings::deleteSection: null or empty key\n"); }
894   if(__likely(!empty())){
895     FXuval p,b,h,x;
896     p=b=h=FXString::hash(section);
897     FXASSERT(h);
898     while(table[x=p&(no()-1)].hash!=h || table[x].key!=section){
899       if(!table[x].hash) return;
900       p=(p<<2)+p+b+1;
901       b>>=BSHIFT;
902       }
903     table[x].key.clear();                                 // Void the slot (not empty!)
904     table[x].data.clear();
905     used(used()-1);
906     modified=true;
907     if(__unlikely(used()<=(no()>>2))) resize(no()>>1);
908     }
909   }
910 
911 
912 // Delete section
deleteSection(const FXString & section)913 void FXSettings::deleteSection(const FXString& section){
914   deleteSection(section.text());
915   }
916 
917 
918 // Clear all sections
clear()919 void FXSettings::clear(){
920   modified=true;
921   no(1);
922   }
923 
924 
925 // Clean up
~FXSettings()926 FXSettings::~FXSettings(){
927   clear();
928   }
929 
930 }
931