1 /********************************************************************************
2 *                                                                               *
3 *                           S e t t i n g s   C l a s s                         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (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 GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXSettings.cpp,v 1.54.2.1 2008/07/28 18:24:09 fox Exp $                      *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxascii.h"
28 #include "FXHash.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXStringDict.h"
32 #include "FXFile.h"
33 #include "FXSettings.h"
34 
35 /*
36   Notes:
37 
38   - Format for settings database file:
39 
40     [Section Key]
41     EntryKey=string-with-no-spaces
42     EntryKey="string\nwith a\nnewline in it\n"
43     EntryKey=" string with leading and trailing spaces and \"embedded\" in it  "
44     EntryKey=string with no leading or trailing spaces
45 
46   - EntryKey may is of the form "ali baba", "ali-baba", "ali_baba", or "ali.baba".
47 
48   - Leading/trailing spaces are NOT part of the EntryKey.
49 
50   - FXSectionDict should go; FXSettings should simply derive from FXDict.
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 MAXBUFFER 2000
62 #define MAXNAME   200
63 #define MAXVALUE  2000
64 
65 using namespace FX;
66 
67 /*******************************************************************************/
68 
69 namespace FX {
70 
71 // Object implementation
72 FXIMPLEMENT(FXSettings,FXDict,NULL,0)
73 
74 
75 // Construct settings database
FXSettings()76 FXSettings::FXSettings(){
77   modified=false;
78   }
79 
80 
81 // Construct copy of existing database
FXSettings(const FXSettings & orig)82 FXSettings::FXSettings(const FXSettings& orig):FXDict(orig){
83   register FXint i;
84   modified=orig.modified;
85   for(i=0; i<orig.total; i++){
86     if(0<=dict[i].hash){
87       dict[i].data=new FXStringDict(*((FXStringDict*)orig.dict[i].data));
88       }
89     }
90   }
91 
92 
93 // Assignment operator
operator =(const FXSettings & orig)94 FXSettings& FXSettings::operator=(const FXSettings& orig){
95   register FXint i;
96   if(&orig!=this){
97     FXDict::operator=(orig);
98     for(i=0; i<orig.total; i++){
99       if(0<=orig.dict[i].hash){
100         dict[i].data=new FXStringDict(*((FXStringDict*)orig.dict[i].data));
101         }
102       }
103     }
104   return *this;
105   }
106 
107 
108 // Create data
createData(const void *)109 void *FXSettings::createData(const void*){
110   return new FXStringDict;
111   }
112 
113 
114 // Delete data
deleteData(void * ptr)115 void FXSettings::deleteData(void* ptr){
116   delete ((FXStringDict*)ptr);
117   }
118 
119 
120 // Read string
readString(FXFile & file,FXchar * buffer,FXint & bol,FXint & eol,FXint & end)121 static bool readString(FXFile& file,FXchar *buffer,FXint& bol,FXint& eol,FXint& end){
122   register FXint n;
123   do{
124     if(eol>=end){
125       if(bol<end){ memmove(buffer,buffer+bol,end-bol); }
126       end=end-bol;
127       bol=0;
128       eol=end;
129       n=file.readBlock(buffer+end,MAXBUFFER-end);
130       if(n<0) return false;
131       end+=n;
132       }
133     }
134   while(eol<end && buffer[eol++]!='\n');
135   return bol<eol;
136   }
137 
138 
139 // Parse filename
parseFile(const FXString & filename,bool mark)140 bool FXSettings::parseFile(const FXString& filename,bool mark){
141   FXFile file(filename,FXIO::Reading);
142   if(file.isOpen()){
143     FXchar line[MAXBUFFER];
144     FXint bol,eol,end,section,name,value,p,lineno;
145     FXStringDict *group=NULL;
146 
147     lineno=bol=eol=end=0;
148 
149     // Read lines
150     while(readString(file,line,bol,eol,end)){
151       lineno++;
152 
153       // Skip leading spaces
154       while(bol<eol && Ascii::isBlank(line[bol])) bol++;
155 
156       // Skip comment lines and empty lines
157       if(bol>=eol || line[bol]=='#' || line[bol]==';' || line[bol]=='\n' || line[bol]=='\r') goto next;
158 
159       // Parse section name
160       if(line[bol]=='['){
161 
162         // Scan section name
163         for(section=++bol; bol<eol && line[bol]!=']' && !Ascii::isControl(line[bol]); bol++);
164 
165         // Check errors
166         if(bol>=eol || line[bol]!=']'){ fxwarning("%s:%d: illegal section name.\n",filename.text(),lineno); goto next; }
167 
168         // Terminate name
169         line[bol]='\0';
170 
171         // Add new section dict
172         group=insert(line+section);
173         }
174 
175       // Parse name-value pair
176       else{
177 
178         // Should have a group
179         if(!group){ fxwarning("%s:%d: settings entry should follow a section.\n",filename.text(),lineno); goto next; }
180 
181         // Scan key name
182         for(name=bol; bol<eol && line[bol]!='=' && !Ascii::isControl(line[bol]); bol++);
183 
184         // Check errors
185         if(bol>=eol || line[bol]!='='){ fxwarning("%s:%d: expected '=' to follow key.\n",filename.text(),lineno); goto next; }
186 
187         // Remove trailing spaces after name
188         for(p=bol; name<p && Ascii::isBlank(line[p-1]); p--);
189 
190         // Terminate name
191         line[p]='\0';
192 
193         // Skip leading spaces
194         for(bol++; bol<eol && Ascii::isBlank(line[bol]); bol++);
195 
196         // Scan value
197         for(value=bol; bol<eol && !Ascii::isControl(line[bol]); bol++);
198 
199         // Remove trailing spaces after value
200         for(p=bol; value<p && Ascii::isBlank(line[p-1]); p--);
201 
202         // Terminate value
203         line[p]='\0';
204 
205         // Add entry to current section
206         group->replace(line+name,dequote(line+value),mark);
207         }
208 next: bol=eol;
209       }
210 
211     // Done
212     return true;
213     }
214   return false;
215   }
216 
217 
218 // Write string
writeString(FXFile & file,const FXchar * string)219 static bool writeString(FXFile& file,const FXchar* string){
220   register FXint len=strlen(string);
221   return file.writeBlock(string,len)==len;
222   }
223 
224 
225 // Unparse registry file
unparseFile(const FXString & filename)226 bool FXSettings::unparseFile(const FXString& filename){
227   FXFile file(filename,FXIO::Writing);
228   FXchar line[MAXVALUE];
229   if(file.isOpen()){
230 
231     // Loop over all sections
232     for(FXint s=first(); s<size(); s=next(s)){
233 
234       // Get group
235       FXStringDict* group=data(s);
236       bool sec=false;
237 
238       // Loop over all entries
239       for(FXint e=group->first(); e<group->size(); e=group->next(e)){
240 
241         // Is key-value pair marked?
242         if(group->mark(e)){
243 
244           // Write section name if not written yet
245           if(!sec){
246             if(!writeString(file,"[")) goto x;
247             if(!writeString(file,key(s))) goto x;
248             if(!writeString(file,"]" ENDLINE)) goto x;
249             sec=true;
250             }
251 
252           // Write marked key-value pairs only
253           if(!writeString(file,group->key(e))) goto x;
254           if(!writeString(file,"=")) goto x;
255           if(!writeString(file,enquote(line,group->data(e)))) goto x;
256           if(!writeString(file,ENDLINE)) goto x;
257           }
258         }
259 
260       // Blank line after end
261       if(sec){
262         if(!writeString(file,ENDLINE)) goto x;
263         }
264       }
265     return true;
266     }
267 x:return false;
268   }
269 
270 
271 // Dequote a value, in situ
dequote(FXchar * text) const272 FXchar* FXSettings::dequote(FXchar* text) const {
273   register FXchar *result=text;
274   register FXchar *ptr=text;
275   register FXuint v;
276   if(*text=='"'){
277     text++;
278     while((v=*text++)!='\0' && v!='\n' && v!='"'){
279       if(v=='\\'){
280         v=*text++;
281         switch(v){
282           case 'n':
283             v='\n';
284             break;
285           case 'r':
286             v='\r';
287             break;
288           case 'b':
289             v='\b';
290             break;
291           case 'v':
292             v='\v';
293             break;
294           case 'a':
295             v='\a';
296             break;
297           case 'f':
298             v='\f';
299             break;
300           case 't':
301             v='\t';
302             break;
303           case '\\':
304             v='\\';
305             break;
306           case '"':
307             v='"';
308             break;
309           case '\'':
310             v='\'';
311             break;
312           case 'x':
313             v='x';
314             if(Ascii::isHexDigit(*text)){
315               v=Ascii::digitValue(*text++);
316               if(Ascii::isHexDigit(*text)){
317                 v=(v<<4)+Ascii::digitValue(*text++);
318                 }
319               }
320             break;
321           case '0':
322           case '1':
323           case '2':
324           case '3':
325           case '4':
326           case '5':
327           case '6':
328           case '7':
329             v=v-'0';
330             if('0'<=*text && *text<='7'){
331               v=(v<<3)+*text++-'0';
332               if('0'<=*text && *text<='7'){
333                 v=(v<<3)+*text++-'0';
334                 }
335               }
336             break;
337           }
338         }
339       *ptr++=v;
340       }
341     *ptr='\0';
342     }
343   return result;
344   }
345 
346 
347 // Check if quotes are needed
needquotes(const FXchar * text)348 static bool needquotes(const FXchar* text){
349   register const FXchar *ptr=text;
350   register FXuchar c;
351   while((c=*ptr++)!='\0'){
352     if(0x7f<=c || c<0x20 || c=='"' || c=='\'' || c=='\\' || (c==' ' && (ptr==(text+1) || *ptr=='\0'))) return true;
353     }
354   return false;
355   }
356 
357 
358 // Enquote a value
enquote(FXchar * result,const FXchar * text)359 FXchar* FXSettings::enquote(FXchar* result,const FXchar* text){
360   register FXchar *end=result+MAXVALUE-6;
361   register FXchar *ptr=result;
362   register FXuchar c;
363   if(needquotes(text)){
364     *ptr++='"';
365     while((c=*text++)!='\0' && ptr<end){
366       switch(c){
367         case '\n':
368           *ptr++='\\';
369           *ptr++='n';
370           break;
371         case '\r':
372           *ptr++='\\';
373           *ptr++='r';
374           break;
375         case '\b':
376           *ptr++='\\';
377           *ptr++='b';
378           break;
379         case '\v':
380           *ptr++='\\';
381           *ptr++='v';
382           break;
383         case '\a':
384           *ptr++='\\';
385           *ptr++='a';
386           break;
387         case '\f':
388           *ptr++='\\';
389           *ptr++='f';
390           break;
391         case '\t':
392           *ptr++='\\';
393           *ptr++='t';
394           break;
395         case '\\':
396           *ptr++='\\';
397           *ptr++='\\';
398           break;
399         case '"':
400           *ptr++='\\';
401           *ptr++='"';
402           break;
403         case '\'':
404           *ptr++='\\';
405           *ptr++='\'';
406           break;
407         default:
408           if(c<0x20 || 0x7f<c){
409             *ptr++='\\';
410             *ptr++='x';
411             *ptr++=FXString::HEX[c>>4];
412             *ptr++=FXString::HEX[c&15];
413             }
414           else{
415             *ptr++=c;
416             }
417           break;
418         }
419       }
420     *ptr++='"';
421     }
422   else{
423     while((c=*text++)!='\0' && ptr<end){
424       *ptr++=c;
425       }
426     }
427   *ptr='\0';
428   return result;
429   }
430 
431 
432 // Furnish our own version if we have to
433 #ifndef HAVE_VSSCANF
434 extern "C" int vsscanf(const char* str, const char* format, va_list arg_ptr);
435 #endif
436 
437 
438 // Read a formatted registry entry
readFormatEntry(const FXchar * section,const FXchar * key,const FXchar * fmt,...)439 FXint FXSettings::readFormatEntry(const FXchar *section,const FXchar *key,const FXchar *fmt,...){
440   if(!section || !section[0]){ fxerror("FXSettings::readFormatEntry: bad section argument.\n"); }
441   if(!key || !key[0]){ fxerror("FXSettings::readFormatEntry: bad key argument.\n"); }
442   if(!fmt){ fxerror("FXSettings::readFormatEntry: bad fmt argument.\n"); }
443   FXStringDict *group=find(section);
444   va_list args;
445   va_start(args,fmt);
446   FXint result=0;
447   if(group){
448     const char *value=group->find(key);
449     if(value){
450       result=vsscanf((char*)value,fmt,args);    // Cast needed for HP-UX 11, which has wrong prototype for vsscanf
451       }
452     }
453   va_end(args);
454   return result;
455   }
456 
457 
458 // Read a string-valued registry entry
readStringEntry(const FXchar * section,const FXchar * key,const FXchar * def)459 const FXchar *FXSettings::readStringEntry(const FXchar *section,const FXchar *key,const FXchar *def){
460   if(!section || !section[0]){ fxerror("FXSettings::readStringEntry: bad section argument.\n"); }
461   if(!key || !key[0]){ fxerror("FXSettings::readStringEntry: bad key argument.\n"); }
462   FXStringDict *group=find(section);
463   if(group){
464     const char *value=group->find(key);
465     if(value) return value;
466     }
467   return def;
468   }
469 
470 
471 // Read a int-valued registry entry
readIntEntry(const FXchar * section,const FXchar * key,FXint def)472 FXint FXSettings::readIntEntry(const FXchar *section,const FXchar *key,FXint def){
473   if(!section || !section[0]){ fxerror("FXSettings::readIntEntry: bad section argument.\n"); }
474   if(!key || !key[0]){ fxerror("FXSettings::readIntEntry: bad key argument.\n"); }
475   FXStringDict *group=find(section);
476   if(group){
477     const char *value=group->find(key);
478     if(value){
479       FXint ivalue;
480       if(value[0]=='0' && (value[1]=='x' || value[1]=='X')){
481         if(sscanf(value+2,"%x",&ivalue)) return ivalue;
482         }
483       else{
484         if(sscanf(value,"%d",&ivalue)==1) return ivalue;
485         }
486       }
487     }
488   return def;
489   }
490 
491 
492 // Read a unsigned int-valued registry entry
readUnsignedEntry(const FXchar * section,const FXchar * key,FXuint def)493 FXuint FXSettings::readUnsignedEntry(const FXchar *section,const FXchar *key,FXuint def){
494   if(!section || !section[0]){ fxerror("FXSettings::readUnsignedEntry: bad section argument.\n"); }
495   if(!key || !key[0]){ fxerror("FXSettings::readUnsignedEntry: bad key argument.\n"); }
496   FXStringDict *group=find(section);
497   if(group){
498     const char *value=group->find(key);
499     if(value){
500       FXuint ivalue;
501       if(value[0]=='0' && (value[1]=='x' || value[1]=='X')){
502         if(sscanf(value+2,"%x",&ivalue)) return ivalue;
503         }
504       else{
505         if(sscanf(value,"%u",&ivalue)==1) return ivalue;
506         }
507       }
508     }
509   return def;
510   }
511 
512 
513 // Read a double-valued registry entry
readRealEntry(const FXchar * section,const FXchar * key,FXdouble def)514 FXdouble FXSettings::readRealEntry(const FXchar *section,const FXchar *key,FXdouble def){
515   if(!section || !section[0]){ fxerror("FXSettings::readRealEntry: bad section argument.\n"); }
516   if(!key || !key[0]){ fxerror("FXSettings::readRealEntry: bad key argument.\n"); }
517   FXStringDict *group=find(section);
518   if(group){
519     const char *value=group->find(key);
520     if(value){
521       FXdouble dvalue;
522       if(sscanf(value,"%lf",&dvalue)==1) return dvalue;
523       }
524     }
525   return def;
526   }
527 
528 
529 // Read a color registry entry
readColorEntry(const FXchar * section,const FXchar * key,FXColor def)530 FXColor FXSettings::readColorEntry(const FXchar *section,const FXchar *key,FXColor def){
531   if(!section || !section[0]){ fxerror("FXSettings::readColorEntry: bad section argument.\n"); }
532   if(!key || !key[0]){ fxerror("FXSettings::readColorEntry: bad key argument.\n"); }
533   FXStringDict *group=find(section);
534   if(group){
535     const char *value=group->find(key);
536     if(value){
537       return fxcolorfromname(value);
538       }
539     }
540   return def;
541   }
542 
543 
544 // Read a boolean registry entry
readBoolEntry(const FXchar * section,const FXchar * key,FXbool def)545 FXbool FXSettings::readBoolEntry(const FXchar *section,const FXchar *key,FXbool def){
546   if(!section || !section[0]){ fxerror("FXSettings::readBoolEntry: bad section argument.\n"); }
547   if(!key || !key[0]){ fxerror("FXSettings::readBoolEntry: bad key argument.\n"); }
548   FXStringDict *group=find(section);
549   if(group){
550     const char *value=group->find(key);
551     if(value){
552       if(comparecase(value,"true")==0) return TRUE;
553       else if(comparecase(value,"false")==0) return FALSE;
554       else if(comparecase(value,"yes")==0) return TRUE;
555       else if(comparecase(value,"no")==0) return FALSE;
556       else if(comparecase(value,"on")==0) return TRUE;
557       else if(comparecase(value,"off")==0) return FALSE;
558       else if(comparecase(value,"1")==0) return TRUE;
559       else if(comparecase(value,"0")==0) return FALSE;
560       else if(comparecase(value,"maybe")==0) return MAYBE;
561       }
562     }
563   return def;
564   }
565 
566 
567 // Write a formatted registry entry
writeFormatEntry(const FXchar * section,const FXchar * key,const FXchar * fmt,...)568 FXint FXSettings::writeFormatEntry(const FXchar *section,const FXchar *key,const FXchar *fmt,...){
569   if(!section || !section[0]){ fxerror("FXSettings::writeFormatEntry: bad section argument.\n"); }
570   if(!key || !key[0]){ fxerror("FXSettings::writeFormatEntry: bad key argument.\n"); }
571   if(!fmt){ fxerror("FXSettings::writeFormatEntry: bad fmt argument.\n"); }
572   FXStringDict *group=insert(section);
573   va_list args;
574   va_start(args,fmt);
575   FXint result=0;
576   if(group){
577     FXchar buffer[2048];
578 #if defined(WIN32) || defined(HAVE_VSNPRINTF)
579     result=vsnprintf(buffer,sizeof(buffer),fmt,args);
580 #else
581     result=vsprintf(buffer,fmt,args);
582 #endif
583     group->replace(key,buffer,TRUE);
584     modified=true;
585     }
586   va_end(args);
587   return result;
588   }
589 
590 
591 // Write a string-valued registry entry
writeStringEntry(const FXchar * section,const FXchar * key,const FXchar * val)592 bool FXSettings::writeStringEntry(const FXchar *section,const FXchar *key,const FXchar *val){
593   if(!section || !section[0]){ fxerror("FXSettings::writeStringEntry: bad section argument.\n"); }
594   if(!key || !key[0]){ fxerror("FXSettings::writeStringEntry: bad key argument.\n"); }
595   FXStringDict *group=insert(section);
596   if(group){
597     group->replace(key,val,true);
598     modified=true;
599     return true;
600     }
601   return false;
602   }
603 
604 
605 // Write a int-valued registry entry
writeIntEntry(const FXchar * section,const FXchar * key,FXint val)606 bool FXSettings::writeIntEntry(const FXchar *section,const FXchar *key,FXint val){
607   if(!section || !section[0]){ fxerror("FXSettings::writeIntEntry: bad section argument.\n"); }
608   if(!key || !key[0]){ fxerror("FXSettings::writeIntEntry: bad key argument.\n"); }
609   FXStringDict *group=insert(section);
610   if(group){
611     FXchar buffer[32];
612     sprintf(buffer,"%d",val);
613     group->replace(key,buffer,true);
614     modified=true;
615     return true;
616     }
617   return false;
618   }
619 
620 
621 // Write a unsigned int-valued registry entry
writeUnsignedEntry(const FXchar * section,const FXchar * key,FXuint val)622 bool FXSettings::writeUnsignedEntry(const FXchar *section,const FXchar *key,FXuint val){
623   if(!section || !section[0]){ fxerror("FXSettings::writeUnsignedEntry: bad section argument.\n"); }
624   if(!key || !key[0]){ fxerror("FXSettings::writeUnsignedEntry: bad key argument.\n"); }
625   FXStringDict *group=insert(section);
626   if(group){
627     FXchar buffer[32];
628     sprintf(buffer,"%u",val);
629     group->replace(key,buffer,TRUE);
630     modified=true;
631     return true;
632     }
633   return false;
634   }
635 
636 
637 // Write a double-valued registry entry
writeRealEntry(const FXchar * section,const FXchar * key,FXdouble val)638 bool FXSettings::writeRealEntry(const FXchar *section,const FXchar *key,FXdouble val){
639   if(!section || !section[0]){ fxerror("FXSettings::writeRealEntry: bad section argument.\n"); }
640   if(!key || !key[0]){ fxerror("FXSettings::writeRealEntry: bad key argument.\n"); }
641   FXStringDict *group=insert(section);
642   if(group){
643     FXchar buffer[64];
644     sprintf(buffer,"%.16g",val);
645     group->replace(key,buffer,TRUE);
646     modified=true;
647     return true;
648     }
649   return false;
650   }
651 
652 
653 // Write a color registry entry
writeColorEntry(const FXchar * section,const FXchar * key,FXColor val)654 bool FXSettings::writeColorEntry(const FXchar *section,const FXchar *key,FXColor val){
655   if(!section || !section[0]){ fxerror("FXSettings::writeColorEntry: bad section argument.\n"); }
656   if(!key || !key[0]){ fxerror("FXSettings::writeColorEntry: bad key argument.\n"); }
657   FXStringDict *group=insert(section);
658   if(group){
659     FXchar buffer[64];
660     group->replace(key,fxnamefromcolor(buffer,val),TRUE);
661     modified=true;
662     return true;
663     }
664   return false;
665   }
666 
667 
668 // Write a boolean registry entry
writeBoolEntry(const FXchar * section,const FXchar * key,FXbool val)669 bool FXSettings::writeBoolEntry(const FXchar *section,const FXchar *key,FXbool val){
670   if(!section || !section[0]){ fxerror("FXSettings::writeBoolEntry: bad section argument.\n"); }
671   if(!key || !key[0]){ fxerror("FXSettings::writeBoolEntry: bad key argument.\n"); }
672   FXStringDict *group=insert(section);
673   if(group){
674     group->replace(key,(val==FALSE) ? "false" : (val==TRUE) ? "true" : "maybe",TRUE);
675     modified=true;
676     return true;
677     }
678   return false;
679   }
680 
681 
682 // Delete a registry entry
deleteEntry(const FXchar * section,const FXchar * key)683 bool FXSettings::deleteEntry(const FXchar *section,const FXchar *key){
684   if(!section || !section[0]){ fxerror("FXSettings::deleteEntry: bad section argument.\n"); }
685   if(!key || !key[0]){ fxerror("FXSettings::deleteEntry: bad key argument.\n"); }
686   FXStringDict *group=insert(section);
687   if(group){
688     group->remove(key);
689     modified=true;
690     return true;
691     }
692   return false;
693   }
694 
695 
696 // Delete section
deleteSection(const FXchar * section)697 bool FXSettings::deleteSection(const FXchar *section){
698   if(!section || !section[0]){ fxerror("FXSettings::deleteSection: bad section argument.\n"); }
699   remove(section);
700   modified=true;
701   return true;
702   }
703 
704 
705 // Clear all sections
clear()706 bool FXSettings::clear(){
707   FXDict::clear();
708   modified=true;
709   return true;
710   }
711 
712 
713 // See if section exists
existingSection(const FXchar * section)714 bool FXSettings::existingSection(const FXchar *section){
715   if(!section || !section[0]){ fxerror("FXSettings::existingSection: bad section argument.\n"); }
716   return find(section)!=NULL;
717   }
718 
719 
720 // See if entry exists
existingEntry(const FXchar * section,const FXchar * key)721 bool FXSettings::existingEntry(const FXchar *section,const FXchar *key){
722   if(!section || !section[0]){ fxerror("FXSettings::existingEntry: bad section argument.\n"); }
723   if(!key || !key[0]){ fxerror("FXSettings::existingEntry: bad key argument.\n"); }
724   FXStringDict *group=find(section);
725   return group && group->find(key)!=NULL;
726   }
727 
728 
729 // Clean up
~FXSettings()730 FXSettings::~FXSettings(){
731   clear();
732   }
733 
734 }
735