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