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 == &section->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 = &section->RootEntry;
564 		section->Next = NULL;
565 		section->SectionName = name;
566 		*LastSectionPtr = section;
567 		LastSectionPtr = &section->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