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