1 /*
2 ** configfile.cpp
3 ** Implements the basic .ini parsing class
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38
39 #include "doomtype.h"
40 #include "configfile.h"
41 #include "m_random.h"
42
43 #define READBUFFERSIZE 256
44
45 static FRandom pr_endtag;
46
47 //====================================================================
48 //
49 // FConfigFile Constructor
50 //
51 //====================================================================
52
FConfigFile()53 FConfigFile::FConfigFile ()
54 {
55 Sections = CurrentSection = NULL;
56 LastSectionPtr = &Sections;
57 CurrentEntry = NULL;
58 PathName = "";
59 OkayToWrite = true;
60 FileExisted = true;
61 }
62
63 //====================================================================
64 //
65 // FConfigFile Constructor
66 //
67 //====================================================================
68
FConfigFile(const char * pathname)69 FConfigFile::FConfigFile (const char *pathname)
70 {
71 Sections = CurrentSection = NULL;
72 LastSectionPtr = &Sections;
73 CurrentEntry = NULL;
74 ChangePathName (pathname);
75 LoadConfigFile ();
76 OkayToWrite = true;
77 FileExisted = true;
78 }
79
80 //====================================================================
81 //
82 // FConfigFile Copy Constructor
83 //
84 //====================================================================
85
FConfigFile(const FConfigFile & other)86 FConfigFile::FConfigFile (const FConfigFile &other)
87 {
88 Sections = CurrentSection = NULL;
89 LastSectionPtr = &Sections;
90 CurrentEntry = NULL;
91 ChangePathName (other.PathName);
92 *this = other;
93 OkayToWrite = other.OkayToWrite;
94 FileExisted = other.FileExisted;
95 }
96
97 //====================================================================
98 //
99 // FConfigFile Destructor
100 //
101 //====================================================================
102
~FConfigFile()103 FConfigFile::~FConfigFile ()
104 {
105 FConfigSection *section = Sections;
106
107 while (section != NULL)
108 {
109 FConfigSection *nextsection = section->Next;
110 FConfigEntry *entry = section->RootEntry;
111
112 while (entry != NULL)
113 {
114 FConfigEntry *nextentry = entry->Next;
115 delete[] entry->Value;
116 delete[] (char *)entry;
117 entry = nextentry;
118 }
119 delete section;
120 section = nextsection;
121 }
122 }
123
124 //====================================================================
125 //
126 // FConfigFile Copy Operator
127 //
128 //====================================================================
129
operator =(const FConfigFile & other)130 FConfigFile &FConfigFile::operator = (const FConfigFile &other)
131 {
132 FConfigSection *fromsection, *tosection;
133 FConfigEntry *fromentry;
134
135 ClearConfig ();
136 fromsection = other.Sections;
137 while (fromsection != NULL)
138 {
139 fromentry = fromsection->RootEntry;
140 tosection = NewConfigSection (fromsection->SectionName);
141 while (fromentry != NULL)
142 {
143 NewConfigEntry (tosection, fromentry->Key, fromentry->Value);
144 fromentry = fromentry->Next;
145 }
146 fromsection = fromsection->Next;
147 }
148 return *this;
149 }
150
151 //====================================================================
152 //
153 // FConfigFile :: ClearConfig
154 //
155 // Removes all sections and entries from the config file.
156 //
157 //====================================================================
158
ClearConfig()159 void FConfigFile::ClearConfig ()
160 {
161 CurrentSection = Sections;
162 while (CurrentSection != NULL)
163 {
164 FConfigSection *next = CurrentSection->Next;
165 ClearCurrentSection ();
166 delete CurrentSection;
167 CurrentSection = next;
168 }
169 Sections = NULL;
170 LastSectionPtr = &Sections;
171 }
172
173 //====================================================================
174 //
175 // FConfigFile :: ChangePathName
176 //
177 //====================================================================
178
ChangePathName(const char * pathname)179 void FConfigFile::ChangePathName (const char *pathname)
180 {
181 PathName = pathname;
182 }
183
184 //====================================================================
185 //
186 // FConfigFile :: CreateSectionAtStart
187 //
188 // Creates the section at the start of the file if it does not exist.
189 // Otherwise, simply moves the section to the start of the file.
190 //
191 //====================================================================
192
CreateSectionAtStart(const char * name)193 void FConfigFile::CreateSectionAtStart (const char *name)
194 {
195 NewConfigSection (name);
196 MoveSectionToStart (name);
197 }
198
199 //====================================================================
200 //
201 // FConfigFile :: MoveSectionToStart
202 //
203 // Moves the named section to the start of the file if it exists.
204 // Otherwise, does nothing.
205 //
206 //====================================================================
207
MoveSectionToStart(const char * name)208 void FConfigFile::MoveSectionToStart (const char *name)
209 {
210 FConfigSection *section = FindSection (name);
211
212 if (section != NULL)
213 {
214 FConfigSection **prevsec = &Sections;
215 while (*prevsec != NULL && *prevsec != section)
216 {
217 prevsec = &((*prevsec)->Next);
218 }
219 *prevsec = section->Next;
220 section->Next = Sections;
221 Sections = section;
222 if (LastSectionPtr == §ion->Next)
223 {
224 LastSectionPtr = prevsec;
225 }
226 }
227 }
228
229
230 //====================================================================
231 //
232 // FConfigFile :: SetSection
233 //
234 // Sets the current section to the named one, optionally creating it
235 // if it does not exist. Returns true if the section exists (even if
236 // it was newly created), false otherwise.
237 //
238 //====================================================================
239
SetSection(const char * name,bool allowCreate)240 bool FConfigFile::SetSection (const char *name, bool allowCreate)
241 {
242 FConfigSection *section = FindSection (name);
243 if (section == NULL && allowCreate)
244 {
245 section = NewConfigSection (name);
246 }
247 if (section != NULL)
248 {
249 CurrentSection = section;
250 CurrentEntry = section->RootEntry;
251 return true;
252 }
253 return false;
254 }
255
256 //====================================================================
257 //
258 // FConfigFile :: SetFirstSection
259 //
260 // Sets the current section to the first one in the file. Returns
261 // false if there are no sections.
262 //
263 //====================================================================
264
SetFirstSection()265 bool FConfigFile::SetFirstSection ()
266 {
267 CurrentSection = Sections;
268 if (CurrentSection != NULL)
269 {
270 CurrentEntry = CurrentSection->RootEntry;
271 return true;
272 }
273 return false;
274 }
275
276 //====================================================================
277 //
278 // FConfigFile :: SetNextSection
279 //
280 // Advances the current section to the next one in the file. Returns
281 // false if there are no more sections.
282 //
283 //====================================================================
284
SetNextSection()285 bool FConfigFile::SetNextSection ()
286 {
287 if (CurrentSection != NULL)
288 {
289 CurrentSection = CurrentSection->Next;
290 if (CurrentSection != NULL)
291 {
292 CurrentEntry = CurrentSection->RootEntry;
293 return true;
294 }
295 }
296 return false;
297 }
298
299 //====================================================================
300 //
301 // FConfigFile :: GetCurrentSection
302 //
303 // Returns the name of the current section.
304 //
305 //====================================================================
306
GetCurrentSection() const307 const char *FConfigFile::GetCurrentSection () const
308 {
309 if (CurrentSection != NULL)
310 {
311 return CurrentSection->SectionName.GetChars();
312 }
313 return NULL;
314 }
315
316 //====================================================================
317 //
318 // FConfigFile :: ClearCurrentSection
319 //
320 // Removes all entries from the current section.
321 //
322 //====================================================================
323
ClearCurrentSection()324 void FConfigFile::ClearCurrentSection ()
325 {
326 if (CurrentSection != NULL)
327 {
328 FConfigEntry *entry, *next;
329
330 entry = CurrentSection->RootEntry;
331 while (entry != NULL)
332 {
333 next = entry->Next;
334 delete[] entry->Value;
335 delete[] (char *)entry;
336 entry = next;
337 }
338 CurrentSection->RootEntry = NULL;
339 CurrentSection->LastEntryPtr = &CurrentSection->RootEntry;
340 }
341 }
342
343 //====================================================================
344 //
345 // FConfigFile :: DeleteCurrentSection
346 //
347 // Completely removes the current section. The current section is
348 // advanced to the next section. Returns true if there is still a
349 // current section.
350 //
351 //====================================================================
352
DeleteCurrentSection()353 bool FConfigFile::DeleteCurrentSection()
354 {
355 if (CurrentSection != NULL)
356 {
357 FConfigSection *sec;
358
359 ClearCurrentSection();
360
361 // Find the preceding section.
362 for (sec = Sections; sec != NULL && sec->Next != CurrentSection; sec = sec->Next)
363 { }
364
365 sec->Next = CurrentSection->Next;
366 if (LastSectionPtr == &CurrentSection->Next)
367 {
368 LastSectionPtr = &sec->Next;
369 }
370
371 delete CurrentSection;
372
373 CurrentSection = sec->Next;
374 return CurrentSection != NULL;
375 }
376 return false;
377 }
378
379 //====================================================================
380 //
381 // FConfigFile :: ClearKey
382 //
383 // Removes a key from the current section, if found. If there are
384 // duplicates, only the first is removed.
385 //
386 //====================================================================
387
ClearKey(const char * key)388 void FConfigFile::ClearKey(const char *key)
389 {
390 if (CurrentSection->RootEntry == NULL)
391 {
392 return;
393 }
394 FConfigEntry **prober = &CurrentSection->RootEntry, *probe = *prober;
395
396 while (probe != NULL && stricmp(probe->Key, key) != 0)
397 {
398 prober = &probe->Next;
399 probe = *prober;
400 }
401 if (probe != NULL)
402 {
403 *prober = probe->Next;
404 if (CurrentSection->LastEntryPtr == &probe->Next)
405 {
406 CurrentSection->LastEntryPtr = prober;
407 }
408 delete[] probe->Value;
409 delete[] (char *)probe;
410 }
411 }
412
413 //====================================================================
414 //
415 // FConfigFile :: SectionIsEmpty
416 //
417 // Returns true if the current section has no entries. If there is
418 // no current section, it is also considered empty.
419 //
420 //====================================================================
421
SectionIsEmpty()422 bool FConfigFile::SectionIsEmpty()
423 {
424 return (CurrentSection == NULL) || (CurrentSection->RootEntry == NULL);
425 }
426
427
428 //====================================================================
429 //
430 // FConfigFile :: NextInSection
431 //
432 // Provides the next key/value pair in the current section. Returns
433 // true if there was another, false otherwise.
434 //
435 //====================================================================
436
NextInSection(const char * & key,const char * & value)437 bool FConfigFile::NextInSection (const char *&key, const char *&value)
438 {
439 FConfigEntry *entry = CurrentEntry;
440
441 if (entry == NULL)
442 return false;
443
444 CurrentEntry = entry->Next;
445 key = entry->Key;
446 value = entry->Value;
447 return true;
448 }
449
450 //====================================================================
451 //
452 // FConfigFile :: GetValueForKey
453 //
454 // Returns the value for the specified key in the current section,
455 // returning NULL if the key does not exist.
456 //
457 //====================================================================
458
GetValueForKey(const char * key) const459 const char *FConfigFile::GetValueForKey (const char *key) const
460 {
461 FConfigEntry *entry = FindEntry (CurrentSection, key);
462
463 if (entry != NULL)
464 {
465 return entry->Value;
466 }
467 return NULL;
468 }
469
470 //====================================================================
471 //
472 // FConfigFile :: SetValueForKey
473 //
474 // Sets they key/value pair as specified in the current section. If
475 // duplicates are allowed, it always creates a new pair. Otherwise, it
476 // will overwrite the value of an existing key with the same name.
477 //
478 //====================================================================
479
SetValueForKey(const char * key,const char * value,bool duplicates)480 void FConfigFile::SetValueForKey (const char *key, const char *value, bool duplicates)
481 {
482 if (CurrentSection != NULL)
483 {
484 FConfigEntry *entry;
485
486 if (duplicates || (entry = FindEntry (CurrentSection, key)) == NULL)
487 {
488 NewConfigEntry (CurrentSection, key, value);
489 }
490 else
491 {
492 entry->SetValue (value);
493 }
494 }
495 }
496
497 //====================================================================
498 //
499 // FConfigFile :: FindSection
500 //
501 //====================================================================
502
FindSection(const char * name) const503 FConfigFile::FConfigSection *FConfigFile::FindSection (const char *name) const
504 {
505 FConfigSection *section = Sections;
506
507 while (section != NULL && section->SectionName.CompareNoCase(name) != 0)
508 {
509 section = section->Next;
510 }
511 return section;
512 }
513
514 //====================================================================
515 //
516 // FConfigFile :: RenameSection
517 //
518 //====================================================================
519
RenameSection(const char * oldname,const char * newname) const520 void FConfigFile::RenameSection (const char *oldname, const char *newname) const
521 {
522 FConfigSection *section = FindSection(oldname);
523
524 if (section != NULL)
525 {
526 section->SectionName = newname;
527 }
528 }
529
530 //====================================================================
531 //
532 // FConfigFile :: FindEntry
533 //
534 //====================================================================
535
FindEntry(FConfigFile::FConfigSection * section,const char * key) const536 FConfigFile::FConfigEntry *FConfigFile::FindEntry (
537 FConfigFile::FConfigSection *section, const char *key) const
538 {
539 FConfigEntry *probe = section->RootEntry;
540
541 while (probe != NULL && stricmp (probe->Key, key) != 0)
542 {
543 probe = probe->Next;
544 }
545 return probe;
546 }
547
548 //====================================================================
549 //
550 // FConfigFile :: NewConfigSection
551 //
552 //====================================================================
553
NewConfigSection(const char * name)554 FConfigFile::FConfigSection *FConfigFile::NewConfigSection (const char *name)
555 {
556 FConfigSection *section;
557
558 section = FindSection (name);
559 if (section == NULL)
560 {
561 section = new FConfigSection;
562 section->RootEntry = NULL;
563 section->LastEntryPtr = §ion->RootEntry;
564 section->Next = NULL;
565 section->SectionName = name;
566 *LastSectionPtr = section;
567 LastSectionPtr = §ion->Next;
568 }
569 return section;
570 }
571
572 //====================================================================
573 //
574 // FConfigFile :: NewConfigEntry
575 //
576 //====================================================================
577
NewConfigEntry(FConfigFile::FConfigSection * section,const char * key,const char * value)578 FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry (
579 FConfigFile::FConfigSection *section, const char *key, const char *value)
580 {
581 FConfigEntry *entry;
582 size_t keylen;
583
584 keylen = strlen (key);
585 entry = (FConfigEntry *)new char[sizeof(*section)+keylen];
586 entry->Value = NULL;
587 entry->Next = NULL;
588 memcpy (entry->Key, key, keylen);
589 entry->Key[keylen] = 0;
590 *(section->LastEntryPtr) = entry;
591 section->LastEntryPtr = &entry->Next;
592 entry->SetValue (value);
593 return entry;
594 }
595
596 //====================================================================
597 //
598 // FConfigFile :: LoadConfigFile
599 //
600 //====================================================================
601
LoadConfigFile()602 void FConfigFile::LoadConfigFile ()
603 {
604 FILE *file = fopen (PathName, "r");
605 bool succ;
606
607 FileExisted = false;
608 if (file == NULL)
609 {
610 return;
611 }
612
613 succ = ReadConfig (file);
614 fclose (file);
615 FileExisted = succ;
616 }
617
618 //====================================================================
619 //
620 // FConfigFile :: ReadConfig
621 //
622 //====================================================================
623
ReadConfig(void * file)624 bool FConfigFile::ReadConfig (void *file)
625 {
626 char readbuf[READBUFFERSIZE];
627 FConfigSection *section = NULL;
628 ClearConfig ();
629
630 while (ReadLine (readbuf, READBUFFERSIZE, file) != NULL)
631 {
632 char *start = readbuf;
633 char *equalpt;
634 char *endpt;
635
636 // Remove white space at start of line
637 while (*start && *start <= ' ')
638 {
639 start++;
640 }
641 // Remove comment lines
642 if (*start == '#' || (start[0] == '/' && start[1] == '/'))
643 {
644 continue;
645 }
646 // Remove white space at end of line
647 endpt = start + strlen (start) - 1;
648 while (endpt > start && *endpt <= ' ')
649 {
650 endpt--;
651 }
652 endpt[1] = 0;
653 if (endpt <= start)
654 continue; // Nothing here
655
656 if (*start == '[')
657 { // Section header
658 if (*endpt == ']')
659 *endpt = 0;
660 section = NewConfigSection (start+1);
661 }
662 else if (section == NULL)
663 {
664 return false;
665 }
666 else
667 { // Should be key=value
668 equalpt = strchr (start, '=');
669 if (equalpt != NULL && equalpt > start)
670 {
671 // Remove white space in front of =
672 char *whiteprobe = equalpt - 1;
673 while (whiteprobe > start && isspace(*whiteprobe))
674 {
675 whiteprobe--;
676 }
677 whiteprobe[1] = 0;
678 // Remove white space after =
679 whiteprobe = equalpt + 1;
680 while (*whiteprobe && isspace(*whiteprobe))
681 {
682 whiteprobe++;
683 }
684 *(whiteprobe - 1) = 0;
685 // Check for multi-line value
686 if (whiteprobe[0] == '<' && whiteprobe[1] == '<' && whiteprobe[2] == '<' && whiteprobe[3] != '\0')
687 {
688 ReadMultiLineValue (file, section, start, whiteprobe + 3);
689 }
690 else
691 {
692 NewConfigEntry (section, start, whiteprobe);
693 }
694 }
695 }
696 }
697 return true;
698 }
699
700 //====================================================================
701 //
702 // FConfigFile :: ReadMultiLineValue
703 //
704 // Reads a multi-line value, with format as follows:
705 //
706 // key=<<<ENDTAG
707 // ... blah blah blah ...
708 // >>>ENDTAG
709 //
710 // The final ENDTAG must be on a line all by itself.
711 //
712 //====================================================================
713
ReadMultiLineValue(void * file,FConfigSection * section,const char * key,const char * endtag)714 FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSection *section, const char *key, const char *endtag)
715 {
716 char readbuf[READBUFFERSIZE];
717 FString value;
718 size_t endlen = strlen(endtag);
719
720 // Keep on reading lines until we reach a line that matches >>>endtag
721 while (ReadLine(readbuf, READBUFFERSIZE, file) != NULL)
722 {
723 // Does the start of this line match the endtag?
724 if (readbuf[0] == '>' && readbuf[1] == '>' && readbuf[2] == '>' &&
725 strncmp(readbuf + 3, endtag, endlen) == 0)
726 { // Is there nothing but line break characters after the match?
727 size_t i;
728 for (i = endlen + 3; readbuf[i] != '\0'; ++i)
729 {
730 if (readbuf[i] != '\n' && readbuf[i] != '\r')
731 { // Not a line break character
732 break;
733 }
734 }
735 if (readbuf[i] == '\0')
736 { // We're done; strip the previous line's line breaks, since it's not part of the value.
737 value.StripRight("\n\r");
738 }
739 break;
740 }
741 // Append this line to the value.
742 value << readbuf;
743 }
744 return NewConfigEntry(section, key, value);
745 }
746
747 //====================================================================
748 //
749 // FConfigFile :: ReadLine
750 //
751 //====================================================================
752
ReadLine(char * string,int n,void * file) const753 char *FConfigFile::ReadLine (char *string, int n, void *file) const
754 {
755 return fgets (string, n, (FILE *)file);
756 }
757
758 //====================================================================
759 //
760 // FConfigFile :: WriteConfigFile
761 //
762 //====================================================================
763
WriteConfigFile() const764 bool FConfigFile::WriteConfigFile () const
765 {
766 if (!OkayToWrite && FileExisted)
767 { // Pretend it was written anyway so that the user doesn't get
768 // any "config not written" notifications, but only if the file
769 // already existed. Otherwise, let it write out a default one.
770 return true;
771 }
772
773 FILE *file = fopen (PathName, "w");
774 FConfigSection *section;
775 FConfigEntry *entry;
776
777 if (file == NULL)
778 return false;
779
780 WriteCommentHeader (file);
781
782 section = Sections;
783 while (section != NULL)
784 {
785 entry = section->RootEntry;
786 if (section->Note.IsNotEmpty())
787 {
788 fputs (section->Note.GetChars(), file);
789 }
790 fprintf (file, "[%s]\n", section->SectionName.GetChars());
791 while (entry != NULL)
792 {
793 if (strpbrk(entry->Value, "\r\n") == NULL)
794 { // Single-line value
795 fprintf (file, "%s=%s\n", entry->Key, entry->Value);
796 }
797 else
798 { // Multi-line value
799 const char *endtag = GenerateEndTag(entry->Value);
800 fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key,
801 endtag, entry->Value, endtag);
802 }
803 entry = entry->Next;
804 }
805 section = section->Next;
806 fputs ("\n", file);
807 }
808 fclose (file);
809 return true;
810 }
811
812 //====================================================================
813 //
814 // FConfigFile :: GenerateEndTag
815 //
816 // Generates a terminator sequence for multi-line values that does
817 // not appear anywhere in the value.
818 //
819 //====================================================================
820
GenerateEndTag(const char * value)821 const char *FConfigFile::GenerateEndTag(const char *value)
822 {
823 static const char Base64Table[] =
824 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
825 static char EndTag[25] = "EOV-";
826
827 // Try different 20-character sequences until we find one that
828 // isn't in the value. We create the sequences by generating two
829 // 64-bit random numbers and Base64 encoding the first 15 bytes
830 // from them.
831 union { QWORD rand_num[2]; BYTE rand_bytes[16]; };
832 do
833 {
834 rand_num[0] = pr_endtag.GenRand64();
835 rand_num[1] = pr_endtag.GenRand64();
836
837 for (int i = 0; i < 5; ++i)
838 {
839 //DWORD three_bytes = (rand_bytes[i*3] << 16) | (rand_bytes[i*3+1] << 8) | (rand_bytes[i*3+2]); // ???
840 EndTag[4+i*4 ] = Base64Table[rand_bytes[i*3] >> 2];
841 EndTag[4+i*4+1] = Base64Table[((rand_bytes[i*3] & 3) << 4) | (rand_bytes[i*3+1] >> 4)];
842 EndTag[4+i*4+2] = Base64Table[((rand_bytes[i*3+1] & 15) << 2) | (rand_bytes[i*3+2] >> 6)];
843 EndTag[4+i*4+3] = Base64Table[rand_bytes[i*3+2] & 63];
844 }
845 }
846 while (strstr(value, EndTag) != NULL);
847 return EndTag;
848 }
849
850 //====================================================================
851 //
852 // FConfigFile :: WriteCommentHeader
853 //
854 // Override in a subclass to write a header to the config file.
855 //
856 //====================================================================
857
WriteCommentHeader(FILE * file) const858 void FConfigFile::WriteCommentHeader (FILE *file) const
859 {
860 }
861
862 //====================================================================
863 //
864 // FConfigFile :: FConfigEntry :: SetValue
865 //
866 //====================================================================
867
SetValue(const char * value)868 void FConfigFile::FConfigEntry::SetValue (const char *value)
869 {
870 if (Value != NULL)
871 {
872 delete[] Value;
873 }
874 Value = new char[strlen (value)+1];
875 strcpy (Value, value);
876 }
877
878 //====================================================================
879 //
880 // FConfigFile :: GetPosition
881 //
882 // Populates a struct with the current position of the parse cursor.
883 //
884 //====================================================================
885
GetPosition(FConfigFile::Position & pos) const886 void FConfigFile::GetPosition (FConfigFile::Position &pos) const
887 {
888 pos.Section = CurrentSection;
889 pos.Entry = CurrentEntry;
890 }
891
892 //====================================================================
893 //
894 // FConfigFile :: SetPosition
895 //
896 // Sets the parse cursor to a previously retrieved position.
897 //
898 //====================================================================
899
SetPosition(const FConfigFile::Position & pos)900 void FConfigFile::SetPosition (const FConfigFile::Position &pos)
901 {
902 CurrentSection = pos.Section;
903 CurrentEntry = pos.Entry;
904 }
905
906 //====================================================================
907 //
908 // FConfigFile :: SetSectionNote
909 //
910 // Sets a comment note to be inserted into the INI verbatim directly
911 // ahead of the section. Notes are lost when the INI is read so must
912 // be explicitly set to be maintained.
913 //
914 //====================================================================
915
SetSectionNote(const char * section,const char * note)916 void FConfigFile::SetSectionNote(const char *section, const char *note)
917 {
918 SetSectionNote(FindSection(section), note);
919 }
920
SetSectionNote(const char * note)921 void FConfigFile::SetSectionNote(const char *note)
922 {
923 SetSectionNote(CurrentSection, note);
924 }
925
SetSectionNote(FConfigSection * section,const char * note)926 void FConfigFile::SetSectionNote(FConfigSection *section, const char *note)
927 {
928 if (section != NULL)
929 {
930 if (note == NULL)
931 {
932 note = "";
933 }
934 section->Note = note;
935 }
936 }
937