Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/simpleini/include/SimpleIni.h
4255 views
1
/** @mainpage
2
3
<table>
4
<tr><th>Library <td>SimpleIni
5
<tr><th>File <td>SimpleIni.h
6
<tr><th>Author <td>Brodie Thiesfield
7
<tr><th>Source <td>https://github.com/brofield/simpleini
8
<tr><th>Version <td>4.22
9
</table>
10
11
Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
12
13
@section intro INTRODUCTION
14
15
This component allows an INI-style configuration file to be used on both
16
Windows and Linux/Unix. It is fast, simple and source code using this
17
component will compile unchanged on either OS.
18
19
@section features FEATURES
20
21
- MIT Licence allows free use in all software (including GPL and commercial)
22
- multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix)
23
- loading and saving of INI-style configuration files
24
- configuration files can have any newline format on all platforms
25
- liberal acceptance of file format
26
- key/values with no section
27
- removal of whitespace around sections, keys and values
28
- support for multi-line values (values with embedded newline characters)
29
- optional support for multiple keys with the same name
30
- optional case-insensitive sections and keys (for ASCII characters only)
31
- saves files with sections and keys in the same order as they were loaded
32
- preserves comments on the file, section and keys where possible.
33
- supports both char or wchar_t programming interfaces
34
- supports both MBCS (system locale) and UTF-8 file encodings
35
- system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
36
- support for non-ASCII characters in section, keys, values and comments
37
- support for non-standard character types or file encodings
38
via user-written converter classes
39
- support for adding/modifying values programmatically
40
- should compile cleanly without warning usually at the strictest warning level
41
- it has been tested with the following compilers:
42
- Windows/VC6 (warning level 3)
43
- Windows/VC.NET 2003 (warning level 4)
44
- Windows/VC 2005 (warning level 4)
45
- Windows/VC 2019 (warning level 4)
46
- Linux/gcc (-Wall)
47
- Mac OS/c++ (-Wall)
48
49
@section usage USAGE SUMMARY
50
51
-# Decide if you will be using utf8 or MBCS files, and working with the
52
data in utf8, wchar_t or ICU chars.
53
-# If you will only be using straight utf8 files and access the data via the
54
char interface, then you do not need any conversion library and could define
55
SI_NO_CONVERSION. Note that no conversion also means no validation of the data.
56
If no converter is specified then the default converter is SI_CONVERT_GENERIC
57
on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on
58
Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also
59
supported on all platforms.
60
-# Define the appropriate symbol for the converter you wish to use and
61
include the SimpleIni.h header file.
62
-# Declare an instance of the appropriate class. Note that the following
63
definitions are just shortcuts for commonly used types. Other types
64
(PRUnichar, unsigned short, unsigned char) are also possible.
65
<table>
66
<tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
67
<tr><th>SI_NO_CONVERSION
68
<tr><td>char <td>No <td>Yes <td>No <td>CSimpleIniA
69
<tr><td>char <td>Yes <td>Yes <td>No <td>CSimpleIniCaseA
70
<tr><th>SI_CONVERT_GENERIC
71
<tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
72
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
73
<tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
74
<tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
75
<tr><th>SI_CONVERT_WIN32
76
<tr><td>char <td>No <td>No #2 <td>Yes <td>CSimpleIniA
77
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
78
<tr><td>wchar_t <td>No <td>Yes <td>Yes <td>CSimpleIniW
79
<tr><td>wchar_t <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
80
<tr><th>SI_CONVERT_ICU
81
<tr><td>char <td>No <td>Yes <td>Yes <td>CSimpleIniA
82
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
83
<tr><td>UChar <td>No <td>Yes <td>Yes <td>CSimpleIniW
84
<tr><td>UChar <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseW
85
</table>
86
#1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.<br>
87
#2 Only affects Windows. On Windows this uses MBCS functions and
88
so may fold case incorrectly leading to uncertain results.
89
-# Set all the options that you require, see all the Set*() options below.
90
The SetUnicode() option is very common and can be specified in the constructor.
91
-# Call LoadData() or LoadFile() to load and parse the INI configuration file
92
-# Access and modify the data of the file using the following functions
93
<table>
94
<tr><td>GetAllSections <td>Return all section names
95
<tr><td>GetAllKeys <td>Return all key names within a section
96
<tr><td>GetAllValues <td>Return all values within a section & key
97
<tr><td>GetSection <td>Return all key names and values in a section
98
<tr><td>GetSectionSize <td>Return the number of keys in a section
99
<tr><td>GetValue <td>Return a value for a section & key
100
<tr><td>SetValue <td>Add or update a value for a section & key
101
<tr><td>Delete <td>Remove a section, or a key from a section
102
<tr><td>SectionExists <td>Does a section exist?
103
<tr><td>KeyExists <td>Does a key exist?
104
</table>
105
-# Call Save() or SaveFile() to save the INI configuration data
106
107
@section iostreams IO STREAMS
108
109
SimpleIni supports reading from and writing to STL IO streams. Enable this
110
by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header
111
file. Ensure that if the streams are backed by a file (e.g. ifstream or
112
ofstream) then the flag ios_base::binary has been used when the file was
113
opened.
114
115
@section multiline MULTI-LINE VALUES
116
117
Values that span multiple lines are created using the following format.
118
119
<pre>
120
key = <<<ENDTAG
121
.... multiline value ....
122
ENDTAG
123
</pre>
124
125
Note the following:
126
- The text used for ENDTAG can be anything and is used to find
127
where the multi-line text ends.
128
- The newline after ENDTAG in the start tag, and the newline
129
before ENDTAG in the end tag is not included in the data value.
130
- The ending tag must be on it's own line with no whitespace before
131
or after it.
132
- The multi-line value is modified at load so that each line in the value
133
is delimited by a single '\\n' character on all platforms. At save time
134
it will be converted into the newline format used by the current
135
platform.
136
137
@section comments COMMENTS
138
139
Comments are preserved in the file within the following restrictions:
140
- Every file may have a single "file comment". It must start with the
141
first character in the file, and will end with the first non-comment
142
line in the file.
143
- Every section may have a single "section comment". It will start
144
with the first comment line following the file comment, or the last
145
data entry. It ends at the beginning of the section.
146
- Every key may have a single "key comment". This comment will start
147
with the first comment line following the section start, or the file
148
comment if there is no section name.
149
- Comments are set at the time that the file, section or key is first
150
created. The only way to modify a comment on a section or a key is to
151
delete that entry and recreate it with the new comment. There is no
152
way to change the file comment.
153
154
@section save SAVE ORDER
155
156
The sections and keys are written out in the same order as they were
157
read in from the file. Sections and keys added to the data after the
158
file has been loaded will be added to the end of the file when it is
159
written. There is no way to specify the location of a section or key
160
other than in first-created, first-saved order.
161
162
@section notes NOTES
163
164
- To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
165
Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
166
- When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
167
- When using SI_CONVERT_ICU, ICU header files must be on the include
168
path and icuuc.lib must be linked in.
169
- To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
170
you should use SI_CONVERT_GENERIC.
171
- The collation (sorting) order used for sections and keys returned from
172
iterators is NOT DEFINED. If collation order of the text is important
173
then it should be done yourself by either supplying a replacement
174
SI_STRLESS class, or by sorting the strings external to this library.
175
- Usage of the <mbstring.h> header on Windows can be disabled by defining
176
SI_NO_MBCS. This is defined automatically on Windows CE platforms.
177
- Not thread-safe so manage your own locking
178
179
@section contrib CONTRIBUTIONS
180
181
Many thanks to the following contributors:
182
183
- 2010/05/03: Tobias Gehrig: added GetDoubleValue()
184
- See list of many contributors in github
185
186
@section licence MIT LICENCE
187
188
The licence text below is the boilerplate "MIT Licence" used from:
189
http://www.opensource.org/licenses/mit-license.php
190
191
Copyright (c) 2006-2024, Brodie Thiesfield
192
193
Permission is hereby granted, free of charge, to any person obtaining a copy
194
of this software and associated documentation files (the "Software"), to deal
195
in the Software without restriction, including without limitation the rights
196
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
197
copies of the Software, and to permit persons to whom the Software is furnished
198
to do so, subject to the following conditions:
199
200
The above copyright notice and this permission notice shall be included in
201
all copies or substantial portions of the Software.
202
203
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
204
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
205
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
206
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
207
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
208
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
209
*/
210
211
#ifndef INCLUDED_SimpleIni_h
212
#define INCLUDED_SimpleIni_h
213
214
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
215
# pragma once
216
#endif
217
218
// Disable these warnings in MSVC:
219
// 4127 "conditional expression is constant" as the conversion classes trigger
220
// it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
221
// be optimized away in a release build.
222
// 4503 'insert' : decorated name length exceeded, name was truncated
223
// 4702 "unreachable code" as the MS STL header causes it in release mode.
224
// Again, the code causing the warning will be cleaned up by the compiler.
225
// 4786 "identifier truncated to 256 characters" as this is thrown hundreds
226
// of times VC6 as soon as STL is used.
227
#ifdef _MSC_VER
228
# pragma warning (push)
229
# pragma warning (disable: 4127 4503 4702 4786)
230
#endif
231
232
#include <cstring>
233
#include <cstdlib>
234
#include <string>
235
#include <map>
236
#include <list>
237
#include <algorithm>
238
#include <stdio.h>
239
240
#ifdef SI_SUPPORT_IOSTREAMS
241
# include <iostream>
242
#endif // SI_SUPPORT_IOSTREAMS
243
244
#ifdef _DEBUG
245
# ifndef assert
246
# include <cassert>
247
# endif
248
# define SI_ASSERT(x) assert(x)
249
#else
250
# define SI_ASSERT(x)
251
#endif
252
253
using SI_Error = int;
254
255
constexpr int SI_OK = 0; //!< No error
256
constexpr int SI_UPDATED = 1; //!< An existing value was updated
257
constexpr int SI_INSERTED = 2; //!< A new value was inserted
258
259
// note: test for any error with (retval < 0)
260
constexpr int SI_FAIL = -1; //!< Generic failure
261
constexpr int SI_NOMEM = -2; //!< Out of memory error
262
constexpr int SI_FILE = -3; //!< File error (see errno for detail error)
263
264
#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
265
266
#ifdef _WIN32
267
# define SI_NEWLINE_A "\r\n"
268
# define SI_NEWLINE_W L"\r\n"
269
#else // !_WIN32
270
# define SI_NEWLINE_A "\n"
271
# define SI_NEWLINE_W L"\n"
272
#endif // _WIN32
273
274
#if defined(SI_CONVERT_ICU)
275
# include <unicode/ustring.h>
276
#endif
277
278
#if defined(_WIN32)
279
# define SI_HAS_WIDE_FILE
280
# define SI_WCHAR_T wchar_t
281
#elif defined(SI_CONVERT_ICU)
282
# define SI_HAS_WIDE_FILE
283
# define SI_WCHAR_T UChar
284
#endif
285
286
287
// ---------------------------------------------------------------------------
288
// MAIN TEMPLATE CLASS
289
// ---------------------------------------------------------------------------
290
291
/** Simple INI file reader.
292
293
This can be instantiated with the choice of unicode or native characterset,
294
and case sensitive or insensitive comparisons of section and key names.
295
The supported combinations are pre-defined with the following typedefs:
296
297
<table>
298
<tr><th>Interface <th>Case-sensitive <th>Typedef
299
<tr><td>char <td>No <td>CSimpleIniA
300
<tr><td>char <td>Yes <td>CSimpleIniCaseA
301
<tr><td>wchar_t <td>No <td>CSimpleIniW
302
<tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
303
</table>
304
305
Note that using other types for the SI_CHAR is supported. For instance,
306
unsigned char, unsigned short, etc. Note that where the alternative type
307
is a different size to char/wchar_t you may need to supply new helper
308
classes for SI_STRLESS and SI_CONVERTER.
309
*/
310
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
311
class CSimpleIniTempl
312
{
313
public:
314
typedef SI_CHAR SI_CHAR_T;
315
316
/** key entry */
317
struct Entry {
318
const SI_CHAR * pItem;
319
const SI_CHAR * pComment;
320
int nOrder;
321
322
Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
323
: pItem(a_pszItem)
324
, pComment(NULL)
325
, nOrder(a_nOrder)
326
{ }
327
Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
328
: pItem(a_pszItem)
329
, pComment(a_pszComment)
330
, nOrder(a_nOrder)
331
{ }
332
Entry(const Entry & rhs) { operator=(rhs); }
333
Entry & operator=(const Entry & rhs) {
334
pItem = rhs.pItem;
335
pComment = rhs.pComment;
336
nOrder = rhs.nOrder;
337
return *this;
338
}
339
340
#if defined(_MSC_VER) && _MSC_VER <= 1200
341
/** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
342
bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
343
bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
344
#endif
345
346
/** Strict less ordering by name of key only */
347
struct KeyOrder {
348
bool operator()(const Entry & lhs, const Entry & rhs) const {
349
const static SI_STRLESS isLess = SI_STRLESS();
350
return isLess(lhs.pItem, rhs.pItem);
351
}
352
};
353
354
/** Strict less ordering by order, and then name of key */
355
struct LoadOrder {
356
bool operator()(const Entry & lhs, const Entry & rhs) const {
357
if (lhs.nOrder != rhs.nOrder) {
358
return lhs.nOrder < rhs.nOrder;
359
}
360
return KeyOrder()(lhs.pItem, rhs.pItem);
361
}
362
};
363
};
364
365
/** map keys to values */
366
typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
367
368
/** map sections to key/value map */
369
typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
370
371
/** set of dependent string pointers. Note that these pointers are
372
dependent on memory owned by CSimpleIni.
373
*/
374
typedef std::list<Entry> TNamesDepend;
375
376
/** interface definition for the OutputWriter object to pass to Save()
377
in order to output the INI file data.
378
*/
379
class OutputWriter {
380
public:
381
OutputWriter() { }
382
virtual ~OutputWriter() { }
383
virtual void Write(const char * a_pBuf) = 0;
384
private:
385
OutputWriter(const OutputWriter &); // disable
386
OutputWriter & operator=(const OutputWriter &); // disable
387
};
388
389
/** OutputWriter class to write the INI data to a file */
390
class FileWriter : public OutputWriter {
391
FILE * m_file;
392
public:
393
FileWriter(FILE * a_file) : m_file(a_file) { }
394
void Write(const char * a_pBuf) {
395
fputs(a_pBuf, m_file);
396
}
397
private:
398
FileWriter(const FileWriter &); // disable
399
FileWriter & operator=(const FileWriter &); // disable
400
};
401
402
/** OutputWriter class to write the INI data to a string */
403
class StringWriter : public OutputWriter {
404
std::string & m_string;
405
public:
406
StringWriter(std::string & a_string) : m_string(a_string) { }
407
void Write(const char * a_pBuf) {
408
m_string.append(a_pBuf);
409
}
410
private:
411
StringWriter(const StringWriter &); // disable
412
StringWriter & operator=(const StringWriter &); // disable
413
};
414
415
#ifdef SI_SUPPORT_IOSTREAMS
416
/** OutputWriter class to write the INI data to an ostream */
417
class StreamWriter : public OutputWriter {
418
std::ostream & m_ostream;
419
public:
420
StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
421
void Write(const char * a_pBuf) {
422
m_ostream << a_pBuf;
423
}
424
private:
425
StreamWriter(const StreamWriter &); // disable
426
StreamWriter & operator=(const StreamWriter &); // disable
427
};
428
#endif // SI_SUPPORT_IOSTREAMS
429
430
/** Characterset conversion utility class to convert strings to the
431
same format as is used for the storage.
432
*/
433
class Converter : private SI_CONVERTER {
434
public:
435
Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
436
m_scratch.resize(1024);
437
}
438
Converter(const Converter & rhs) { operator=(rhs); }
439
Converter & operator=(const Converter & rhs) {
440
m_scratch = rhs.m_scratch;
441
return *this;
442
}
443
bool ConvertToStore(const SI_CHAR * a_pszString) {
444
size_t uLen = SI_CONVERTER::SizeToStore(a_pszString);
445
if (uLen == (size_t)(-1)) {
446
return false;
447
}
448
while (uLen > m_scratch.size()) {
449
m_scratch.resize(m_scratch.size() * 2);
450
}
451
return SI_CONVERTER::ConvertToStore(
452
a_pszString,
453
const_cast<char*>(m_scratch.data()),
454
m_scratch.size());
455
}
456
const char * Data() { return m_scratch.data(); }
457
private:
458
std::string m_scratch;
459
};
460
461
public:
462
/*-----------------------------------------------------------------------*/
463
464
/** Default constructor.
465
466
@param a_bIsUtf8 See the method SetUnicode() for details.
467
@param a_bMultiKey See the method SetMultiKey() for details.
468
@param a_bMultiLine See the method SetMultiLine() for details.
469
*/
470
CSimpleIniTempl(
471
bool a_bIsUtf8 = false,
472
bool a_bMultiKey = false,
473
bool a_bMultiLine = false
474
);
475
476
/** Destructor */
477
~CSimpleIniTempl();
478
479
/** Deallocate all memory stored by this object */
480
void Reset();
481
482
/** Has any data been loaded */
483
bool IsEmpty() const { return m_data.empty(); }
484
485
/*-----------------------------------------------------------------------*/
486
/** @{ @name Settings */
487
488
/** Set the storage format of the INI data. This affects both the loading
489
and saving of the INI data using all of the Load/Save API functions.
490
This value cannot be changed after any INI data has been loaded.
491
492
If the file is not set to Unicode (UTF-8), then the data encoding is
493
assumed to be the OS native encoding. This encoding is the system
494
locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP.
495
If the storage format is set to Unicode then the file will be loaded
496
as UTF-8 encoded data regardless of the native file encoding. If
497
SI_CHAR == char then all of the char* parameters take and return UTF-8
498
encoded data regardless of the system locale.
499
500
\param a_bIsUtf8 Assume UTF-8 encoding for the source?
501
*/
502
void SetUnicode(bool a_bIsUtf8 = true) {
503
if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
504
}
505
506
/** Get the storage format of the INI data. */
507
bool IsUnicode() const { return m_bStoreIsUtf8; }
508
509
/** Should multiple identical keys be permitted in the file. If set to false
510
then the last value encountered will be used as the value of the key.
511
If set to true, then all values will be available to be queried. For
512
example, with the following input:
513
514
<pre>
515
[section]
516
test=value1
517
test=value2
518
</pre>
519
520
Then with SetMultiKey(true), both of the values "value1" and "value2"
521
will be returned for the key test. If SetMultiKey(false) is used, then
522
the value for "test" will only be "value2". This value may be changed
523
at any time.
524
525
\param a_bAllowMultiKey Allow multi-keys in the source?
526
*/
527
void SetMultiKey(bool a_bAllowMultiKey = true) {
528
m_bAllowMultiKey = a_bAllowMultiKey;
529
}
530
531
/** Get the storage format of the INI data. */
532
bool IsMultiKey() const { return m_bAllowMultiKey; }
533
534
/** Should data values be permitted to span multiple lines in the file. If
535
set to false then the multi-line construct <<<TAG as a value will be
536
returned as is instead of loading the data. This value may be changed
537
at any time.
538
539
\param a_bAllowMultiLine Allow multi-line values in the source?
540
*/
541
void SetMultiLine(bool a_bAllowMultiLine = true) {
542
m_bAllowMultiLine = a_bAllowMultiLine;
543
}
544
545
/** Query the status of multi-line data */
546
bool IsMultiLine() const { return m_bAllowMultiLine; }
547
548
/** Should spaces be added around the equals sign when writing key/value
549
pairs out. When true, the result will be "key = value". When false,
550
the result will be "key=value". This value may be changed at any time.
551
552
\param a_bSpaces Add spaces around the equals sign?
553
*/
554
void SetSpaces(bool a_bSpaces = true) {
555
m_bSpaces = a_bSpaces;
556
}
557
558
/** Query the status of spaces output */
559
bool UsingSpaces() const { return m_bSpaces; }
560
561
562
/** Should we recognise and parse quotes in single line values?
563
564
\param a_bParseQuotes Parse quoted data in values?
565
*/
566
void SetQuotes(bool a_bParseQuotes = true) {
567
m_bParseQuotes = a_bParseQuotes;
568
}
569
570
/** Are we permitting keys and values to be quoted? */
571
bool UsingQuotes() const { return m_bParseQuotes; }
572
573
/** When reading/writing an ini file, do we require every key to have an equals
574
sign to delineate a valid key value. If false, then every valid key must
575
have an equals sign and any lines without an equals sign is ignored. If
576
true then keys do not require an equals sign to be considered a key. Note
577
that this means that any non-commented line of text would become a key.
578
579
\param a_bAllowKeyOnly Permit keys without an equals sign or value.
580
*/
581
void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
582
m_bAllowKeyOnly = a_bAllowKeyOnly;
583
}
584
585
/** Do we allow keys to exist without a value or equals sign? */
586
bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
587
588
589
590
/*-----------------------------------------------------------------------*/
591
/** @}
592
@{ @name Loading INI Data */
593
594
/** Load an INI file from disk into memory
595
596
@param a_pszFile Path of the file to be loaded. This will be passed
597
to fopen() and so must be a valid path for the
598
current platform.
599
600
@return SI_Error See error definitions
601
*/
602
SI_Error LoadFile(
603
const char * a_pszFile
604
);
605
606
#ifdef SI_HAS_WIDE_FILE
607
/** Load an INI file from disk into memory
608
609
@param a_pwszFile Path of the file to be loaded in UTF-16.
610
611
@return SI_Error See error definitions
612
*/
613
SI_Error LoadFile(
614
const SI_WCHAR_T * a_pwszFile
615
);
616
#endif // SI_HAS_WIDE_FILE
617
618
/** Load the file from a file pointer.
619
620
@param a_fpFile Valid file pointer to read the file data from. The
621
file will be read until end of file.
622
623
@return SI_Error See error definitions
624
*/
625
SI_Error LoadFile(
626
FILE * a_fpFile
627
);
628
629
#ifdef SI_SUPPORT_IOSTREAMS
630
/** Load INI file data from an istream.
631
632
@param a_istream Stream to read from
633
634
@return SI_Error See error definitions
635
*/
636
SI_Error LoadData(
637
std::istream & a_istream
638
);
639
#endif // SI_SUPPORT_IOSTREAMS
640
641
/** Load INI file data direct from a std::string
642
643
@param a_strData Data to be loaded
644
645
@return SI_Error See error definitions
646
*/
647
SI_Error LoadData(const std::string & a_strData) {
648
return LoadData(a_strData.c_str(), a_strData.size());
649
}
650
651
/** Load INI file data direct from memory
652
653
@param a_pData Data to be loaded
654
@param a_uDataLen Length of the data in bytes
655
656
@return SI_Error See error definitions
657
*/
658
SI_Error LoadData(
659
const char * a_pData,
660
size_t a_uDataLen
661
);
662
663
/*-----------------------------------------------------------------------*/
664
/** @}
665
@{ @name Saving INI Data */
666
667
/** Save an INI file from memory to disk
668
669
@param a_pszFile Path of the file to be saved. This will be passed
670
to fopen() and so must be a valid path for the
671
current platform.
672
673
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is
674
in UTF-8 format. If it is not UTF-8 then
675
this parameter is ignored.
676
677
@return SI_Error See error definitions
678
*/
679
SI_Error SaveFile(
680
const char * a_pszFile,
681
bool a_bAddSignature = true
682
) const;
683
684
#ifdef SI_HAS_WIDE_FILE
685
/** Save an INI file from memory to disk
686
687
@param a_pwszFile Path of the file to be saved in UTF-16.
688
689
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is
690
in UTF-8 format. If it is not UTF-8 then
691
this parameter is ignored.
692
693
@return SI_Error See error definitions
694
*/
695
SI_Error SaveFile(
696
const SI_WCHAR_T * a_pwszFile,
697
bool a_bAddSignature = true
698
) const;
699
#endif // _WIN32
700
701
/** Save the INI data to a file. See Save() for details.
702
703
@param a_pFile Handle to a file. File should be opened for
704
binary output.
705
706
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
707
UTF-8 format. If it is not UTF-8 then this value is
708
ignored. Do not set this to true if anything has
709
already been written to the file.
710
711
@return SI_Error See error definitions
712
*/
713
SI_Error SaveFile(
714
FILE * a_pFile,
715
bool a_bAddSignature = false
716
) const;
717
718
/** Save the INI data. The data will be written to the output device
719
in a format appropriate to the current data, selected by:
720
721
<table>
722
<tr><th>SI_CHAR <th>FORMAT
723
<tr><td>char <td>same format as when loaded (MBCS or UTF-8)
724
<tr><td>wchar_t <td>UTF-8
725
<tr><td>other <td>UTF-8
726
</table>
727
728
Note that comments from the original data is preserved as per the
729
documentation on comments. The order of the sections and values
730
from the original file will be preserved.
731
732
Any data prepended or appended to the output device must use the the
733
same format (MBCS or UTF-8). You may use the GetConverter() method to
734
convert text to the correct format regardless of the output format
735
being used by SimpleIni.
736
737
To add a BOM to UTF-8 data, write it out manually at the very beginning
738
like is done in SaveFile when a_bUseBOM is true.
739
740
@param a_oOutput Output writer to write the data to.
741
742
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
743
UTF-8 format. If it is not UTF-8 then this value is
744
ignored. Do not set this to true if anything has
745
already been written to the OutputWriter.
746
747
@return SI_Error See error definitions
748
*/
749
SI_Error Save(
750
OutputWriter & a_oOutput,
751
bool a_bAddSignature = false
752
) const;
753
754
#ifdef SI_SUPPORT_IOSTREAMS
755
/** Save the INI data to an ostream. See Save() for details.
756
757
@param a_ostream String to have the INI data appended to.
758
759
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
760
UTF-8 format. If it is not UTF-8 then this value is
761
ignored. Do not set this to true if anything has
762
already been written to the stream.
763
764
@return SI_Error See error definitions
765
*/
766
SI_Error Save(
767
std::ostream & a_ostream,
768
bool a_bAddSignature = false
769
) const
770
{
771
StreamWriter writer(a_ostream);
772
return Save(writer, a_bAddSignature);
773
}
774
#endif // SI_SUPPORT_IOSTREAMS
775
776
/** Append the INI data to a string. See Save() for details.
777
778
@param a_sBuffer String to have the INI data appended to.
779
780
@param a_bAddSignature Prepend the UTF-8 BOM if the output data is in
781
UTF-8 format. If it is not UTF-8 then this value is
782
ignored. Do not set this to true if anything has
783
already been written to the string.
784
785
@return SI_Error See error definitions
786
*/
787
SI_Error Save(
788
std::string & a_sBuffer,
789
bool a_bAddSignature = false
790
) const
791
{
792
StringWriter writer(a_sBuffer);
793
return Save(writer, a_bAddSignature);
794
}
795
796
/*-----------------------------------------------------------------------*/
797
/** @}
798
@{ @name Accessing INI Data */
799
800
/** Retrieve the number keys across all sections.
801
@return number of keys currently present.
802
*/
803
size_t GetKeyCount() const;
804
805
/** Retrieve all section names. The list is returned as an STL vector of
806
names and can be iterated or searched as necessary. Note that the
807
sort order of the returned strings is NOT DEFINED. You can sort
808
the names into the load order if desired. Search this file for ".sort"
809
for an example.
810
811
NOTE! This structure contains only pointers to strings. The actual
812
string data is stored in memory owned by CSimpleIni. Ensure that the
813
CSimpleIni object is not destroyed or Reset() while these pointers
814
are in use!
815
816
@param a_names Vector that will receive all of the section
817
names. See note above!
818
*/
819
void GetAllSections(
820
TNamesDepend & a_names
821
) const;
822
823
/** Retrieve all unique key names in a section. The sort order of the
824
returned strings is NOT DEFINED. You can sort the names into the load
825
order if desired. Search this file for ".sort" for an example. Only
826
unique key names are returned.
827
828
NOTE! This structure contains only pointers to strings. The actual
829
string data is stored in memory owned by CSimpleIni. Ensure that the
830
CSimpleIni object is not destroyed or Reset() while these strings
831
are in use!
832
833
@param a_pSection Section to request data for
834
@param a_names List that will receive all of the key
835
names. See note above!
836
837
@return true Section was found.
838
@return false Matching section was not found.
839
*/
840
bool GetAllKeys(
841
const SI_CHAR * a_pSection,
842
TNamesDepend & a_names
843
) const;
844
845
/** Retrieve all values for a specific key. This method can be used when
846
multiple keys are both enabled and disabled. Note that the sort order
847
of the returned strings is NOT DEFINED. You can sort the names into
848
the load order if desired. Search this file for ".sort" for an example.
849
850
NOTE! The returned values are pointers to string data stored in memory
851
owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
852
or Reset while you are using this pointer!
853
854
@param a_pSection Section to search
855
@param a_pKey Key to search for
856
@param a_values List to return if the key is not found
857
858
@return true Key was found.
859
@return false Matching section/key was not found.
860
*/
861
bool GetAllValues(
862
const SI_CHAR * a_pSection,
863
const SI_CHAR * a_pKey,
864
TNamesDepend & a_values
865
) const;
866
867
/** Query the number of keys in a specific section. Note that if multiple
868
keys are enabled, then this value may be different to the number of
869
keys returned by GetAllKeys.
870
871
@param a_pSection Section to request data for
872
873
@return -1 Section does not exist in the file
874
@return >=0 Number of keys in the section
875
*/
876
int GetSectionSize(
877
const SI_CHAR * a_pSection
878
) const;
879
880
/** Retrieve all key and value pairs for a section. The data is returned
881
as a pointer to an STL map and can be iterated or searched as
882
desired. Note that multiple entries for the same key may exist when
883
multiple keys have been enabled.
884
885
NOTE! This structure contains only pointers to strings. The actual
886
string data is stored in memory owned by CSimpleIni. Ensure that the
887
CSimpleIni object is not destroyed or Reset() while these strings
888
are in use!
889
890
@param a_pSection Name of the section to return
891
@return Section data
892
*/
893
const TKeyVal * GetSection(
894
const SI_CHAR * a_pSection
895
) const;
896
897
/** Test if a section exists. Convenience function */
898
inline bool SectionExists(
899
const SI_CHAR * a_pSection
900
) const {
901
return GetSection(a_pSection) != NULL;
902
}
903
904
/** Test if the key exists in a section. Convenience function. */
905
inline bool KeyExists(
906
const SI_CHAR * a_pSection,
907
const SI_CHAR * a_pKey
908
) const {
909
return GetValue(a_pSection, a_pKey) != NULL;
910
}
911
912
/** Retrieve the value for a specific key. If multiple keys are enabled
913
(see SetMultiKey) then only the first value associated with that key
914
will be returned, see GetAllValues for getting all values with multikey.
915
916
NOTE! The returned value is a pointer to string data stored in memory
917
owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
918
or Reset while you are using this pointer!
919
920
@param a_pSection Section to search
921
@param a_pKey Key to search for
922
@param a_pDefault Value to return if the key is not found
923
@param a_pHasMultiple Optionally receive notification of if there are
924
multiple entries for this key.
925
926
@return a_pDefault Key was not found in the section
927
@return other Value of the key
928
*/
929
const SI_CHAR * GetValue(
930
const SI_CHAR * a_pSection,
931
const SI_CHAR * a_pKey,
932
const SI_CHAR * a_pDefault = NULL,
933
bool * a_pHasMultiple = NULL
934
) const;
935
936
/** Retrieve a numeric value for a specific key. If multiple keys are enabled
937
(see SetMultiKey) then only the first value associated with that key
938
will be returned, see GetAllValues for getting all values with multikey.
939
940
@param a_pSection Section to search
941
@param a_pKey Key to search for
942
@param a_nDefault Value to return if the key is not found
943
@param a_pHasMultiple Optionally receive notification of if there are
944
multiple entries for this key.
945
946
@return a_nDefault Key was not found in the section
947
@return other Value of the key
948
*/
949
long GetLongValue(
950
const SI_CHAR * a_pSection,
951
const SI_CHAR * a_pKey,
952
long a_nDefault = 0,
953
bool * a_pHasMultiple = NULL
954
) const;
955
956
/** Retrieve a numeric value for a specific key. If multiple keys are enabled
957
(see SetMultiKey) then only the first value associated with that key
958
will be returned, see GetAllValues for getting all values with multikey.
959
960
@param a_pSection Section to search
961
@param a_pKey Key to search for
962
@param a_nDefault Value to return if the key is not found
963
@param a_pHasMultiple Optionally receive notification of if there are
964
multiple entries for this key.
965
966
@return a_nDefault Key was not found in the section
967
@return other Value of the key
968
*/
969
double GetDoubleValue(
970
const SI_CHAR * a_pSection,
971
const SI_CHAR * a_pKey,
972
double a_nDefault = 0,
973
bool * a_pHasMultiple = NULL
974
) const;
975
976
/** Retrieve a boolean value for a specific key. If multiple keys are enabled
977
(see SetMultiKey) then only the first value associated with that key
978
will be returned, see GetAllValues for getting all values with multikey.
979
980
Strings starting with "t", "y", "on" or "1" are returned as logically true.
981
Strings starting with "f", "n", "of" or "0" are returned as logically false.
982
For all other values the default is returned. Character comparisons are
983
case-insensitive.
984
985
@param a_pSection Section to search
986
@param a_pKey Key to search for
987
@param a_bDefault Value to return if the key is not found
988
@param a_pHasMultiple Optionally receive notification of if there are
989
multiple entries for this key.
990
991
@return a_nDefault Key was not found in the section
992
@return other Value of the key
993
*/
994
bool GetBoolValue(
995
const SI_CHAR * a_pSection,
996
const SI_CHAR * a_pKey,
997
bool a_bDefault = false,
998
bool * a_pHasMultiple = NULL
999
) const;
1000
1001
/** Add or update a section or value. This will always insert
1002
when multiple keys are enabled.
1003
1004
@param a_pSection Section to add or update
1005
@param a_pKey Key to add or update. Set to NULL to
1006
create an empty section.
1007
@param a_pValue Value to set. Set to NULL to create an
1008
empty section.
1009
@param a_pComment Comment to be associated with the section or the
1010
key. If a_pKey is NULL then it will be associated
1011
with the section, otherwise the key. Note that a
1012
comment may be set ONLY when the section or key is
1013
first created (i.e. when this function returns the
1014
value SI_INSERTED). If you wish to create a section
1015
with a comment then you need to create the section
1016
separately to the key. The comment string must be
1017
in full comment form already (have a comment
1018
character starting every line).
1019
@param a_bForceReplace Should all existing values in a multi-key INI
1020
file be replaced with this entry. This option has
1021
no effect if not using multi-key files. The
1022
difference between Delete/SetValue and SetValue
1023
with a_bForceReplace = true, is that the load
1024
order and comment will be preserved this way.
1025
1026
@return SI_Error See error definitions
1027
@return SI_UPDATED Value was updated
1028
@return SI_INSERTED Value was inserted
1029
*/
1030
SI_Error SetValue(
1031
const SI_CHAR * a_pSection,
1032
const SI_CHAR * a_pKey,
1033
const SI_CHAR * a_pValue,
1034
const SI_CHAR * a_pComment = NULL,
1035
bool a_bForceReplace = false
1036
)
1037
{
1038
return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
1039
}
1040
1041
/** Add or update a numeric value. This will always insert
1042
when multiple keys are enabled.
1043
1044
@param a_pSection Section to add or update
1045
@param a_pKey Key to add or update.
1046
@param a_nValue Value to set.
1047
@param a_pComment Comment to be associated with the key. See the
1048
notes on SetValue() for comments.
1049
@param a_bUseHex By default the value will be written to the file
1050
in decimal format. Set this to true to write it
1051
as hexadecimal.
1052
@param a_bForceReplace Should all existing values in a multi-key INI
1053
file be replaced with this entry. This option has
1054
no effect if not using multi-key files. The
1055
difference between Delete/SetLongValue and
1056
SetLongValue with a_bForceReplace = true, is that
1057
the load order and comment will be preserved this
1058
way.
1059
1060
@return SI_Error See error definitions
1061
@return SI_UPDATED Value was updated
1062
@return SI_INSERTED Value was inserted
1063
*/
1064
SI_Error SetLongValue(
1065
const SI_CHAR * a_pSection,
1066
const SI_CHAR * a_pKey,
1067
long a_nValue,
1068
const SI_CHAR * a_pComment = NULL,
1069
bool a_bUseHex = false,
1070
bool a_bForceReplace = false
1071
);
1072
1073
/** Add or update a double value. This will always insert
1074
when multiple keys are enabled.
1075
1076
@param a_pSection Section to add or update
1077
@param a_pKey Key to add or update.
1078
@param a_nValue Value to set.
1079
@param a_pComment Comment to be associated with the key. See the
1080
notes on SetValue() for comments.
1081
@param a_bForceReplace Should all existing values in a multi-key INI
1082
file be replaced with this entry. This option has
1083
no effect if not using multi-key files. The
1084
difference between Delete/SetDoubleValue and
1085
SetDoubleValue with a_bForceReplace = true, is that
1086
the load order and comment will be preserved this
1087
way.
1088
1089
@return SI_Error See error definitions
1090
@return SI_UPDATED Value was updated
1091
@return SI_INSERTED Value was inserted
1092
*/
1093
SI_Error SetDoubleValue(
1094
const SI_CHAR * a_pSection,
1095
const SI_CHAR * a_pKey,
1096
double a_nValue,
1097
const SI_CHAR * a_pComment = NULL,
1098
bool a_bForceReplace = false
1099
);
1100
1101
/** Add or update a boolean value. This will always insert
1102
when multiple keys are enabled.
1103
1104
@param a_pSection Section to add or update
1105
@param a_pKey Key to add or update.
1106
@param a_bValue Value to set.
1107
@param a_pComment Comment to be associated with the key. See the
1108
notes on SetValue() for comments.
1109
@param a_bForceReplace Should all existing values in a multi-key INI
1110
file be replaced with this entry. This option has
1111
no effect if not using multi-key files. The
1112
difference between Delete/SetBoolValue and
1113
SetBoolValue with a_bForceReplace = true, is that
1114
the load order and comment will be preserved this
1115
way.
1116
1117
@return SI_Error See error definitions
1118
@return SI_UPDATED Value was updated
1119
@return SI_INSERTED Value was inserted
1120
*/
1121
SI_Error SetBoolValue(
1122
const SI_CHAR * a_pSection,
1123
const SI_CHAR * a_pKey,
1124
bool a_bValue,
1125
const SI_CHAR * a_pComment = NULL,
1126
bool a_bForceReplace = false
1127
);
1128
1129
/** Delete an entire section, or a key from a section. Note that the
1130
data returned by GetSection is invalid and must not be used after
1131
anything has been deleted from that section using this method.
1132
Note when multiple keys is enabled, this will delete all keys with
1133
that name; to selectively delete individual key/values, use
1134
DeleteValue.
1135
1136
@param a_pSection Section to delete key from, or if
1137
a_pKey is NULL, the section to remove.
1138
@param a_pKey Key to remove from the section. Set to
1139
NULL to remove the entire section.
1140
@param a_bRemoveEmpty If the section is empty after this key has
1141
been deleted, should the empty section be
1142
removed?
1143
1144
@return true Key or section was deleted.
1145
@return false Key or section was not found.
1146
*/
1147
bool Delete(
1148
const SI_CHAR * a_pSection,
1149
const SI_CHAR * a_pKey,
1150
bool a_bRemoveEmpty = false
1151
);
1152
1153
/** Delete an entire section, or a key from a section. If value is
1154
provided, only remove keys with the value. Note that the data
1155
returned by GetSection is invalid and must not be used after
1156
anything has been deleted from that section using this method.
1157
Note when multiple keys is enabled, all keys with the value will
1158
be deleted.
1159
1160
@param a_pSection Section to delete key from, or if
1161
a_pKey is NULL, the section to remove.
1162
@param a_pKey Key to remove from the section. Set to
1163
NULL to remove the entire section.
1164
@param a_pValue Value of key to remove from the section.
1165
Set to NULL to remove all keys.
1166
@param a_bRemoveEmpty If the section is empty after this key has
1167
been deleted, should the empty section be
1168
removed?
1169
1170
@return true Key/value or section was deleted.
1171
@return false Key/value or section was not found.
1172
*/
1173
bool DeleteValue(
1174
const SI_CHAR * a_pSection,
1175
const SI_CHAR * a_pKey,
1176
const SI_CHAR * a_pValue,
1177
bool a_bRemoveEmpty = false
1178
);
1179
1180
/*-----------------------------------------------------------------------*/
1181
/** @}
1182
@{ @name Converter */
1183
1184
/** Return a conversion object to convert text to the same encoding
1185
as is used by the Save(), SaveFile() and SaveString() functions.
1186
Use this to prepare the strings that you wish to append or prepend
1187
to the output INI data.
1188
*/
1189
Converter GetConverter() const {
1190
return Converter(m_bStoreIsUtf8);
1191
}
1192
1193
/*-----------------------------------------------------------------------*/
1194
/** @} */
1195
1196
private:
1197
// copying is not permitted
1198
CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1199
CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
1200
1201
/** Parse the data looking for a file comment and store it if found.
1202
*/
1203
SI_Error FindFileComment(
1204
SI_CHAR *& a_pData,
1205
bool a_bCopyStrings
1206
);
1207
1208
/** Parse the data looking for the next valid entry. The memory pointed to
1209
by a_pData is modified by inserting NULL characters. The pointer is
1210
updated to the current location in the block of text.
1211
*/
1212
bool FindEntry(
1213
SI_CHAR *& a_pData,
1214
const SI_CHAR *& a_pSection,
1215
const SI_CHAR *& a_pKey,
1216
const SI_CHAR *& a_pVal,
1217
const SI_CHAR *& a_pComment
1218
) const;
1219
1220
/** Add the section/key/value to our data.
1221
1222
@param a_pSection Section name. Sections will be created if they
1223
don't already exist.
1224
@param a_pKey Key name. May be NULL to create an empty section.
1225
Existing entries will be updated. New entries will
1226
be created.
1227
@param a_pValue Value for the key.
1228
@param a_pComment Comment to be associated with the section or the
1229
key. If a_pKey is NULL then it will be associated
1230
with the section, otherwise the key. This must be
1231
a string in full comment form already (have a
1232
comment character starting every line).
1233
@param a_bForceReplace Should all existing values in a multi-key INI
1234
file be replaced with this entry. This option has
1235
no effect if not using multi-key files. The
1236
difference between Delete/AddEntry and AddEntry
1237
with a_bForceReplace = true, is that the load
1238
order and comment will be preserved this way.
1239
@param a_bCopyStrings Should copies of the strings be made or not.
1240
If false then the pointers will be used as is.
1241
*/
1242
SI_Error AddEntry(
1243
const SI_CHAR * a_pSection,
1244
const SI_CHAR * a_pKey,
1245
const SI_CHAR * a_pValue,
1246
const SI_CHAR * a_pComment,
1247
bool a_bForceReplace,
1248
bool a_bCopyStrings
1249
);
1250
1251
/** Is the supplied character a whitespace character? */
1252
inline bool IsSpace(SI_CHAR ch) const {
1253
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1254
}
1255
1256
/** Does the supplied character start a comment line? */
1257
inline bool IsComment(SI_CHAR ch) const {
1258
return (ch == ';' || ch == '#');
1259
}
1260
1261
1262
/** Skip over a newline character (or characters) for either DOS or UNIX */
1263
inline void SkipNewLine(SI_CHAR *& a_pData) const {
1264
a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
1265
}
1266
1267
/** Make a copy of the supplied string, replacing the original pointer */
1268
SI_Error CopyString(const SI_CHAR *& a_pString);
1269
1270
/** Delete a string from the copied strings buffer if necessary */
1271
void DeleteString(const SI_CHAR * a_pString);
1272
1273
/** Internal use of our string comparison function */
1274
bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
1275
const static SI_STRLESS isLess = SI_STRLESS();
1276
return isLess(a_pLeft, a_pRight);
1277
}
1278
1279
bool IsMultiLineTag(const SI_CHAR * a_pData) const;
1280
bool IsMultiLineData(const SI_CHAR * a_pData) const;
1281
bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
1282
bool LoadMultiLineText(
1283
SI_CHAR *& a_pData,
1284
const SI_CHAR *& a_pVal,
1285
const SI_CHAR * a_pTagName,
1286
bool a_bAllowBlankLinesInComment = false
1287
) const;
1288
bool IsNewLineChar(SI_CHAR a_c) const;
1289
1290
bool OutputMultiLineText(
1291
OutputWriter & a_oOutput,
1292
Converter & a_oConverter,
1293
const SI_CHAR * a_pText
1294
) const;
1295
1296
private:
1297
/** Copy of the INI file data in our character format. This will be
1298
modified when parsed to have NULL characters added after all
1299
interesting string entries. All of the string pointers to sections,
1300
keys and values point into this block of memory.
1301
*/
1302
SI_CHAR * m_pData;
1303
1304
/** Length of the data that we have stored. Used when deleting strings
1305
to determine if the string is stored here or in the allocated string
1306
buffer.
1307
*/
1308
size_t m_uDataLen;
1309
1310
/** File comment for this data, if one exists. */
1311
const SI_CHAR * m_pFileComment;
1312
1313
/** constant empty string */
1314
const SI_CHAR m_cEmptyString;
1315
1316
/** Parsed INI data. Section -> (Key -> Value). */
1317
TSection m_data;
1318
1319
/** This vector stores allocated memory for copies of strings that have
1320
been supplied after the file load. It will be empty unless SetValue()
1321
has been called.
1322
*/
1323
TNamesDepend m_strings;
1324
1325
/** Is the format of our datafile UTF-8 or MBCS? */
1326
bool m_bStoreIsUtf8;
1327
1328
/** Are multiple values permitted for the same key? */
1329
bool m_bAllowMultiKey;
1330
1331
/** Are data values permitted to span multiple lines? */
1332
bool m_bAllowMultiLine;
1333
1334
/** Should spaces be written out surrounding the equals sign? */
1335
bool m_bSpaces;
1336
1337
/** Should quoted data in values be recognized and parsed? */
1338
bool m_bParseQuotes;
1339
1340
/** Do keys always need to have an equals sign when reading/writing? */
1341
bool m_bAllowKeyOnly;
1342
1343
/** Next order value, used to ensure sections and keys are output in the
1344
same order that they are loaded/added.
1345
*/
1346
int m_nOrder;
1347
};
1348
1349
// ---------------------------------------------------------------------------
1350
// IMPLEMENTATION
1351
// ---------------------------------------------------------------------------
1352
1353
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1354
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
1355
bool a_bIsUtf8,
1356
bool a_bAllowMultiKey,
1357
bool a_bAllowMultiLine
1358
)
1359
: m_pData(0)
1360
, m_uDataLen(0)
1361
, m_pFileComment(NULL)
1362
, m_cEmptyString(0)
1363
, m_bStoreIsUtf8(a_bIsUtf8)
1364
, m_bAllowMultiKey(a_bAllowMultiKey)
1365
, m_bAllowMultiLine(a_bAllowMultiLine)
1366
, m_bSpaces(true)
1367
, m_bParseQuotes(false)
1368
, m_bAllowKeyOnly(false)
1369
, m_nOrder(0)
1370
{ }
1371
1372
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1373
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::~CSimpleIniTempl()
1374
{
1375
Reset();
1376
}
1377
1378
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1379
void
1380
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Reset()
1381
{
1382
// remove all data
1383
delete[] m_pData;
1384
m_pData = NULL;
1385
m_uDataLen = 0;
1386
m_pFileComment = NULL;
1387
if (!m_data.empty()) {
1388
m_data.erase(m_data.begin(), m_data.end());
1389
}
1390
1391
// remove all strings
1392
if (!m_strings.empty()) {
1393
typename TNamesDepend::iterator i = m_strings.begin();
1394
for (; i != m_strings.end(); ++i) {
1395
delete[] const_cast<SI_CHAR*>(i->pItem);
1396
}
1397
m_strings.erase(m_strings.begin(), m_strings.end());
1398
}
1399
}
1400
1401
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1402
SI_Error
1403
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1404
const char * a_pszFile
1405
)
1406
{
1407
FILE * fp = NULL;
1408
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1409
fopen_s(&fp, a_pszFile, "rb");
1410
#else // !__STDC_WANT_SECURE_LIB__
1411
fp = fopen(a_pszFile, "rb");
1412
#endif // __STDC_WANT_SECURE_LIB__
1413
if (!fp) {
1414
return SI_FILE;
1415
}
1416
SI_Error rc = LoadFile(fp);
1417
fclose(fp);
1418
return rc;
1419
}
1420
1421
#ifdef SI_HAS_WIDE_FILE
1422
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1423
SI_Error
1424
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1425
const SI_WCHAR_T * a_pwszFile
1426
)
1427
{
1428
#ifdef _WIN32
1429
FILE * fp = NULL;
1430
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1431
_wfopen_s(&fp, a_pwszFile, L"rb");
1432
#else // !__STDC_WANT_SECURE_LIB__
1433
fp = _wfopen(a_pwszFile, L"rb");
1434
#endif // __STDC_WANT_SECURE_LIB__
1435
if (!fp) return SI_FILE;
1436
SI_Error rc = LoadFile(fp);
1437
fclose(fp);
1438
return rc;
1439
#else // !_WIN32 (therefore SI_CONVERT_ICU)
1440
char szFile[256];
1441
u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1442
return LoadFile(szFile);
1443
#endif // _WIN32
1444
}
1445
#endif // SI_HAS_WIDE_FILE
1446
1447
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1448
SI_Error
1449
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
1450
FILE * a_fpFile
1451
)
1452
{
1453
// load the raw file data
1454
int retval = fseek(a_fpFile, 0, SEEK_END);
1455
if (retval != 0) {
1456
return SI_FILE;
1457
}
1458
long lSize = ftell(a_fpFile);
1459
if (lSize < 0) {
1460
return SI_FILE;
1461
}
1462
if (lSize == 0) {
1463
return SI_OK;
1464
}
1465
1466
// allocate and ensure NULL terminated
1467
char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
1468
if (!pData) {
1469
return SI_NOMEM;
1470
}
1471
pData[lSize] = 0;
1472
1473
// load data into buffer
1474
fseek(a_fpFile, 0, SEEK_SET);
1475
size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1476
if (uRead != (size_t) lSize) {
1477
delete[] pData;
1478
return SI_FILE;
1479
}
1480
1481
// convert the raw data to unicode
1482
SI_Error rc = LoadData(pData, uRead);
1483
delete[] pData;
1484
return rc;
1485
}
1486
1487
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1488
SI_Error
1489
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
1490
const char * a_pData,
1491
size_t a_uDataLen
1492
)
1493
{
1494
if (!a_pData) {
1495
return SI_OK;
1496
}
1497
1498
// if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
1499
// already loaded data and try to change mode half-way through then this will
1500
// be ignored and we will assert in debug versions
1501
if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1502
a_pData += 3;
1503
a_uDataLen -= 3;
1504
SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
1505
SetUnicode();
1506
}
1507
1508
if (a_uDataLen == 0) {
1509
return SI_OK;
1510
}
1511
1512
// determine the length of the converted data
1513
SI_CONVERTER converter(m_bStoreIsUtf8);
1514
size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1515
if (uLen == (size_t)(-1)) {
1516
return SI_FAIL;
1517
}
1518
1519
// allocate memory for the data, ensure that there is a NULL
1520
// terminator wherever the converted data ends
1521
SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
1522
if (!pData) {
1523
return SI_NOMEM;
1524
}
1525
memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1526
1527
// convert the data
1528
if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1529
delete[] pData;
1530
return SI_FAIL;
1531
}
1532
1533
// parse it
1534
const static SI_CHAR empty = 0;
1535
SI_CHAR * pWork = pData;
1536
const SI_CHAR * pSection = &empty;
1537
const SI_CHAR * pItem = NULL;
1538
const SI_CHAR * pVal = NULL;
1539
const SI_CHAR * pComment = NULL;
1540
1541
// We copy the strings if we are loading data into this class when we
1542
// already have stored some.
1543
bool bCopyStrings = (m_pData != NULL);
1544
1545
// find a file comment if it exists, this is a comment that starts at the
1546
// beginning of the file and continues until the first blank line.
1547
SI_Error rc = FindFileComment(pWork, bCopyStrings);
1548
if (rc < 0) return rc;
1549
1550
// add every entry in the file to the data table
1551
while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1552
rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1553
if (rc < 0) return rc;
1554
}
1555
1556
// store these strings if we didn't copy them
1557
if (bCopyStrings) {
1558
delete[] pData;
1559
}
1560
else {
1561
m_pData = pData;
1562
m_uDataLen = uLen+1;
1563
}
1564
1565
return SI_OK;
1566
}
1567
1568
#ifdef SI_SUPPORT_IOSTREAMS
1569
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1570
SI_Error
1571
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
1572
std::istream & a_istream
1573
)
1574
{
1575
std::string strData;
1576
char szBuf[512];
1577
do {
1578
a_istream.get(szBuf, sizeof(szBuf), '\0');
1579
strData.append(szBuf);
1580
}
1581
while (a_istream.good());
1582
return LoadData(strData);
1583
}
1584
#endif // SI_SUPPORT_IOSTREAMS
1585
1586
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1587
SI_Error
1588
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindFileComment(
1589
SI_CHAR *& a_pData,
1590
bool a_bCopyStrings
1591
)
1592
{
1593
// there can only be a single file comment
1594
if (m_pFileComment) {
1595
return SI_OK;
1596
}
1597
1598
// Load the file comment as multi-line text, this will modify all of
1599
// the newline characters to be single \n chars
1600
if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1601
return SI_OK;
1602
}
1603
1604
// copy the string if necessary
1605
if (a_bCopyStrings) {
1606
SI_Error rc = CopyString(m_pFileComment);
1607
if (rc < 0) return rc;
1608
}
1609
1610
return SI_OK;
1611
}
1612
1613
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1614
bool
1615
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
1616
SI_CHAR *& a_pData,
1617
const SI_CHAR *& a_pSection,
1618
const SI_CHAR *& a_pKey,
1619
const SI_CHAR *& a_pVal,
1620
const SI_CHAR *& a_pComment
1621
) const
1622
{
1623
a_pComment = NULL;
1624
1625
bool bHaveValue = false;
1626
SI_CHAR * pTrail = NULL;
1627
while (*a_pData) {
1628
// skip spaces and empty lines
1629
while (*a_pData && IsSpace(*a_pData)) {
1630
++a_pData;
1631
}
1632
if (!*a_pData) {
1633
break;
1634
}
1635
1636
// skip processing of comment lines but keep a pointer to
1637
// the start of the comment.
1638
if (IsComment(*a_pData)) {
1639
LoadMultiLineText(a_pData, a_pComment, NULL, true);
1640
continue;
1641
}
1642
1643
// process section names
1644
if (*a_pData == '[') {
1645
// skip leading spaces
1646
++a_pData;
1647
while (*a_pData && IsSpace(*a_pData)) {
1648
++a_pData;
1649
}
1650
1651
// find the end of the section name (it may contain spaces)
1652
// and convert it to lowercase as necessary
1653
a_pSection = a_pData;
1654
while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1655
++a_pData;
1656
}
1657
1658
// if it's an invalid line, just skip it
1659
if (*a_pData != ']') {
1660
continue;
1661
}
1662
1663
// remove trailing spaces from the section
1664
pTrail = a_pData - 1;
1665
while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1666
--pTrail;
1667
}
1668
++pTrail;
1669
*pTrail = 0;
1670
1671
// skip to the end of the line
1672
++a_pData; // safe as checked that it == ']' above
1673
while (*a_pData && !IsNewLineChar(*a_pData)) {
1674
++a_pData;
1675
}
1676
1677
a_pKey = NULL;
1678
a_pVal = NULL;
1679
return true;
1680
}
1681
1682
// find the end of the key name (it may contain spaces)
1683
a_pKey = a_pData;
1684
while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1685
++a_pData;
1686
}
1687
// *a_pData is null, equals, or newline
1688
1689
// if no value and we don't allow no value, then invalid
1690
bHaveValue = (*a_pData == '=');
1691
if (!bHaveValue && !m_bAllowKeyOnly) {
1692
continue;
1693
}
1694
1695
// empty keys are invalid
1696
if (bHaveValue && a_pKey == a_pData) {
1697
while (*a_pData && !IsNewLineChar(*a_pData)) {
1698
++a_pData;
1699
}
1700
continue;
1701
}
1702
1703
// remove trailing spaces from the key
1704
pTrail = a_pData - 1;
1705
while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1706
--pTrail;
1707
}
1708
++pTrail;
1709
1710
if (bHaveValue) {
1711
// process the value
1712
*pTrail = 0;
1713
1714
// skip leading whitespace on the value
1715
++a_pData; // safe as checked that it == '=' above
1716
while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1717
++a_pData;
1718
}
1719
1720
// find the end of the value which is the end of this line
1721
a_pVal = a_pData;
1722
while (*a_pData && !IsNewLineChar(*a_pData)) {
1723
++a_pData;
1724
}
1725
1726
// remove trailing spaces from the value
1727
pTrail = a_pData - 1;
1728
if (*a_pData) { // prepare for the next round
1729
SkipNewLine(a_pData);
1730
}
1731
while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1732
--pTrail;
1733
}
1734
++pTrail;
1735
*pTrail = 0;
1736
1737
// check for multi-line entries
1738
if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1739
// skip the "<<<" to get the tag that will end the multiline
1740
const SI_CHAR* pTagName = a_pVal + 3;
1741
return LoadMultiLineText(a_pData, a_pVal, pTagName);
1742
}
1743
1744
// check for quoted values, we are not supporting escapes in quoted values (yet)
1745
if (m_bParseQuotes) {
1746
--pTrail;
1747
if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
1748
++a_pVal;
1749
*pTrail = 0;
1750
}
1751
}
1752
}
1753
else {
1754
// no value to process, just prepare for the next
1755
if (*a_pData) {
1756
SkipNewLine(a_pData);
1757
}
1758
*pTrail = 0;
1759
}
1760
1761
// return the standard entry
1762
return true;
1763
}
1764
1765
return false;
1766
}
1767
1768
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1769
bool
1770
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineTag(
1771
const SI_CHAR * a_pVal
1772
) const
1773
{
1774
// check for the "<<<" prefix for a multi-line entry
1775
if (*a_pVal++ != '<') return false;
1776
if (*a_pVal++ != '<') return false;
1777
if (*a_pVal++ != '<') return false;
1778
return true;
1779
}
1780
1781
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1782
bool
1783
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
1784
const SI_CHAR * a_pData
1785
) const
1786
{
1787
// data is multi-line if it has any of the following features:
1788
// * whitespace prefix
1789
// * embedded newlines
1790
// * whitespace suffix
1791
1792
// empty string
1793
if (!*a_pData) {
1794
return false;
1795
}
1796
1797
// check for prefix
1798
if (IsSpace(*a_pData)) {
1799
return true;
1800
}
1801
1802
// embedded newlines
1803
while (*a_pData) {
1804
if (IsNewLineChar(*a_pData)) {
1805
return true;
1806
}
1807
++a_pData;
1808
}
1809
1810
// check for suffix
1811
if (IsSpace(*--a_pData)) {
1812
return true;
1813
}
1814
1815
return false;
1816
}
1817
1818
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1819
bool
1820
CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::IsSingleLineQuotedValue(
1821
const SI_CHAR* a_pData
1822
) const
1823
{
1824
// data needs quoting if it starts or ends with whitespace
1825
// and doesn't have embedded newlines
1826
1827
// empty string
1828
if (!*a_pData) {
1829
return false;
1830
}
1831
1832
// check for prefix
1833
if (IsSpace(*a_pData)) {
1834
return true;
1835
}
1836
1837
// embedded newlines
1838
while (*a_pData) {
1839
if (IsNewLineChar(*a_pData)) {
1840
return false;
1841
}
1842
++a_pData;
1843
}
1844
1845
// check for suffix
1846
if (IsSpace(*--a_pData)) {
1847
return true;
1848
}
1849
1850
return false;
1851
}
1852
1853
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1854
bool
1855
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
1856
SI_CHAR a_c
1857
) const
1858
{
1859
return (a_c == '\n' || a_c == '\r');
1860
}
1861
1862
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1863
bool
1864
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
1865
SI_CHAR *& a_pData,
1866
const SI_CHAR *& a_pVal,
1867
const SI_CHAR * a_pTagName,
1868
bool a_bAllowBlankLinesInComment
1869
) const
1870
{
1871
// we modify this data to strip all newlines down to a single '\n'
1872
// character. This means that on Windows we need to strip out some
1873
// characters which will make the data shorter.
1874
// i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1875
// LINE1-LINE1\nLINE2-LINE2\0
1876
// The pDataLine entry is the pointer to the location in memory that
1877
// the current line needs to start to run following the existing one.
1878
// This may be the same as pCurrLine in which case no move is needed.
1879
SI_CHAR * pDataLine = a_pData;
1880
SI_CHAR * pCurrLine;
1881
1882
// value starts at the current line
1883
a_pVal = a_pData;
1884
1885
// find the end tag. This tag must start in column 1 and be
1886
// followed by a newline. We ignore any whitespace after the end
1887
// tag but not whitespace before it.
1888
SI_CHAR cEndOfLineChar = *a_pData;
1889
for(;;) {
1890
// if we are loading comments then we need a comment character as
1891
// the first character on every line
1892
if (!a_pTagName && !IsComment(*a_pData)) {
1893
// if we aren't allowing blank lines then we're done
1894
if (!a_bAllowBlankLinesInComment) {
1895
break;
1896
}
1897
1898
// if we are allowing blank lines then we only include them
1899
// in this comment if another comment follows, so read ahead
1900
// to find out.
1901
SI_CHAR * pCurr = a_pData;
1902
int nNewLines = 0;
1903
while (IsSpace(*pCurr)) {
1904
if (IsNewLineChar(*pCurr)) {
1905
++nNewLines;
1906
SkipNewLine(pCurr);
1907
}
1908
else {
1909
++pCurr;
1910
}
1911
}
1912
1913
// we have a comment, add the blank lines to the output
1914
// and continue processing from here
1915
if (IsComment(*pCurr)) {
1916
for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1917
a_pData = pCurr;
1918
continue;
1919
}
1920
1921
// the comment ends here
1922
break;
1923
}
1924
1925
// find the end of this line
1926
pCurrLine = a_pData;
1927
while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1928
1929
// move this line down to the location that it should be if necessary
1930
if (pDataLine < pCurrLine) {
1931
size_t nLen = (size_t) (a_pData - pCurrLine);
1932
memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1933
pDataLine[nLen] = '\0';
1934
}
1935
1936
// end the line with a NULL
1937
cEndOfLineChar = *a_pData;
1938
*a_pData = 0;
1939
1940
// if are looking for a tag then do the check now. This is done before
1941
// checking for end of the data, so that if we have the tag at the end
1942
// of the data then the tag is removed correctly.
1943
if (a_pTagName) {
1944
// strip whitespace from the end of this tag
1945
SI_CHAR* pc = a_pData - 1;
1946
while (pc > pDataLine && IsSpace(*pc)) --pc;
1947
SI_CHAR ch = *++pc;
1948
*pc = 0;
1949
1950
if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
1951
break;
1952
}
1953
1954
*pc = ch;
1955
}
1956
1957
// if we are at the end of the data then we just automatically end
1958
// this entry and return the current data.
1959
if (!cEndOfLineChar) {
1960
return true;
1961
}
1962
1963
// otherwise we need to process this newline to ensure that it consists
1964
// of just a single \n character.
1965
pDataLine += (a_pData - pCurrLine);
1966
*a_pData = cEndOfLineChar;
1967
SkipNewLine(a_pData);
1968
*pDataLine++ = '\n';
1969
}
1970
1971
// if we didn't find a comment at all then return false
1972
if (a_pVal == a_pData) {
1973
a_pVal = NULL;
1974
return false;
1975
}
1976
1977
// the data (which ends at the end of the last line) needs to be
1978
// null-terminated BEFORE before the newline character(s). If the
1979
// user wants a new line in the multi-line data then they need to
1980
// add an empty line before the tag.
1981
*--pDataLine = '\0';
1982
1983
// if looking for a tag and if we aren't at the end of the data,
1984
// then move a_pData to the start of the next line.
1985
if (a_pTagName && cEndOfLineChar) {
1986
SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1987
*a_pData = cEndOfLineChar;
1988
SkipNewLine(a_pData);
1989
}
1990
1991
return true;
1992
}
1993
1994
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1995
SI_Error
1996
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
1997
const SI_CHAR *& a_pString
1998
)
1999
{
2000
size_t uLen = 0;
2001
if (sizeof(SI_CHAR) == sizeof(char)) {
2002
uLen = strlen((const char *)a_pString);
2003
}
2004
else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
2005
uLen = wcslen((const wchar_t *)a_pString);
2006
}
2007
else {
2008
for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
2009
}
2010
++uLen; // NULL character
2011
SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
2012
if (!pCopy) {
2013
return SI_NOMEM;
2014
}
2015
memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
2016
m_strings.push_back(pCopy);
2017
a_pString = pCopy;
2018
return SI_OK;
2019
}
2020
2021
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2022
SI_Error
2023
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
2024
const SI_CHAR * a_pSection,
2025
const SI_CHAR * a_pKey,
2026
const SI_CHAR * a_pValue,
2027
const SI_CHAR * a_pComment,
2028
bool a_bForceReplace,
2029
bool a_bCopyStrings
2030
)
2031
{
2032
SI_Error rc;
2033
bool bInserted = false;
2034
2035
SI_ASSERT(!a_pComment || IsComment(*a_pComment));
2036
2037
// if we are copying strings then make a copy of the comment now
2038
// because we will need it when we add the entry.
2039
if (a_bCopyStrings && a_pComment) {
2040
rc = CopyString(a_pComment);
2041
if (rc < 0) return rc;
2042
}
2043
2044
// create the section entry if necessary
2045
typename TSection::iterator iSection = m_data.find(a_pSection);
2046
if (iSection == m_data.end()) {
2047
// if the section doesn't exist then we need a copy as the
2048
// string needs to last beyond the end of this function
2049
if (a_bCopyStrings) {
2050
rc = CopyString(a_pSection);
2051
if (rc < 0) return rc;
2052
}
2053
2054
// only set the comment if this is a section only entry
2055
Entry oSection(a_pSection, ++m_nOrder);
2056
if (a_pComment && !a_pKey) {
2057
oSection.pComment = a_pComment;
2058
}
2059
2060
typename TSection::value_type oEntry(oSection, TKeyVal());
2061
typedef typename TSection::iterator SectionIterator;
2062
std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
2063
iSection = i.first;
2064
bInserted = true;
2065
}
2066
if (!a_pKey) {
2067
// section only entries are specified with pItem as NULL
2068
return bInserted ? SI_INSERTED : SI_UPDATED;
2069
}
2070
2071
// check for existence of the key
2072
TKeyVal & keyval = iSection->second;
2073
typename TKeyVal::iterator iKey = keyval.find(a_pKey);
2074
bInserted = iKey == keyval.end();
2075
2076
// remove all existing entries but save the load order and
2077
// comment of the first entry
2078
int nLoadOrder = ++m_nOrder;
2079
if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
2080
const SI_CHAR * pComment = NULL;
2081
while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
2082
if (iKey->first.nOrder < nLoadOrder) {
2083
nLoadOrder = iKey->first.nOrder;
2084
pComment = iKey->first.pComment;
2085
}
2086
++iKey;
2087
}
2088
if (pComment) {
2089
DeleteString(a_pComment);
2090
a_pComment = pComment;
2091
CopyString(a_pComment);
2092
}
2093
Delete(a_pSection, a_pKey);
2094
iKey = keyval.end();
2095
}
2096
2097
// values need to be a valid string, even if they are an empty string
2098
if (!a_pValue) {
2099
a_pValue = &m_cEmptyString;
2100
}
2101
2102
// make string copies if necessary
2103
bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
2104
if (a_bCopyStrings) {
2105
if (bForceCreateNewKey || iKey == keyval.end()) {
2106
// if the key doesn't exist then we need a copy as the
2107
// string needs to last beyond the end of this function
2108
// because we will be inserting the key next
2109
rc = CopyString(a_pKey);
2110
if (rc < 0) return rc;
2111
}
2112
2113
// we always need a copy of the value
2114
rc = CopyString(a_pValue);
2115
if (rc < 0) return rc;
2116
}
2117
2118
// create the key entry
2119
if (iKey == keyval.end() || bForceCreateNewKey) {
2120
Entry oKey(a_pKey, nLoadOrder);
2121
if (a_pComment) {
2122
oKey.pComment = a_pComment;
2123
}
2124
typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
2125
iKey = keyval.insert(oEntry);
2126
}
2127
2128
iKey->second = a_pValue;
2129
return bInserted ? SI_INSERTED : SI_UPDATED;
2130
}
2131
2132
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2133
const SI_CHAR *
2134
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetValue(
2135
const SI_CHAR * a_pSection,
2136
const SI_CHAR * a_pKey,
2137
const SI_CHAR * a_pDefault,
2138
bool * a_pHasMultiple
2139
) const
2140
{
2141
if (a_pHasMultiple) {
2142
*a_pHasMultiple = false;
2143
}
2144
if (!a_pSection || !a_pKey) {
2145
return a_pDefault;
2146
}
2147
typename TSection::const_iterator iSection = m_data.find(a_pSection);
2148
if (iSection == m_data.end()) {
2149
return a_pDefault;
2150
}
2151
typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2152
if (iKeyVal == iSection->second.end()) {
2153
return a_pDefault;
2154
}
2155
2156
// check for multiple entries with the same key
2157
if (m_bAllowMultiKey && a_pHasMultiple) {
2158
typename TKeyVal::const_iterator iTemp = iKeyVal;
2159
if (++iTemp != iSection->second.end()) {
2160
if (!IsLess(a_pKey, iTemp->first.pItem)) {
2161
*a_pHasMultiple = true;
2162
}
2163
}
2164
}
2165
2166
return iKeyVal->second;
2167
}
2168
2169
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2170
long
2171
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
2172
const SI_CHAR * a_pSection,
2173
const SI_CHAR * a_pKey,
2174
long a_nDefault,
2175
bool * a_pHasMultiple
2176
) const
2177
{
2178
// return the default if we don't have a value
2179
const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2180
if (!pszValue || !*pszValue) return a_nDefault;
2181
2182
// convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2183
char szValue[64] = { 0 };
2184
SI_CONVERTER c(m_bStoreIsUtf8);
2185
if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2186
return a_nDefault;
2187
}
2188
2189
// handle the value as hex if prefaced with "0x"
2190
long nValue = a_nDefault;
2191
char * pszSuffix = szValue;
2192
if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2193
if (!szValue[2]) return a_nDefault;
2194
nValue = strtol(&szValue[2], &pszSuffix, 16);
2195
}
2196
else {
2197
nValue = strtol(szValue, &pszSuffix, 10);
2198
}
2199
2200
// any invalid strings will return the default value
2201
if (*pszSuffix) {
2202
return a_nDefault;
2203
}
2204
2205
return nValue;
2206
}
2207
2208
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2209
SI_Error
2210
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
2211
const SI_CHAR * a_pSection,
2212
const SI_CHAR * a_pKey,
2213
long a_nValue,
2214
const SI_CHAR * a_pComment,
2215
bool a_bUseHex,
2216
bool a_bForceReplace
2217
)
2218
{
2219
// use SetValue to create sections
2220
if (!a_pSection || !a_pKey) return SI_FAIL;
2221
2222
// convert to an ASCII string
2223
char szInput[64];
2224
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2225
sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2226
#else // !__STDC_WANT_SECURE_LIB__
2227
snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2228
#endif // __STDC_WANT_SECURE_LIB__
2229
2230
// convert to output text
2231
SI_CHAR szOutput[64];
2232
SI_CONVERTER c(m_bStoreIsUtf8);
2233
c.ConvertFromStore(szInput, strlen(szInput) + 1,
2234
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2235
2236
// actually add it
2237
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2238
}
2239
2240
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2241
double
2242
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
2243
const SI_CHAR * a_pSection,
2244
const SI_CHAR * a_pKey,
2245
double a_nDefault,
2246
bool * a_pHasMultiple
2247
) const
2248
{
2249
// return the default if we don't have a value
2250
const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2251
if (!pszValue || !*pszValue) return a_nDefault;
2252
2253
// convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2254
char szValue[64] = { 0 };
2255
SI_CONVERTER c(m_bStoreIsUtf8);
2256
if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2257
return a_nDefault;
2258
}
2259
2260
char * pszSuffix = NULL;
2261
double nValue = strtod(szValue, &pszSuffix);
2262
2263
// any invalid strings will return the default value
2264
if (!pszSuffix || *pszSuffix) {
2265
return a_nDefault;
2266
}
2267
2268
return nValue;
2269
}
2270
2271
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2272
SI_Error
2273
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
2274
const SI_CHAR * a_pSection,
2275
const SI_CHAR * a_pKey,
2276
double a_nValue,
2277
const SI_CHAR * a_pComment,
2278
bool a_bForceReplace
2279
)
2280
{
2281
// use SetValue to create sections
2282
if (!a_pSection || !a_pKey) return SI_FAIL;
2283
2284
// convert to an ASCII string
2285
char szInput[64];
2286
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2287
sprintf_s(szInput, "%f", a_nValue);
2288
#else // !__STDC_WANT_SECURE_LIB__
2289
snprintf(szInput, sizeof(szInput), "%f", a_nValue);
2290
#endif // __STDC_WANT_SECURE_LIB__
2291
2292
// convert to output text
2293
SI_CHAR szOutput[64];
2294
SI_CONVERTER c(m_bStoreIsUtf8);
2295
c.ConvertFromStore(szInput, strlen(szInput) + 1,
2296
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2297
2298
// actually add it
2299
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2300
}
2301
2302
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2303
bool
2304
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
2305
const SI_CHAR * a_pSection,
2306
const SI_CHAR * a_pKey,
2307
bool a_bDefault,
2308
bool * a_pHasMultiple
2309
) const
2310
{
2311
// return the default if we don't have a value
2312
const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2313
if (!pszValue || !*pszValue) return a_bDefault;
2314
2315
// we only look at the minimum number of characters
2316
switch (pszValue[0]) {
2317
case 't': case 'T': // true
2318
case 'y': case 'Y': // yes
2319
case '1': // 1 (one)
2320
return true;
2321
2322
case 'f': case 'F': // false
2323
case 'n': case 'N': // no
2324
case '0': // 0 (zero)
2325
return false;
2326
2327
case 'o': case 'O':
2328
if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
2329
if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
2330
break;
2331
}
2332
2333
// no recognized value, return the default
2334
return a_bDefault;
2335
}
2336
2337
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2338
SI_Error
2339
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
2340
const SI_CHAR * a_pSection,
2341
const SI_CHAR * a_pKey,
2342
bool a_bValue,
2343
const SI_CHAR * a_pComment,
2344
bool a_bForceReplace
2345
)
2346
{
2347
// use SetValue to create sections
2348
if (!a_pSection || !a_pKey) return SI_FAIL;
2349
2350
// convert to an ASCII string
2351
const char * pszInput = a_bValue ? "true" : "false";
2352
2353
// convert to output text
2354
SI_CHAR szOutput[64];
2355
SI_CONVERTER c(m_bStoreIsUtf8);
2356
c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2357
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2358
2359
// actually add it
2360
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2361
}
2362
2363
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2364
bool
2365
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
2366
const SI_CHAR * a_pSection,
2367
const SI_CHAR * a_pKey,
2368
TNamesDepend & a_values
2369
) const
2370
{
2371
a_values.clear();
2372
2373
if (!a_pSection || !a_pKey) {
2374
return false;
2375
}
2376
typename TSection::const_iterator iSection = m_data.find(a_pSection);
2377
if (iSection == m_data.end()) {
2378
return false;
2379
}
2380
typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2381
if (iKeyVal == iSection->second.end()) {
2382
return false;
2383
}
2384
2385
// insert all values for this key
2386
a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2387
if (m_bAllowMultiKey) {
2388
++iKeyVal;
2389
while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2390
a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2391
++iKeyVal;
2392
}
2393
}
2394
2395
return true;
2396
}
2397
2398
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2399
int
2400
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSectionSize(
2401
const SI_CHAR * a_pSection
2402
) const
2403
{
2404
if (!a_pSection) {
2405
return -1;
2406
}
2407
2408
typename TSection::const_iterator iSection = m_data.find(a_pSection);
2409
if (iSection == m_data.end()) {
2410
return -1;
2411
}
2412
const TKeyVal & section = iSection->second;
2413
2414
// if multi-key isn't permitted then the section size is
2415
// the number of keys that we have.
2416
if (!m_bAllowMultiKey || section.empty()) {
2417
return (int) section.size();
2418
}
2419
2420
// otherwise we need to count them
2421
int nCount = 0;
2422
const SI_CHAR * pLastKey = NULL;
2423
typename TKeyVal::const_iterator iKeyVal = section.begin();
2424
for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2425
if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2426
++nCount;
2427
pLastKey = iKeyVal->first.pItem;
2428
}
2429
}
2430
return nCount;
2431
}
2432
2433
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2434
const typename CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::TKeyVal *
2435
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
2436
const SI_CHAR * a_pSection
2437
) const
2438
{
2439
if (a_pSection) {
2440
typename TSection::const_iterator i = m_data.find(a_pSection);
2441
if (i != m_data.end()) {
2442
return &(i->second);
2443
}
2444
}
2445
return 0;
2446
}
2447
2448
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2449
size_t
2450
CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::GetKeyCount() const
2451
{
2452
size_t count = 0;
2453
typename TSection::const_iterator i = m_data.begin();
2454
for (; i != m_data.end(); ++i)
2455
count += i->second.size();
2456
return count;
2457
}
2458
2459
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2460
void
2461
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(
2462
TNamesDepend & a_names
2463
) const
2464
{
2465
a_names.clear();
2466
typename TSection::const_iterator i = m_data.begin();
2467
for (int n = 0; i != m_data.end(); ++i, ++n ) {
2468
a_names.push_back(i->first);
2469
}
2470
}
2471
2472
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2473
bool
2474
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllKeys(
2475
const SI_CHAR * a_pSection,
2476
TNamesDepend & a_names
2477
) const
2478
{
2479
a_names.clear();
2480
2481
if (!a_pSection) {
2482
return false;
2483
}
2484
2485
typename TSection::const_iterator iSection = m_data.find(a_pSection);
2486
if (iSection == m_data.end()) {
2487
return false;
2488
}
2489
2490
const TKeyVal & section = iSection->second;
2491
const SI_CHAR * pLastKey = NULL;
2492
typename TKeyVal::const_iterator iKeyVal = section.begin();
2493
for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
2494
if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2495
a_names.push_back(iKeyVal->first);
2496
pLastKey = iKeyVal->first.pItem;
2497
}
2498
}
2499
2500
return true;
2501
}
2502
2503
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2504
SI_Error
2505
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2506
const char * a_pszFile,
2507
bool a_bAddSignature
2508
) const
2509
{
2510
FILE * fp = NULL;
2511
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2512
fopen_s(&fp, a_pszFile, "wb");
2513
#else // !__STDC_WANT_SECURE_LIB__
2514
fp = fopen(a_pszFile, "wb");
2515
#endif // __STDC_WANT_SECURE_LIB__
2516
if (!fp) return SI_FILE;
2517
SI_Error rc = SaveFile(fp, a_bAddSignature);
2518
fclose(fp);
2519
return rc;
2520
}
2521
2522
#ifdef SI_HAS_WIDE_FILE
2523
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2524
SI_Error
2525
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2526
const SI_WCHAR_T * a_pwszFile,
2527
bool a_bAddSignature
2528
) const
2529
{
2530
#ifdef _WIN32
2531
FILE * fp = NULL;
2532
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2533
_wfopen_s(&fp, a_pwszFile, L"wb");
2534
#else // !__STDC_WANT_SECURE_LIB__
2535
fp = _wfopen(a_pwszFile, L"wb");
2536
#endif // __STDC_WANT_SECURE_LIB__
2537
if (!fp) return SI_FILE;
2538
SI_Error rc = SaveFile(fp, a_bAddSignature);
2539
fclose(fp);
2540
return rc;
2541
#else // !_WIN32 (therefore SI_CONVERT_ICU)
2542
char szFile[256];
2543
u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2544
return SaveFile(szFile, a_bAddSignature);
2545
#endif // _WIN32
2546
}
2547
#endif // SI_HAS_WIDE_FILE
2548
2549
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2550
SI_Error
2551
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SaveFile(
2552
FILE * a_pFile,
2553
bool a_bAddSignature
2554
) const
2555
{
2556
FileWriter writer(a_pFile);
2557
return Save(writer, a_bAddSignature);
2558
}
2559
2560
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2561
SI_Error
2562
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
2563
OutputWriter & a_oOutput,
2564
bool a_bAddSignature
2565
) const
2566
{
2567
Converter convert(m_bStoreIsUtf8);
2568
2569
// add the UTF-8 signature if it is desired
2570
if (m_bStoreIsUtf8 && a_bAddSignature) {
2571
a_oOutput.Write(SI_UTF8_SIGNATURE);
2572
}
2573
2574
// get all of the sections sorted in load order
2575
TNamesDepend oSections;
2576
GetAllSections(oSections);
2577
#if defined(_MSC_VER) && _MSC_VER <= 1200
2578
oSections.sort();
2579
#elif defined(__BORLANDC__)
2580
oSections.sort(Entry::LoadOrder());
2581
#else
2582
oSections.sort(typename Entry::LoadOrder());
2583
#endif
2584
2585
// if there is an empty section name, then it must be written out first
2586
// regardless of the load order
2587
typename TNamesDepend::iterator is = oSections.begin();
2588
for (; is != oSections.end(); ++is) {
2589
if (!*is->pItem) {
2590
// move the empty section name to the front of the section list
2591
if (is != oSections.begin()) {
2592
oSections.splice(oSections.begin(), oSections, is, std::next(is));
2593
}
2594
break;
2595
}
2596
}
2597
2598
// write the file comment if we have one
2599
bool bNeedNewLine = false;
2600
if (m_pFileComment) {
2601
if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2602
return SI_FAIL;
2603
}
2604
bNeedNewLine = true;
2605
}
2606
2607
// iterate through our sections and output the data
2608
typename TNamesDepend::const_iterator iSection = oSections.begin();
2609
for ( ; iSection != oSections.end(); ++iSection ) {
2610
// write out the comment if there is one
2611
if (iSection->pComment) {
2612
if (bNeedNewLine) {
2613
a_oOutput.Write(SI_NEWLINE_A);
2614
a_oOutput.Write(SI_NEWLINE_A);
2615
}
2616
if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2617
return SI_FAIL;
2618
}
2619
bNeedNewLine = false;
2620
}
2621
2622
if (bNeedNewLine) {
2623
a_oOutput.Write(SI_NEWLINE_A);
2624
a_oOutput.Write(SI_NEWLINE_A);
2625
bNeedNewLine = false;
2626
}
2627
2628
// write the section (unless there is no section name)
2629
if (*iSection->pItem) {
2630
if (!convert.ConvertToStore(iSection->pItem)) {
2631
return SI_FAIL;
2632
}
2633
a_oOutput.Write("[");
2634
a_oOutput.Write(convert.Data());
2635
a_oOutput.Write("]");
2636
a_oOutput.Write(SI_NEWLINE_A);
2637
}
2638
2639
// get all of the keys sorted in load order
2640
TNamesDepend oKeys;
2641
GetAllKeys(iSection->pItem, oKeys);
2642
#if defined(_MSC_VER) && _MSC_VER <= 1200
2643
oKeys.sort();
2644
#elif defined(__BORLANDC__)
2645
oKeys.sort(Entry::LoadOrder());
2646
#else
2647
oKeys.sort(typename Entry::LoadOrder());
2648
#endif
2649
2650
// write all keys and values
2651
typename TNamesDepend::const_iterator iKey = oKeys.begin();
2652
for ( ; iKey != oKeys.end(); ++iKey) {
2653
// get all values for this key
2654
TNamesDepend oValues;
2655
GetAllValues(iSection->pItem, iKey->pItem, oValues);
2656
2657
typename TNamesDepend::const_iterator iValue = oValues.begin();
2658
for ( ; iValue != oValues.end(); ++iValue) {
2659
// write out the comment if there is one
2660
if (iValue->pComment) {
2661
a_oOutput.Write(SI_NEWLINE_A);
2662
if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2663
return SI_FAIL;
2664
}
2665
}
2666
2667
// write the key
2668
if (!convert.ConvertToStore(iKey->pItem)) {
2669
return SI_FAIL;
2670
}
2671
a_oOutput.Write(convert.Data());
2672
2673
// write the value as long
2674
if (*iValue->pItem || !m_bAllowKeyOnly) {
2675
if (!convert.ConvertToStore(iValue->pItem)) {
2676
return SI_FAIL;
2677
}
2678
a_oOutput.Write(m_bSpaces ? " = " : "=");
2679
if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
2680
// the only way to preserve external whitespace on a value (i.e. before or after)
2681
// is to quote it. This is simple quoting, we don't escape quotes within the data.
2682
a_oOutput.Write("\"");
2683
a_oOutput.Write(convert.Data());
2684
a_oOutput.Write("\"");
2685
}
2686
else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2687
// multi-line data needs to be processed specially to ensure
2688
// that we use the correct newline format for the current system
2689
a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2690
if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2691
return SI_FAIL;
2692
}
2693
a_oOutput.Write("END_OF_TEXT");
2694
}
2695
else {
2696
a_oOutput.Write(convert.Data());
2697
}
2698
}
2699
a_oOutput.Write(SI_NEWLINE_A);
2700
}
2701
}
2702
2703
bNeedNewLine = true;
2704
}
2705
2706
return SI_OK;
2707
}
2708
2709
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2710
bool
2711
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::OutputMultiLineText(
2712
OutputWriter & a_oOutput,
2713
Converter & a_oConverter,
2714
const SI_CHAR * a_pText
2715
) const
2716
{
2717
const SI_CHAR * pEndOfLine;
2718
SI_CHAR cEndOfLineChar = *a_pText;
2719
while (cEndOfLineChar) {
2720
// find the end of this line
2721
pEndOfLine = a_pText;
2722
for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2723
cEndOfLineChar = *pEndOfLine;
2724
2725
// temporarily null terminate, convert and output the line
2726
*const_cast<SI_CHAR*>(pEndOfLine) = 0;
2727
if (!a_oConverter.ConvertToStore(a_pText)) {
2728
return false;
2729
}
2730
*const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2731
a_pText += (pEndOfLine - a_pText) + 1;
2732
a_oOutput.Write(a_oConverter.Data());
2733
a_oOutput.Write(SI_NEWLINE_A);
2734
}
2735
return true;
2736
}
2737
2738
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2739
bool
2740
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
2741
const SI_CHAR * a_pSection,
2742
const SI_CHAR * a_pKey,
2743
bool a_bRemoveEmpty
2744
)
2745
{
2746
return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
2747
}
2748
2749
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2750
bool
2751
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(
2752
const SI_CHAR * a_pSection,
2753
const SI_CHAR * a_pKey,
2754
const SI_CHAR * a_pValue,
2755
bool a_bRemoveEmpty
2756
)
2757
{
2758
if (!a_pSection) {
2759
return false;
2760
}
2761
2762
typename TSection::iterator iSection = m_data.find(a_pSection);
2763
if (iSection == m_data.end()) {
2764
return false;
2765
}
2766
2767
// remove a single key if we have a keyname
2768
if (a_pKey) {
2769
typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2770
if (iKeyVal == iSection->second.end()) {
2771
return false;
2772
}
2773
2774
const static SI_STRLESS isLess = SI_STRLESS();
2775
2776
// remove any copied strings and then the key
2777
typename TKeyVal::iterator iDelete;
2778
bool bDeleted = false;
2779
do {
2780
iDelete = iKeyVal++;
2781
2782
if(a_pValue == NULL ||
2783
(isLess(a_pValue, iDelete->second) == false &&
2784
isLess(iDelete->second, a_pValue) == false)) {
2785
DeleteString(iDelete->first.pItem);
2786
DeleteString(iDelete->second);
2787
iSection->second.erase(iDelete);
2788
bDeleted = true;
2789
}
2790
}
2791
while (iKeyVal != iSection->second.end()
2792
&& !IsLess(a_pKey, iKeyVal->first.pItem));
2793
2794
if(!bDeleted) {
2795
return false;
2796
}
2797
2798
// done now if the section is not empty or we are not pruning away
2799
// the empty sections. Otherwise let it fall through into the section
2800
// deletion code
2801
if (!a_bRemoveEmpty || !iSection->second.empty()) {
2802
return true;
2803
}
2804
}
2805
else {
2806
// delete all copied strings from this section. The actual
2807
// entries will be removed when the section is removed.
2808
typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2809
for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2810
DeleteString(iKeyVal->first.pItem);
2811
DeleteString(iKeyVal->second);
2812
}
2813
}
2814
2815
// delete the section itself
2816
DeleteString(iSection->first.pItem);
2817
m_data.erase(iSection);
2818
2819
return true;
2820
}
2821
2822
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2823
void
2824
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
2825
const SI_CHAR * a_pString
2826
)
2827
{
2828
// strings may exist either inside the data block, or they will be
2829
// individually allocated and stored in m_strings. We only physically
2830
// delete those stored in m_strings.
2831
if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2832
typename TNamesDepend::iterator i = m_strings.begin();
2833
for (;i != m_strings.end(); ++i) {
2834
if (a_pString == i->pItem) {
2835
delete[] const_cast<SI_CHAR*>(i->pItem);
2836
m_strings.erase(i);
2837
break;
2838
}
2839
}
2840
}
2841
}
2842
2843
// ---------------------------------------------------------------------------
2844
// CONVERSION FUNCTIONS
2845
// ---------------------------------------------------------------------------
2846
2847
// Defines the conversion classes for different libraries. Before including
2848
// SimpleIni.h, set the converter that you wish you use by defining one of the
2849
// following symbols.
2850
//
2851
// SI_NO_CONVERSION Do not make the "W" wide character version of the
2852
// library available. Only CSimpleIniA etc is defined.
2853
// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2854
// the accompanying files ConvertUTF.h/c
2855
// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2856
// ICU headers on include path and icuuc.lib
2857
// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2858
2859
#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2860
# ifdef _WIN32
2861
# define SI_CONVERT_WIN32
2862
# else
2863
# define SI_CONVERT_GENERIC
2864
# endif
2865
#endif
2866
2867
/**
2868
* Generic case-sensitive less than comparison. This class returns numerically
2869
* ordered ASCII case-sensitive text for all possible sizes and types of
2870
* SI_CHAR.
2871
*/
2872
template<class SI_CHAR>
2873
struct SI_GenericCase {
2874
bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2875
long cmp;
2876
for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2877
cmp = (long) *pLeft - (long) *pRight;
2878
if (cmp != 0) {
2879
return cmp < 0;
2880
}
2881
}
2882
return *pRight != 0;
2883
}
2884
};
2885
2886
/**
2887
* Generic ASCII case-insensitive less than comparison. This class returns
2888
* numerically ordered ASCII case-insensitive text for all possible sizes
2889
* and types of SI_CHAR. It is not safe for MBCS text comparison where
2890
* ASCII A-Z characters are used in the encoding of multi-byte characters.
2891
*/
2892
template<class SI_CHAR>
2893
struct SI_GenericNoCase {
2894
inline SI_CHAR locase(SI_CHAR ch) const {
2895
return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2896
}
2897
bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2898
long cmp;
2899
for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2900
cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2901
if (cmp != 0) {
2902
return cmp < 0;
2903
}
2904
}
2905
return *pRight != 0;
2906
}
2907
};
2908
2909
/**
2910
* Null conversion class for MBCS/UTF-8 to char (or equivalent).
2911
*/
2912
template<class SI_CHAR>
2913
class SI_ConvertA {
2914
bool m_bStoreIsUtf8;
2915
protected:
2916
SI_ConvertA() { }
2917
public:
2918
SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2919
2920
/* copy and assignment */
2921
SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2922
SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2923
m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2924
return *this;
2925
}
2926
2927
/** Calculate the number of SI_CHAR required for converting the input
2928
* from the storage format. The storage format is always UTF-8 or MBCS.
2929
*
2930
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
2931
* @param a_uInputDataLen Length of storage format data in bytes. This
2932
* must be the actual length of the data, including
2933
* NULL byte if NULL terminated string is required.
2934
* @return Number of SI_CHAR required by the string when
2935
* converted. If there are embedded NULL bytes in the
2936
* input data, only the string up and not including
2937
* the NULL byte will be converted.
2938
* @return -1 cast to size_t on a conversion error.
2939
*/
2940
size_t SizeFromStore(
2941
const char * a_pInputData,
2942
size_t a_uInputDataLen)
2943
{
2944
(void)a_pInputData;
2945
SI_ASSERT(a_uInputDataLen != (size_t) -1);
2946
2947
// ASCII/MBCS/UTF-8 needs no conversion
2948
return a_uInputDataLen;
2949
}
2950
2951
/** Convert the input string from the storage format to SI_CHAR.
2952
* The storage format is always UTF-8 or MBCS.
2953
*
2954
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
2955
* @param a_uInputDataLen Length of storage format data in bytes. This
2956
* must be the actual length of the data, including
2957
* NULL byte if NULL terminated string is required.
2958
* @param a_pOutputData Pointer to the output buffer to received the
2959
* converted data.
2960
* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
2961
* @return true if all of the input data was successfully
2962
* converted.
2963
*/
2964
bool ConvertFromStore(
2965
const char * a_pInputData,
2966
size_t a_uInputDataLen,
2967
SI_CHAR * a_pOutputData,
2968
size_t a_uOutputDataSize)
2969
{
2970
// ASCII/MBCS/UTF-8 needs no conversion
2971
if (a_uInputDataLen > a_uOutputDataSize) {
2972
return false;
2973
}
2974
memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2975
return true;
2976
}
2977
2978
/** Calculate the number of char required by the storage format of this
2979
* data. The storage format is always UTF-8 or MBCS.
2980
*
2981
* @param a_pInputData NULL terminated string to calculate the number of
2982
* bytes required to be converted to storage format.
2983
* @return Number of bytes required by the string when
2984
* converted to storage format. This size always
2985
* includes space for the terminating NULL character.
2986
* @return -1 cast to size_t on a conversion error.
2987
*/
2988
size_t SizeToStore(
2989
const SI_CHAR * a_pInputData)
2990
{
2991
// ASCII/MBCS/UTF-8 needs no conversion
2992
return strlen((const char *)a_pInputData) + 1;
2993
}
2994
2995
/** Convert the input string to the storage format of this data.
2996
* The storage format is always UTF-8 or MBCS.
2997
*
2998
* @param a_pInputData NULL terminated source string to convert. All of
2999
* the data will be converted including the
3000
* terminating NULL character.
3001
* @param a_pOutputData Pointer to the buffer to receive the converted
3002
* string.
3003
* @param a_uOutputDataSize Size of the output buffer in char.
3004
* @return true if all of the input data, including the
3005
* terminating NULL character was successfully
3006
* converted.
3007
*/
3008
bool ConvertToStore(
3009
const SI_CHAR * a_pInputData,
3010
char * a_pOutputData,
3011
size_t a_uOutputDataSize)
3012
{
3013
// calc input string length (SI_CHAR type and size independent)
3014
size_t uInputLen = strlen((const char *)a_pInputData) + 1;
3015
if (uInputLen > a_uOutputDataSize) {
3016
return false;
3017
}
3018
3019
// ascii/UTF-8 needs no conversion
3020
memcpy(a_pOutputData, a_pInputData, uInputLen);
3021
return true;
3022
}
3023
};
3024
3025
3026
// ---------------------------------------------------------------------------
3027
// SI_CONVERT_GENERIC
3028
// ---------------------------------------------------------------------------
3029
#ifdef SI_CONVERT_GENERIC
3030
3031
#define SI_Case SI_GenericCase
3032
#define SI_NoCase SI_GenericNoCase
3033
3034
#include <wchar.h>
3035
#include "ConvertUTF.h"
3036
3037
/**
3038
* Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference
3039
* library functions. This can be used on all platforms.
3040
*/
3041
template<class SI_CHAR>
3042
class SI_ConvertW {
3043
bool m_bStoreIsUtf8;
3044
protected:
3045
SI_ConvertW() { }
3046
public:
3047
SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
3048
3049
/* copy and assignment */
3050
SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3051
SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3052
m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
3053
return *this;
3054
}
3055
3056
/** Calculate the number of SI_CHAR required for converting the input
3057
* from the storage format. The storage format is always UTF-8 or MBCS.
3058
*
3059
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
3060
* @param a_uInputDataLen Length of storage format data in bytes. This
3061
* must be the actual length of the data, including
3062
* NULL byte if NULL terminated string is required.
3063
* @return Number of SI_CHAR required by the string when
3064
* converted. If there are embedded NULL bytes in the
3065
* input data, only the string up and not including
3066
* the NULL byte will be converted.
3067
* @return -1 cast to size_t on a conversion error.
3068
*/
3069
size_t SizeFromStore(
3070
const char * a_pInputData,
3071
size_t a_uInputDataLen)
3072
{
3073
SI_ASSERT(a_uInputDataLen != (size_t) -1);
3074
3075
if (m_bStoreIsUtf8) {
3076
// worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
3077
// so we just return the same number of characters required as for
3078
// the source text.
3079
return a_uInputDataLen;
3080
}
3081
3082
#if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux))
3083
// fall back processing for platforms that don't support a NULL dest to mbstowcs
3084
// worst case scenario is 1:1, this will be a sufficient buffer size
3085
(void)a_pInputData;
3086
return a_uInputDataLen;
3087
#else
3088
// get the actual required buffer size
3089
return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
3090
#endif
3091
}
3092
3093
/** Convert the input string from the storage format to SI_CHAR.
3094
* The storage format is always UTF-8 or MBCS.
3095
*
3096
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
3097
* @param a_uInputDataLen Length of storage format data in bytes. This
3098
* must be the actual length of the data, including
3099
* NULL byte if NULL terminated string is required.
3100
* @param a_pOutputData Pointer to the output buffer to received the
3101
* converted data.
3102
* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
3103
* @return true if all of the input data was successfully
3104
* converted.
3105
*/
3106
bool ConvertFromStore(
3107
const char * a_pInputData,
3108
size_t a_uInputDataLen,
3109
SI_CHAR * a_pOutputData,
3110
size_t a_uOutputDataSize)
3111
{
3112
if (m_bStoreIsUtf8) {
3113
// This uses the Unicode reference implementation to do the
3114
// conversion from UTF-8 to wchar_t. The required files are
3115
// ConvertUTF.h and ConvertUTF.c which should be included in
3116
// the distribution but are publicly available from unicode.org
3117
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3118
ConversionResult retval;
3119
const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
3120
if (sizeof(wchar_t) == sizeof(UTF32)) {
3121
UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
3122
retval = ConvertUTF8toUTF32(
3123
&pUtf8, pUtf8 + a_uInputDataLen,
3124
&pUtf32, pUtf32 + a_uOutputDataSize,
3125
lenientConversion);
3126
}
3127
else if (sizeof(wchar_t) == sizeof(UTF16)) {
3128
UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
3129
retval = ConvertUTF8toUTF16(
3130
&pUtf8, pUtf8 + a_uInputDataLen,
3131
&pUtf16, pUtf16 + a_uOutputDataSize,
3132
lenientConversion);
3133
}
3134
return retval == conversionOK;
3135
}
3136
3137
// convert to wchar_t
3138
size_t retval = mbstowcs(a_pOutputData,
3139
a_pInputData, a_uOutputDataSize);
3140
return retval != (size_t)(-1);
3141
}
3142
3143
/** Calculate the number of char required by the storage format of this
3144
* data. The storage format is always UTF-8 or MBCS.
3145
*
3146
* @param a_pInputData NULL terminated string to calculate the number of
3147
* bytes required to be converted to storage format.
3148
* @return Number of bytes required by the string when
3149
* converted to storage format. This size always
3150
* includes space for the terminating NULL character.
3151
* @return -1 cast to size_t on a conversion error.
3152
*/
3153
size_t SizeToStore(
3154
const SI_CHAR * a_pInputData)
3155
{
3156
if (m_bStoreIsUtf8) {
3157
// worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
3158
size_t uLen = 0;
3159
while (a_pInputData[uLen]) {
3160
++uLen;
3161
}
3162
return (6 * uLen) + 1;
3163
}
3164
else {
3165
size_t uLen = wcstombs(NULL, a_pInputData, 0);
3166
if (uLen == (size_t)(-1)) {
3167
return uLen;
3168
}
3169
return uLen + 1; // include NULL terminator
3170
}
3171
}
3172
3173
/** Convert the input string to the storage format of this data.
3174
* The storage format is always UTF-8 or MBCS.
3175
*
3176
* @param a_pInputData NULL terminated source string to convert. All of
3177
* the data will be converted including the
3178
* terminating NULL character.
3179
* @param a_pOutputData Pointer to the buffer to receive the converted
3180
* string.
3181
* @param a_uOutputDataSize Size of the output buffer in char.
3182
* @return true if all of the input data, including the
3183
* terminating NULL character was successfully
3184
* converted.
3185
*/
3186
bool ConvertToStore(
3187
const SI_CHAR * a_pInputData,
3188
char * a_pOutputData,
3189
size_t a_uOutputDataSize
3190
)
3191
{
3192
if (m_bStoreIsUtf8) {
3193
// calc input string length (SI_CHAR type and size independent)
3194
size_t uInputLen = 0;
3195
while (a_pInputData[uInputLen]) {
3196
++uInputLen;
3197
}
3198
++uInputLen; // include the NULL char
3199
3200
// This uses the Unicode reference implementation to do the
3201
// conversion from wchar_t to UTF-8. The required files are
3202
// ConvertUTF.h and ConvertUTF.c which should be included in
3203
// the distribution but are publicly available from unicode.org
3204
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
3205
ConversionResult retval;
3206
UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
3207
if (sizeof(wchar_t) == sizeof(UTF32)) {
3208
const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
3209
retval = ConvertUTF32toUTF8(
3210
&pUtf32, pUtf32 + uInputLen,
3211
&pUtf8, pUtf8 + a_uOutputDataSize,
3212
lenientConversion);
3213
}
3214
else if (sizeof(wchar_t) == sizeof(UTF16)) {
3215
const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
3216
retval = ConvertUTF16toUTF8(
3217
&pUtf16, pUtf16 + uInputLen,
3218
&pUtf8, pUtf8 + a_uOutputDataSize,
3219
lenientConversion);
3220
}
3221
return retval == conversionOK;
3222
}
3223
else {
3224
size_t retval = wcstombs(a_pOutputData,
3225
a_pInputData, a_uOutputDataSize);
3226
return retval != (size_t) -1;
3227
}
3228
}
3229
};
3230
3231
#endif // SI_CONVERT_GENERIC
3232
3233
3234
// ---------------------------------------------------------------------------
3235
// SI_CONVERT_ICU
3236
// ---------------------------------------------------------------------------
3237
#ifdef SI_CONVERT_ICU
3238
3239
#define SI_Case SI_GenericCase
3240
#define SI_NoCase SI_GenericNoCase
3241
3242
#include <unicode/ucnv.h>
3243
3244
/**
3245
* Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms.
3246
*/
3247
template<class SI_CHAR>
3248
class SI_ConvertW {
3249
const char * m_pEncoding;
3250
UConverter * m_pConverter;
3251
protected:
3252
SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
3253
public:
3254
SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3255
m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3256
}
3257
3258
/* copy and assignment */
3259
SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3260
SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3261
m_pEncoding = rhs.m_pEncoding;
3262
m_pConverter = NULL;
3263
return *this;
3264
}
3265
~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
3266
3267
/** Calculate the number of UChar required for converting the input
3268
* from the storage format. The storage format is always UTF-8 or MBCS.
3269
*
3270
* @param a_pInputData Data in storage format to be converted to UChar.
3271
* @param a_uInputDataLen Length of storage format data in bytes. This
3272
* must be the actual length of the data, including
3273
* NULL byte if NULL terminated string is required.
3274
* @return Number of UChar required by the string when
3275
* converted. If there are embedded NULL bytes in the
3276
* input data, only the string up and not including
3277
* the NULL byte will be converted.
3278
* @return -1 cast to size_t on a conversion error.
3279
*/
3280
size_t SizeFromStore(
3281
const char * a_pInputData,
3282
size_t a_uInputDataLen)
3283
{
3284
SI_ASSERT(a_uInputDataLen != (size_t) -1);
3285
3286
UErrorCode nError;
3287
3288
if (!m_pConverter) {
3289
nError = U_ZERO_ERROR;
3290
m_pConverter = ucnv_open(m_pEncoding, &nError);
3291
if (U_FAILURE(nError)) {
3292
return (size_t) -1;
3293
}
3294
}
3295
3296
nError = U_ZERO_ERROR;
3297
int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3298
a_pInputData, (int32_t) a_uInputDataLen, &nError);
3299
if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3300
return (size_t) -1;
3301
}
3302
3303
return (size_t) nLen;
3304
}
3305
3306
/** Convert the input string from the storage format to UChar.
3307
* The storage format is always UTF-8 or MBCS.
3308
*
3309
* @param a_pInputData Data in storage format to be converted to UChar.
3310
* @param a_uInputDataLen Length of storage format data in bytes. This
3311
* must be the actual length of the data, including
3312
* NULL byte if NULL terminated string is required.
3313
* @param a_pOutputData Pointer to the output buffer to received the
3314
* converted data.
3315
* @param a_uOutputDataSize Size of the output buffer in UChar.
3316
* @return true if all of the input data was successfully
3317
* converted.
3318
*/
3319
bool ConvertFromStore(
3320
const char * a_pInputData,
3321
size_t a_uInputDataLen,
3322
UChar * a_pOutputData,
3323
size_t a_uOutputDataSize)
3324
{
3325
UErrorCode nError;
3326
3327
if (!m_pConverter) {
3328
nError = U_ZERO_ERROR;
3329
m_pConverter = ucnv_open(m_pEncoding, &nError);
3330
if (U_FAILURE(nError)) {
3331
return false;
3332
}
3333
}
3334
3335
nError = U_ZERO_ERROR;
3336
ucnv_toUChars(m_pConverter,
3337
a_pOutputData, (int32_t) a_uOutputDataSize,
3338
a_pInputData, (int32_t) a_uInputDataLen, &nError);
3339
if (U_FAILURE(nError)) {
3340
return false;
3341
}
3342
3343
return true;
3344
}
3345
3346
/** Calculate the number of char required by the storage format of this
3347
* data. The storage format is always UTF-8 or MBCS.
3348
*
3349
* @param a_pInputData NULL terminated string to calculate the number of
3350
* bytes required to be converted to storage format.
3351
* @return Number of bytes required by the string when
3352
* converted to storage format. This size always
3353
* includes space for the terminating NULL character.
3354
* @return -1 cast to size_t on a conversion error.
3355
*/
3356
size_t SizeToStore(
3357
const UChar * a_pInputData)
3358
{
3359
UErrorCode nError;
3360
3361
if (!m_pConverter) {
3362
nError = U_ZERO_ERROR;
3363
m_pConverter = ucnv_open(m_pEncoding, &nError);
3364
if (U_FAILURE(nError)) {
3365
return (size_t) -1;
3366
}
3367
}
3368
3369
nError = U_ZERO_ERROR;
3370
int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3371
a_pInputData, -1, &nError);
3372
if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3373
return (size_t) -1;
3374
}
3375
3376
return (size_t) nLen + 1;
3377
}
3378
3379
/** Convert the input string to the storage format of this data.
3380
* The storage format is always UTF-8 or MBCS.
3381
*
3382
* @param a_pInputData NULL terminated source string to convert. All of
3383
* the data will be converted including the
3384
* terminating NULL character.
3385
* @param a_pOutputData Pointer to the buffer to receive the converted
3386
* string.
3387
* @param a_pOutputDataSize Size of the output buffer in char.
3388
* @return true if all of the input data, including the
3389
* terminating NULL character was successfully
3390
* converted.
3391
*/
3392
bool ConvertToStore(
3393
const UChar * a_pInputData,
3394
char * a_pOutputData,
3395
size_t a_uOutputDataSize)
3396
{
3397
UErrorCode nError;
3398
3399
if (!m_pConverter) {
3400
nError = U_ZERO_ERROR;
3401
m_pConverter = ucnv_open(m_pEncoding, &nError);
3402
if (U_FAILURE(nError)) {
3403
return false;
3404
}
3405
}
3406
3407
nError = U_ZERO_ERROR;
3408
ucnv_fromUChars(m_pConverter,
3409
a_pOutputData, (int32_t) a_uOutputDataSize,
3410
a_pInputData, -1, &nError);
3411
if (U_FAILURE(nError)) {
3412
return false;
3413
}
3414
3415
return true;
3416
}
3417
};
3418
3419
#endif // SI_CONVERT_ICU
3420
3421
3422
// ---------------------------------------------------------------------------
3423
// SI_CONVERT_WIN32
3424
// ---------------------------------------------------------------------------
3425
#ifdef SI_CONVERT_WIN32
3426
3427
#define SI_Case SI_GenericCase
3428
3429
// Windows CE doesn't have errno or MBCS libraries
3430
#ifdef _WIN32_WCE
3431
# ifndef SI_NO_MBCS
3432
# define SI_NO_MBCS
3433
# endif
3434
#endif
3435
3436
#include <windows.h>
3437
#ifdef SI_NO_MBCS
3438
# define SI_NoCase SI_GenericNoCase
3439
#else // !SI_NO_MBCS
3440
/**
3441
* Case-insensitive comparison class using Win32 MBCS functions. This class
3442
* returns a case-insensitive semi-collation order for MBCS text. It may not
3443
* be safe for UTF-8 text returned in char format as we don't know what
3444
* characters will be folded by the function! Therefore, if you are using
3445
* SI_CHAR == char and SetUnicode(true), then you need to use the generic
3446
* SI_NoCase class instead.
3447
*/
3448
#include <mbstring.h>
3449
template<class SI_CHAR>
3450
struct SI_NoCase {
3451
bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
3452
if (sizeof(SI_CHAR) == sizeof(char)) {
3453
return _mbsicmp((const unsigned char *)pLeft,
3454
(const unsigned char *)pRight) < 0;
3455
}
3456
if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3457
return _wcsicmp((const wchar_t *)pLeft,
3458
(const wchar_t *)pRight) < 0;
3459
}
3460
return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
3461
}
3462
};
3463
#endif // SI_NO_MBCS
3464
3465
/**
3466
* Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses
3467
* only the Win32 functions and doesn't require the external Unicode UTF-8
3468
* conversion library. It will not work on Windows 95 without using Microsoft
3469
* Layer for Unicode in your application.
3470
*/
3471
template<class SI_CHAR>
3472
class SI_ConvertW {
3473
UINT m_uCodePage;
3474
protected:
3475
SI_ConvertW() { }
3476
public:
3477
SI_ConvertW(bool a_bStoreIsUtf8) {
3478
m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
3479
}
3480
3481
/* copy and assignment */
3482
SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3483
SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3484
m_uCodePage = rhs.m_uCodePage;
3485
return *this;
3486
}
3487
3488
/** Calculate the number of SI_CHAR required for converting the input
3489
* from the storage format. The storage format is always UTF-8 or MBCS.
3490
*
3491
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
3492
* @param a_uInputDataLen Length of storage format data in bytes. This
3493
* must be the actual length of the data, including
3494
* NULL byte if NULL terminated string is required.
3495
* @return Number of SI_CHAR required by the string when
3496
* converted. If there are embedded NULL bytes in the
3497
* input data, only the string up and not including
3498
* the NULL byte will be converted.
3499
* @return -1 cast to size_t on a conversion error.
3500
*/
3501
size_t SizeFromStore(
3502
const char * a_pInputData,
3503
size_t a_uInputDataLen)
3504
{
3505
SI_ASSERT(a_uInputDataLen != (size_t) -1);
3506
3507
int retval = MultiByteToWideChar(
3508
m_uCodePage, 0,
3509
a_pInputData, (int) a_uInputDataLen,
3510
0, 0);
3511
return (size_t)(retval > 0 ? retval : -1);
3512
}
3513
3514
/** Convert the input string from the storage format to SI_CHAR.
3515
* The storage format is always UTF-8 or MBCS.
3516
*
3517
* @param a_pInputData Data in storage format to be converted to SI_CHAR.
3518
* @param a_uInputDataLen Length of storage format data in bytes. This
3519
* must be the actual length of the data, including
3520
* NULL byte if NULL terminated string is required.
3521
* @param a_pOutputData Pointer to the output buffer to received the
3522
* converted data.
3523
* @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
3524
* @return true if all of the input data was successfully
3525
* converted.
3526
*/
3527
bool ConvertFromStore(
3528
const char * a_pInputData,
3529
size_t a_uInputDataLen,
3530
SI_CHAR * a_pOutputData,
3531
size_t a_uOutputDataSize)
3532
{
3533
int nSize = MultiByteToWideChar(
3534
m_uCodePage, 0,
3535
a_pInputData, (int) a_uInputDataLen,
3536
(wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3537
return (nSize > 0);
3538
}
3539
3540
/** Calculate the number of char required by the storage format of this
3541
* data. The storage format is always UTF-8.
3542
*
3543
* @param a_pInputData NULL terminated string to calculate the number of
3544
* bytes required to be converted to storage format.
3545
* @return Number of bytes required by the string when
3546
* converted to storage format. This size always
3547
* includes space for the terminating NULL character.
3548
* @return -1 cast to size_t on a conversion error.
3549
*/
3550
size_t SizeToStore(
3551
const SI_CHAR * a_pInputData)
3552
{
3553
int retval = WideCharToMultiByte(
3554
m_uCodePage, 0,
3555
(const wchar_t *) a_pInputData, -1,
3556
0, 0, 0, 0);
3557
return (size_t) (retval > 0 ? retval : -1);
3558
}
3559
3560
/** Convert the input string to the storage format of this data.
3561
* The storage format is always UTF-8 or MBCS.
3562
*
3563
* @param a_pInputData NULL terminated source string to convert. All of
3564
* the data will be converted including the
3565
* terminating NULL character.
3566
* @param a_pOutputData Pointer to the buffer to receive the converted
3567
* string.
3568
* @param a_pOutputDataSize Size of the output buffer in char.
3569
* @return true if all of the input data, including the
3570
* terminating NULL character was successfully
3571
* converted.
3572
*/
3573
bool ConvertToStore(
3574
const SI_CHAR * a_pInputData,
3575
char * a_pOutputData,
3576
size_t a_uOutputDataSize)
3577
{
3578
int retval = WideCharToMultiByte(
3579
m_uCodePage, 0,
3580
(const wchar_t *) a_pInputData, -1,
3581
a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3582
return retval > 0;
3583
}
3584
};
3585
3586
#endif // SI_CONVERT_WIN32
3587
3588
3589
3590
// ---------------------------------------------------------------------------
3591
// SI_NO_CONVERSION
3592
// ---------------------------------------------------------------------------
3593
#ifdef SI_NO_CONVERSION
3594
3595
#define SI_Case SI_GenericCase
3596
#define SI_NoCase SI_GenericNoCase
3597
3598
#endif // SI_NO_CONVERSION
3599
3600
3601
3602
// ---------------------------------------------------------------------------
3603
// TYPE DEFINITIONS
3604
// ---------------------------------------------------------------------------
3605
3606
typedef CSimpleIniTempl<char,
3607
SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
3608
typedef CSimpleIniTempl<char,
3609
SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
3610
3611
#if defined(SI_NO_CONVERSION)
3612
// if there is no wide char conversion then we don't need to define the
3613
// widechar "W" versions of CSimpleIni
3614
# define CSimpleIni CSimpleIniA
3615
# define CSimpleIniCase CSimpleIniCaseA
3616
# define SI_NEWLINE SI_NEWLINE_A
3617
#else
3618
# if defined(SI_CONVERT_ICU)
3619
typedef CSimpleIniTempl<UChar,
3620
SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
3621
typedef CSimpleIniTempl<UChar,
3622
SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
3623
# else
3624
typedef CSimpleIniTempl<wchar_t,
3625
SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
3626
typedef CSimpleIniTempl<wchar_t,
3627
SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
3628
# endif
3629
3630
# ifdef _UNICODE
3631
# define CSimpleIni CSimpleIniW
3632
# define CSimpleIniCase CSimpleIniCaseW
3633
# define SI_NEWLINE SI_NEWLINE_W
3634
# else // !_UNICODE
3635
# define CSimpleIni CSimpleIniA
3636
# define CSimpleIniCase CSimpleIniCaseA
3637
# define SI_NEWLINE SI_NEWLINE_A
3638
# endif // _UNICODE
3639
#endif
3640
3641
#ifdef _MSC_VER
3642
# pragma warning (pop)
3643
#endif
3644
3645
#endif // INCLUDED_SimpleIni_h
3646
3647
3648