Kinetic Visualization (Visualisierung 2 - S2012)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
SimpleIni.h
Go to the documentation of this file.
1 
194 #ifndef INCLUDED_SimpleIni_h
195 #define INCLUDED_SimpleIni_h
196 
197 #if defined(_MSC_VER) && (_MSC_VER >= 1020)
198 # pragma once
199 #endif
200 
201 // Disable these warnings in MSVC:
202 // 4127 "conditional expression is constant" as the conversion classes trigger
203 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
204 // be optimized away in a release build.
205 // 4503 'insert' : decorated name length exceeded, name was truncated
206 // 4702 "unreachable code" as the MS STL header causes it in release mode.
207 // Again, the code causing the warning will be cleaned up by the compiler.
208 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
209 // of times VC6 as soon as STL is used.
210 #ifdef _MSC_VER
211 # pragma warning (push)
212 # pragma warning (disable: 4127 4503 4702 4786)
213 #endif
214 
215 #include <cstring>
216 #include <string>
217 #include <map>
218 #include <list>
219 #include <algorithm>
220 #include <stdio.h>
221 
222 #ifdef SI_SUPPORT_IOSTREAMS
223 # include <iostream>
224 #endif // SI_SUPPORT_IOSTREAMS
225 
226 #ifdef _DEBUG
227 # ifndef assert
228 # include <cassert>
229 # endif
230 # define SI_ASSERT(x) assert(x)
231 #else
232 # define SI_ASSERT(x)
233 #endif
234 
235 enum SI_Error {
236  SI_OK = 0,
239 
240  // note: test for any error with (retval < 0)
241  SI_FAIL = -1,
242  SI_NOMEM = -2,
243  SI_FILE = -3
244 };
245 
246 #define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
247 
248 #ifdef _WIN32
249 # define SI_NEWLINE_A "\r\n"
250 # define SI_NEWLINE_W L"\r\n"
251 #else // !_WIN32
252 # define SI_NEWLINE_A "\n"
253 # define SI_NEWLINE_W L"\n"
254 #endif // _WIN32
255 
256 #if defined(SI_CONVERT_ICU)
257 # include <unicode/ustring.h>
258 #endif
259 
260 #if defined(_WIN32)
261 # define SI_HAS_WIDE_FILE
262 # define SI_WCHAR_T wchar_t
263 #elif defined(SI_CONVERT_ICU)
264 # define SI_HAS_WIDE_FILE
265 # define SI_WCHAR_T UChar
266 #endif
267 
269 // ---------------------------------------------------------------------------
270 // MAIN TEMPLATE CLASS
271 // ---------------------------------------------------------------------------
272 
292 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
294 {
295 public:
297  struct Entry {
298  const SI_CHAR * pItem;
299  const SI_CHAR * pComment;
300  int nOrder;
301 
302  Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
303  : pItem(a_pszItem)
304  , pComment(NULL)
305  , nOrder(a_nOrder)
306  { }
307  Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder)
308  : pItem(a_pszItem)
309  , pComment(a_pszComment)
310  , nOrder(a_nOrder)
311  { }
312  Entry(const Entry & rhs) { operator=(rhs); }
313  Entry & operator=(const Entry & rhs) {
314  pItem = rhs.pItem;
315  pComment = rhs.pComment;
316  nOrder = rhs.nOrder;
317  return *this;
318  }
319 
320 #if defined(_MSC_VER) && _MSC_VER <= 1200
321 
322  bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
323  bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
324 #endif
325 
327  struct KeyOrder : std::binary_function<Entry, Entry, bool> {
328  bool operator()(const Entry & lhs, const Entry & rhs) const {
329  const static SI_STRLESS isLess = SI_STRLESS();
330  return isLess(lhs.pItem, rhs.pItem);
331  }
332  };
333 
335  struct LoadOrder : std::binary_function<Entry, Entry, bool> {
336  bool operator()(const Entry & lhs, const Entry & rhs) const {
337  if (lhs.nOrder != rhs.nOrder) {
338  return lhs.nOrder < rhs.nOrder;
339  }
340  return KeyOrder()(lhs.pItem, rhs.pItem);
341  }
342  };
343  };
344 
346  typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
347 
349  typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
350 
354  typedef std::list<Entry> TNamesDepend;
355 
359  class OutputWriter {
360  public:
361  OutputWriter() { }
362  virtual ~OutputWriter() { }
363  virtual void Write(const char * a_pBuf) = 0;
364  private:
365  OutputWriter(const OutputWriter &); // disable
366  OutputWriter & operator=(const OutputWriter &); // disable
367  };
368 
370  class FileWriter : public OutputWriter {
371  FILE * m_file;
372  public:
373  FileWriter(FILE * a_file) : m_file(a_file) { }
374  void Write(const char * a_pBuf) {
375  fputs(a_pBuf, m_file);
376  }
377  private:
378  FileWriter(const FileWriter &); // disable
379  FileWriter & operator=(const FileWriter &); // disable
380  };
381 
383  class StringWriter : public OutputWriter {
384  std::string & m_string;
385  public:
386  StringWriter(std::string & a_string) : m_string(a_string) { }
387  void Write(const char * a_pBuf) {
388  m_string.append(a_pBuf);
389  }
390  private:
391  StringWriter(const StringWriter &); // disable
392  StringWriter & operator=(const StringWriter &); // disable
393  };
394 
395 #ifdef SI_SUPPORT_IOSTREAMS
396 
397  class StreamWriter : public OutputWriter {
398  std::ostream & m_ostream;
399  public:
400  StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
401  void Write(const char * a_pBuf) {
402  m_ostream << a_pBuf;
403  }
404  private:
405  StreamWriter(const StreamWriter &); // disable
406  StreamWriter & operator=(const StreamWriter &); // disable
407  };
408 #endif // SI_SUPPORT_IOSTREAMS
409 
413  class Converter : private SI_CONVERTER {
414  public:
415  Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) {
416  m_scratch.resize(1024);
417  }
418  Converter(const Converter & rhs) { operator=(rhs); }
419  Converter & operator=(const Converter & rhs) {
420  m_scratch = rhs.m_scratch;
421  return *this;
422  }
423  bool ConvertToStore(const SI_CHAR * a_pszString) {
424  size_t uLen = SizeToStore(a_pszString);
425  if (uLen == (size_t)(-1)) {
426  return false;
427  }
428  while (uLen > m_scratch.size()) {
429  m_scratch.resize(m_scratch.size() * 2);
430  }
431  return SI_CONVERTER::ConvertToStore(
432  a_pszString,
433  const_cast<char*>(m_scratch.data()),
434  m_scratch.size());
435  }
436  const char * Data() { return m_scratch.data(); }
437  private:
438  std::string m_scratch;
439  };
440 
441 public:
442  /*-----------------------------------------------------------------------*/
443 
451  bool a_bIsUtf8 = false,
452  bool a_bMultiKey = false,
453  bool a_bMultiLine = false
454  );
455 
458 
460  void Reset();
461 
463  bool IsEmpty() const { return m_data.empty(); }
464 
465  /*-----------------------------------------------------------------------*/
482  void SetUnicode(bool a_bIsUtf8 = true) {
483  if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8;
484  }
485 
487  bool IsUnicode() const { return m_bStoreIsUtf8; }
488 
507  void SetMultiKey(bool a_bAllowMultiKey = true) {
508  m_bAllowMultiKey = a_bAllowMultiKey;
509  }
510 
512  bool IsMultiKey() const { return m_bAllowMultiKey; }
513 
521  void SetMultiLine(bool a_bAllowMultiLine = true) {
522  m_bAllowMultiLine = a_bAllowMultiLine;
523  }
524 
526  bool IsMultiLine() const { return m_bAllowMultiLine; }
527 
534  void SetSpaces(bool a_bSpaces = true) {
535  m_bSpaces = a_bSpaces;
536  }
537 
539  bool UsingSpaces() const { return m_bSpaces; }
540 
541  /*-----------------------------------------------------------------------*/
554  const char * a_pszFile
555  );
556 
557 #ifdef SI_HAS_WIDE_FILE
558 
565  const SI_WCHAR_T * a_pwszFile
566  );
567 #endif // SI_HAS_WIDE_FILE
568 
577  FILE * a_fpFile
578  );
579 
580 #ifdef SI_SUPPORT_IOSTREAMS
581 
588  std::istream & a_istream
589  );
590 #endif // SI_SUPPORT_IOSTREAMS
591 
598  SI_Error LoadData(const std::string & a_strData) {
599  return LoadData(a_strData.c_str(), a_strData.size());
600  }
601 
610  const char * a_pData,
611  size_t a_uDataLen
612  );
613 
614  /*-----------------------------------------------------------------------*/
631  const char * a_pszFile,
632  bool a_bAddSignature = true
633  ) const;
634 
635 #ifdef SI_HAS_WIDE_FILE
636 
647  const SI_WCHAR_T * a_pwszFile,
648  bool a_bAddSignature = true
649  ) const;
650 #endif // _WIN32
651 
665  FILE * a_pFile,
666  bool a_bAddSignature = false
667  ) const;
668 
700  SI_Error Save(
701  OutputWriter & a_oOutput,
702  bool a_bAddSignature = false
703  ) const;
704 
705 #ifdef SI_SUPPORT_IOSTREAMS
706 
717  SI_Error Save(
718  std::ostream & a_ostream,
719  bool a_bAddSignature = false
720  ) const
721  {
722  StreamWriter writer(a_ostream);
723  return Save(writer, a_bAddSignature);
724  }
725 #endif // SI_SUPPORT_IOSTREAMS
726 
738  SI_Error Save(
739  std::string & a_sBuffer,
740  bool a_bAddSignature = false
741  ) const
742  {
743  StringWriter writer(a_sBuffer);
744  return Save(writer, a_bAddSignature);
745  }
746 
747  /*-----------------------------------------------------------------------*/
765  void GetAllSections(
766  TNamesDepend & a_names
767  ) const;
768 
786  bool GetAllKeys(
787  const SI_CHAR * a_pSection,
788  TNamesDepend & a_names
789  ) const;
790 
807  bool GetAllValues(
808  const SI_CHAR * a_pSection,
809  const SI_CHAR * a_pKey,
810  TNamesDepend & a_values
811  ) const;
812 
822  int GetSectionSize(
823  const SI_CHAR * a_pSection
824  ) const;
825 
840  const TKeyVal * GetSection(
841  const SI_CHAR * a_pSection
842  ) const;
843 
861  const SI_CHAR * GetValue(
862  const SI_CHAR * a_pSection,
863  const SI_CHAR * a_pKey,
864  const SI_CHAR * a_pDefault = NULL,
865  bool * a_pHasMultiple = NULL
866  ) const;
867 
881  long GetLongValue(
882  const SI_CHAR * a_pSection,
883  const SI_CHAR * a_pKey,
884  long a_nDefault = 0,
885  bool * a_pHasMultiple = NULL
886  ) const;
887 
901  double GetDoubleValue(
902  const SI_CHAR * a_pSection,
903  const SI_CHAR * a_pKey,
904  double a_nDefault = 0,
905  bool * a_pHasMultiple = NULL
906  ) const;
907 
926  bool GetBoolValue(
927  const SI_CHAR * a_pSection,
928  const SI_CHAR * a_pKey,
929  bool a_bDefault = false,
930  bool * a_pHasMultiple = NULL
931  ) const;
932 
963  const SI_CHAR * a_pSection,
964  const SI_CHAR * a_pKey,
965  const SI_CHAR * a_pValue,
966  const SI_CHAR * a_pComment = NULL,
967  bool a_bForceReplace = false
968  )
969  {
970  return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true);
971  }
972 
997  const SI_CHAR * a_pSection,
998  const SI_CHAR * a_pKey,
999  long a_nValue,
1000  const SI_CHAR * a_pComment = NULL,
1001  bool a_bUseHex = false,
1002  bool a_bForceReplace = false
1003  );
1004 
1026  const SI_CHAR * a_pSection,
1027  const SI_CHAR * a_pKey,
1028  double a_nValue,
1029  const SI_CHAR * a_pComment = NULL,
1030  bool a_bForceReplace = false
1031  );
1032 
1054  const SI_CHAR * a_pSection,
1055  const SI_CHAR * a_pKey,
1056  bool a_bValue,
1057  const SI_CHAR * a_pComment = NULL,
1058  bool a_bForceReplace = false
1059  );
1060 
1079  bool Delete(
1080  const SI_CHAR * a_pSection,
1081  const SI_CHAR * a_pKey,
1082  bool a_bRemoveEmpty = false
1083  );
1084 
1085  /*-----------------------------------------------------------------------*/
1094  Converter GetConverter() const {
1095  return Converter(m_bStoreIsUtf8);
1096  }
1097 
1098  /*-----------------------------------------------------------------------*/
1101 private:
1102  // copying is not permitted
1103  CSimpleIniTempl(const CSimpleIniTempl &); // disabled
1104  CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled
1105 
1109  SI_CHAR *& a_pData,
1110  bool a_bCopyStrings
1111  );
1112 
1117  bool FindEntry(
1118  SI_CHAR *& a_pData,
1119  const SI_CHAR *& a_pSection,
1120  const SI_CHAR *& a_pKey,
1121  const SI_CHAR *& a_pVal,
1122  const SI_CHAR *& a_pComment
1123  ) const;
1124 
1148  const SI_CHAR * a_pSection,
1149  const SI_CHAR * a_pKey,
1150  const SI_CHAR * a_pValue,
1151  const SI_CHAR * a_pComment,
1152  bool a_bForceReplace,
1153  bool a_bCopyStrings
1154  );
1155 
1157  inline bool IsSpace(SI_CHAR ch) const {
1158  return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
1159  }
1160 
1162  inline bool IsComment(SI_CHAR ch) const {
1163  return (ch == ';' || ch == '#');
1164  }
1165 
1166 
1168  inline void SkipNewLine(SI_CHAR *& a_pData) const {
1169  a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
1170  }
1171 
1173  SI_Error CopyString(const SI_CHAR *& a_pString);
1174 
1176  void DeleteString(const SI_CHAR * a_pString);
1177 
1179  bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
1180  const static SI_STRLESS isLess = SI_STRLESS();
1181  return isLess(a_pLeft, a_pRight);
1182  }
1183 
1184  bool IsMultiLineTag(const SI_CHAR * a_pData) const;
1185  bool IsMultiLineData(const SI_CHAR * a_pData) const;
1186  bool LoadMultiLineText(
1187  SI_CHAR *& a_pData,
1188  const SI_CHAR *& a_pVal,
1189  const SI_CHAR * a_pTagName,
1190  bool a_bAllowBlankLinesInComment = false
1191  ) const;
1192  bool IsNewLineChar(SI_CHAR a_c) const;
1193 
1194  bool OutputMultiLineText(
1195  OutputWriter & a_oOutput,
1196  Converter & a_oConverter,
1197  const SI_CHAR * a_pText
1198  ) const;
1199 
1200 private:
1206  SI_CHAR * m_pData;
1207 
1212  size_t m_uDataLen;
1213 
1215  const SI_CHAR * m_pFileComment;
1216 
1218  TSection m_data;
1219 
1225 
1227  bool m_bStoreIsUtf8;
1228 
1230  bool m_bAllowMultiKey;
1231 
1233  bool m_bAllowMultiLine;
1234 
1236  bool m_bSpaces;
1241  int m_nOrder;
1242 };
1243 
1244 // ---------------------------------------------------------------------------
1245 // IMPLEMENTATION
1246 // ---------------------------------------------------------------------------
1247 
1248 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1250  bool a_bIsUtf8,
1251  bool a_bAllowMultiKey,
1252  bool a_bAllowMultiLine
1253  )
1254  : m_pData(0)
1255  , m_uDataLen(0)
1256  , m_pFileComment(NULL)
1257  , m_bStoreIsUtf8(a_bIsUtf8)
1258  , m_bAllowMultiKey(a_bAllowMultiKey)
1259  , m_bAllowMultiLine(a_bAllowMultiLine)
1260  , m_bSpaces(true)
1261  , m_nOrder(0)
1262 { }
1263 
1264 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1266 {
1267  Reset();
1268 }
1269 
1270 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1271 void
1273 {
1274  // remove all data
1275  delete[] m_pData;
1276  m_pData = NULL;
1277  m_uDataLen = 0;
1278  m_pFileComment = NULL;
1279  if (!m_data.empty()) {
1280  m_data.erase(m_data.begin(), m_data.end());
1281  }
1282 
1283  // remove all strings
1284  if (!m_strings.empty()) {
1285  typename TNamesDepend::iterator i = m_strings.begin();
1286  for (; i != m_strings.end(); ++i) {
1287  delete[] const_cast<SI_CHAR*>(i->pItem);
1288  }
1289  m_strings.erase(m_strings.begin(), m_strings.end());
1290  }
1291 }
1292 
1293 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1294 SI_Error
1296  const char * a_pszFile
1297  )
1298 {
1299  FILE * fp = NULL;
1300 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1301  fopen_s(&fp, a_pszFile, "rb");
1302 #else // !__STDC_WANT_SECURE_LIB__
1303  fp = fopen(a_pszFile, "rb");
1304 #endif // __STDC_WANT_SECURE_LIB__
1305  if (!fp) {
1306  return SI_FILE;
1307  }
1308  SI_Error rc = LoadFile(fp);
1309  fclose(fp);
1310  return rc;
1311 }
1312 
1313 #ifdef SI_HAS_WIDE_FILE
1314 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1315 SI_Error
1317  const SI_WCHAR_T * a_pwszFile
1318  )
1319 {
1320 #ifdef _WIN32
1321  FILE * fp = NULL;
1322 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
1323  _wfopen_s(&fp, a_pwszFile, L"rb");
1324 #else // !__STDC_WANT_SECURE_LIB__
1325  fp = _wfopen(a_pwszFile, L"rb");
1326 #endif // __STDC_WANT_SECURE_LIB__
1327  if (!fp) return SI_FILE;
1328  SI_Error rc = LoadFile(fp);
1329  fclose(fp);
1330  return rc;
1331 #else // !_WIN32 (therefore SI_CONVERT_ICU)
1332  char szFile[256];
1333  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1334  return LoadFile(szFile);
1335 #endif // _WIN32
1336 }
1337 #endif // SI_HAS_WIDE_FILE
1338 
1339 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1340 SI_Error
1342  FILE * a_fpFile
1343  )
1344 {
1345  // load the raw file data
1346  int retval = fseek(a_fpFile, 0, SEEK_END);
1347  if (retval != 0) {
1348  return SI_FILE;
1349  }
1350  long lSize = ftell(a_fpFile);
1351  if (lSize < 0) {
1352  return SI_FILE;
1353  }
1354  if (lSize == 0) {
1355  return SI_OK;
1356  }
1357  char * pData = new char[lSize];
1358  if (!pData) {
1359  return SI_NOMEM;
1360  }
1361  fseek(a_fpFile, 0, SEEK_SET);
1362  size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
1363  if (uRead != (size_t) lSize) {
1364  delete[] pData;
1365  return SI_FILE;
1366  }
1367 
1368  // convert the raw data to unicode
1369  SI_Error rc = LoadData(pData, uRead);
1370  delete[] pData;
1371  return rc;
1372 }
1373 
1374 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1375 SI_Error
1377  const char * a_pData,
1378  size_t a_uDataLen
1379  )
1380 {
1381  SI_CONVERTER converter(m_bStoreIsUtf8);
1382 
1383  if (a_uDataLen == 0) {
1384  return SI_OK;
1385  }
1386 
1387  // consume the UTF-8 BOM if it exists
1388  if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
1389  if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
1390  a_pData += 3;
1391  a_uDataLen -= 3;
1392  }
1393  }
1394 
1395  // determine the length of the converted data
1396  size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
1397  if (uLen == (size_t)(-1)) {
1398  return SI_FAIL;
1399  }
1400 
1401  // allocate memory for the data, ensure that there is a NULL
1402  // terminator wherever the converted data ends
1403  SI_CHAR * pData = new SI_CHAR[uLen+1];
1404  if (!pData) {
1405  return SI_NOMEM;
1406  }
1407  memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
1408 
1409  // convert the data
1410  if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
1411  delete[] pData;
1412  return SI_FAIL;
1413  }
1414 
1415  // parse it
1416  const static SI_CHAR empty = 0;
1417  SI_CHAR * pWork = pData;
1418  const SI_CHAR * pSection = &empty;
1419  const SI_CHAR * pItem = NULL;
1420  const SI_CHAR * pVal = NULL;
1421  const SI_CHAR * pComment = NULL;
1422 
1423  // We copy the strings if we are loading data into this class when we
1424  // already have stored some.
1425  bool bCopyStrings = (m_pData != NULL);
1426 
1427  // find a file comment if it exists, this is a comment that starts at the
1428  // beginning of the file and continues until the first blank line.
1429  SI_Error rc = FindFileComment(pWork, bCopyStrings);
1430  if (rc < 0) return rc;
1431 
1432  // add every entry in the file to the data table
1433  while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
1434  rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings);
1435  if (rc < 0) return rc;
1436  }
1437 
1438  // store these strings if we didn't copy them
1439  if (bCopyStrings) {
1440  delete[] pData;
1441  }
1442  else {
1443  m_pData = pData;
1444  m_uDataLen = uLen+1;
1445  }
1446 
1447  return SI_OK;
1448 }
1449 
1450 #ifdef SI_SUPPORT_IOSTREAMS
1451 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1452 SI_Error
1454  std::istream & a_istream
1455  )
1456 {
1457  std::string strData;
1458  char szBuf[512];
1459  do {
1460  a_istream.get(szBuf, sizeof(szBuf), '\0');
1461  strData.append(szBuf);
1462  }
1463  while (a_istream.good());
1464  return LoadData(strData);
1465 }
1466 #endif // SI_SUPPORT_IOSTREAMS
1467 
1468 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1469 SI_Error
1471  SI_CHAR *& a_pData,
1472  bool a_bCopyStrings
1473  )
1474 {
1475  // there can only be a single file comment
1476  if (m_pFileComment) {
1477  return SI_OK;
1478  }
1479 
1480  // Load the file comment as multi-line text, this will modify all of
1481  // the newline characters to be single \n chars
1482  if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1483  return SI_OK;
1484  }
1485 
1486  // copy the string if necessary
1487  if (a_bCopyStrings) {
1488  SI_Error rc = CopyString(m_pFileComment);
1489  if (rc < 0) return rc;
1490  }
1491 
1492  return SI_OK;
1493 }
1494 
1495 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1496 bool
1498  SI_CHAR *& a_pData,
1499  const SI_CHAR *& a_pSection,
1500  const SI_CHAR *& a_pKey,
1501  const SI_CHAR *& a_pVal,
1502  const SI_CHAR *& a_pComment
1503  ) const
1504 {
1505  a_pComment = NULL;
1506 
1507  SI_CHAR * pTrail = NULL;
1508  while (*a_pData) {
1509  // skip spaces and empty lines
1510  while (*a_pData && IsSpace(*a_pData)) {
1511  ++a_pData;
1512  }
1513  if (!*a_pData) {
1514  break;
1515  }
1516 
1517  // skip processing of comment lines but keep a pointer to
1518  // the start of the comment.
1519  if (IsComment(*a_pData)) {
1520  LoadMultiLineText(a_pData, a_pComment, NULL, true);
1521  continue;
1522  }
1523 
1524  // process section names
1525  if (*a_pData == '[') {
1526  // skip leading spaces
1527  ++a_pData;
1528  while (*a_pData && IsSpace(*a_pData)) {
1529  ++a_pData;
1530  }
1531 
1532  // find the end of the section name (it may contain spaces)
1533  // and convert it to lowercase as necessary
1534  a_pSection = a_pData;
1535  while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1536  ++a_pData;
1537  }
1538 
1539  // if it's an invalid line, just skip it
1540  if (*a_pData != ']') {
1541  continue;
1542  }
1543 
1544  // remove trailing spaces from the section
1545  pTrail = a_pData - 1;
1546  while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1547  --pTrail;
1548  }
1549  ++pTrail;
1550  *pTrail = 0;
1551 
1552  // skip to the end of the line
1553  ++a_pData; // safe as checked that it == ']' above
1554  while (*a_pData && !IsNewLineChar(*a_pData)) {
1555  ++a_pData;
1556  }
1557 
1558  a_pKey = NULL;
1559  a_pVal = NULL;
1560  return true;
1561  }
1562 
1563  // find the end of the key name (it may contain spaces)
1564  // and convert it to lowercase as necessary
1565  a_pKey = a_pData;
1566  while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1567  ++a_pData;
1568  }
1569 
1570  // if it's an invalid line, just skip it
1571  if (*a_pData != '=') {
1572  continue;
1573  }
1574 
1575  // empty keys are invalid
1576  if (a_pKey == a_pData) {
1577  while (*a_pData && !IsNewLineChar(*a_pData)) {
1578  ++a_pData;
1579  }
1580  continue;
1581  }
1582 
1583  // remove trailing spaces from the key
1584  pTrail = a_pData - 1;
1585  while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1586  --pTrail;
1587  }
1588  ++pTrail;
1589  *pTrail = 0;
1590 
1591  // skip leading whitespace on the value
1592  ++a_pData; // safe as checked that it == '=' above
1593  while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1594  ++a_pData;
1595  }
1596 
1597  // find the end of the value which is the end of this line
1598  a_pVal = a_pData;
1599  while (*a_pData && !IsNewLineChar(*a_pData)) {
1600  ++a_pData;
1601  }
1602 
1603  // remove trailing spaces from the value
1604  pTrail = a_pData - 1;
1605  if (*a_pData) { // prepare for the next round
1606  SkipNewLine(a_pData);
1607  }
1608  while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1609  --pTrail;
1610  }
1611  ++pTrail;
1612  *pTrail = 0;
1613 
1614  // check for multi-line entries
1615  if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1616  // skip the "<<<" to get the tag that will end the multiline
1617  const SI_CHAR * pTagName = a_pVal + 3;
1618  return LoadMultiLineText(a_pData, a_pVal, pTagName);
1619  }
1620 
1621  // return the standard entry
1622  return true;
1623  }
1624 
1625  return false;
1626 }
1627 
1628 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1629 bool
1631  const SI_CHAR * a_pVal
1632  ) const
1633 {
1634  // check for the "<<<" prefix for a multi-line entry
1635  if (*a_pVal++ != '<') return false;
1636  if (*a_pVal++ != '<') return false;
1637  if (*a_pVal++ != '<') return false;
1638  return true;
1639 }
1640 
1641 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1642 bool
1644  const SI_CHAR * a_pData
1645  ) const
1646 {
1647  // data is multi-line if it has any of the following features:
1648  // * whitespace prefix
1649  // * embedded newlines
1650  // * whitespace suffix
1651 
1652  // empty string
1653  if (!*a_pData) {
1654  return false;
1655  }
1656 
1657  // check for prefix
1658  if (IsSpace(*a_pData)) {
1659  return true;
1660  }
1661 
1662  // embedded newlines
1663  while (*a_pData) {
1664  if (IsNewLineChar(*a_pData)) {
1665  return true;
1666  }
1667  ++a_pData;
1668  }
1669 
1670  // check for suffix
1671  if (IsSpace(*--a_pData)) {
1672  return true;
1673  }
1674 
1675  return false;
1676 }
1677 
1678 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1679 bool
1681  SI_CHAR a_c
1682  ) const
1683 {
1684  return (a_c == '\n' || a_c == '\r');
1685 }
1686 
1687 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1688 bool
1690  SI_CHAR *& a_pData,
1691  const SI_CHAR *& a_pVal,
1692  const SI_CHAR * a_pTagName,
1693  bool a_bAllowBlankLinesInComment
1694  ) const
1695 {
1696  // we modify this data to strip all newlines down to a single '\n'
1697  // character. This means that on Windows we need to strip out some
1698  // characters which will make the data shorter.
1699  // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1700  // LINE1-LINE1\nLINE2-LINE2\0
1701  // The pDataLine entry is the pointer to the location in memory that
1702  // the current line needs to start to run following the existing one.
1703  // This may be the same as pCurrLine in which case no move is needed.
1704  SI_CHAR * pDataLine = a_pData;
1705  SI_CHAR * pCurrLine;
1706 
1707  // value starts at the current line
1708  a_pVal = a_pData;
1709 
1710  // find the end tag. This tag must start in column 1 and be
1711  // followed by a newline. No whitespace removal is done while
1712  // searching for this tag.
1713  SI_CHAR cEndOfLineChar = *a_pData;
1714  for(;;) {
1715  // if we are loading comments then we need a comment character as
1716  // the first character on every line
1717  if (!a_pTagName && !IsComment(*a_pData)) {
1718  // if we aren't allowing blank lines then we're done
1719  if (!a_bAllowBlankLinesInComment) {
1720  break;
1721  }
1722 
1723  // if we are allowing blank lines then we only include them
1724  // in this comment if another comment follows, so read ahead
1725  // to find out.
1726  SI_CHAR * pCurr = a_pData;
1727  int nNewLines = 0;
1728  while (IsSpace(*pCurr)) {
1729  if (IsNewLineChar(*pCurr)) {
1730  ++nNewLines;
1731  SkipNewLine(pCurr);
1732  }
1733  else {
1734  ++pCurr;
1735  }
1736  }
1737 
1738  // we have a comment, add the blank lines to the output
1739  // and continue processing from here
1740  if (IsComment(*pCurr)) {
1741  for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1742  a_pData = pCurr;
1743  continue;
1744  }
1745 
1746  // the comment ends here
1747  break;
1748  }
1749 
1750  // find the end of this line
1751  pCurrLine = a_pData;
1752  while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1753 
1754  // move this line down to the location that it should be if necessary
1755  if (pDataLine < pCurrLine) {
1756  size_t nLen = (size_t) (a_pData - pCurrLine);
1757  memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR));
1758  pDataLine[nLen] = '\0';
1759  }
1760 
1761  // end the line with a NULL
1762  cEndOfLineChar = *a_pData;
1763  *a_pData = 0;
1764 
1765  // if are looking for a tag then do the check now. This is done before
1766  // checking for end of the data, so that if we have the tag at the end
1767  // of the data then the tag is removed correctly.
1768  if (a_pTagName &&
1769  (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1770  {
1771  break;
1772  }
1773 
1774  // if we are at the end of the data then we just automatically end
1775  // this entry and return the current data.
1776  if (!cEndOfLineChar) {
1777  return true;
1778  }
1779 
1780  // otherwise we need to process this newline to ensure that it consists
1781  // of just a single \n character.
1782  pDataLine += (a_pData - pCurrLine);
1783  *a_pData = cEndOfLineChar;
1784  SkipNewLine(a_pData);
1785  *pDataLine++ = '\n';
1786  }
1787 
1788  // if we didn't find a comment at all then return false
1789  if (a_pVal == a_pData) {
1790  a_pVal = NULL;
1791  return false;
1792  }
1793 
1794  // the data (which ends at the end of the last line) needs to be
1795  // null-terminated BEFORE before the newline character(s). If the
1796  // user wants a new line in the multi-line data then they need to
1797  // add an empty line before the tag.
1798  *--pDataLine = '\0';
1799 
1800  // if looking for a tag and if we aren't at the end of the data,
1801  // then move a_pData to the start of the next line.
1802  if (a_pTagName && cEndOfLineChar) {
1803  SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1804  *a_pData = cEndOfLineChar;
1805  SkipNewLine(a_pData);
1806  }
1807 
1808  return true;
1809 }
1810 
1811 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1812 SI_Error
1814  const SI_CHAR *& a_pString
1815  )
1816 {
1817  size_t uLen = 0;
1818  if (sizeof(SI_CHAR) == sizeof(char)) {
1819  uLen = strlen((const char *)a_pString);
1820  }
1821  else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1822  uLen = wcslen((const wchar_t *)a_pString);
1823  }
1824  else {
1825  for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1826  }
1827  ++uLen; // NULL character
1828  SI_CHAR * pCopy = new SI_CHAR[uLen];
1829  if (!pCopy) {
1830  return SI_NOMEM;
1831  }
1832  memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1833  m_strings.push_back(pCopy);
1834  a_pString = pCopy;
1835  return SI_OK;
1836 }
1837 
1838 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1839 SI_Error
1841  const SI_CHAR * a_pSection,
1842  const SI_CHAR * a_pKey,
1843  const SI_CHAR * a_pValue,
1844  const SI_CHAR * a_pComment,
1845  bool a_bForceReplace,
1846  bool a_bCopyStrings
1847  )
1848 {
1849  SI_Error rc;
1850  bool bInserted = false;
1851 
1852  SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1853 
1854  // if we are copying strings then make a copy of the comment now
1855  // because we will need it when we add the entry.
1856  if (a_bCopyStrings && a_pComment) {
1857  rc = CopyString(a_pComment);
1858  if (rc < 0) return rc;
1859  }
1860 
1861  // create the section entry if necessary
1862  typename TSection::iterator iSection = m_data.find(a_pSection);
1863  if (iSection == m_data.end()) {
1864  // if the section doesn't exist then we need a copy as the
1865  // string needs to last beyond the end of this function
1866  if (a_bCopyStrings) {
1867  rc = CopyString(a_pSection);
1868  if (rc < 0) return rc;
1869  }
1870 
1871  // only set the comment if this is a section only entry
1872  Entry oSection(a_pSection, ++m_nOrder);
1873  if (a_pComment && (!a_pKey || !a_pValue)) {
1874  oSection.pComment = a_pComment;
1875  }
1876 
1877  typename TSection::value_type oEntry(oSection, TKeyVal());
1878  typedef typename TSection::iterator SectionIterator;
1879  std::pair<SectionIterator,bool> i = m_data.insert(oEntry);
1880  iSection = i.first;
1881  bInserted = true;
1882  }
1883  if (!a_pKey || !a_pValue) {
1884  // section only entries are specified with pItem and pVal as NULL
1885  return bInserted ? SI_INSERTED : SI_UPDATED;
1886  }
1887 
1888  // check for existence of the key
1889  TKeyVal & keyval = iSection->second;
1890  typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1891 
1892  // remove all existing entries but save the load order and
1893  // comment of the first entry
1894  int nLoadOrder = ++m_nOrder;
1895  if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) {
1896  const SI_CHAR * pComment = NULL;
1897  while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) {
1898  if (iKey->first.nOrder < nLoadOrder) {
1899  nLoadOrder = iKey->first.nOrder;
1900  pComment = iKey->first.pComment;
1901  }
1902  ++iKey;
1903  }
1904  if (pComment) {
1905  DeleteString(a_pComment);
1906  a_pComment = pComment;
1907  CopyString(a_pComment);
1908  }
1909  Delete(a_pSection, a_pKey);
1910  iKey = keyval.end();
1911  }
1912 
1913  // make string copies if necessary
1914  bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
1915  if (a_bCopyStrings) {
1916  if (bForceCreateNewKey || iKey == keyval.end()) {
1917  // if the key doesn't exist then we need a copy as the
1918  // string needs to last beyond the end of this function
1919  // because we will be inserting the key next
1920  rc = CopyString(a_pKey);
1921  if (rc < 0) return rc;
1922  }
1923 
1924  // we always need a copy of the value
1925  rc = CopyString(a_pValue);
1926  if (rc < 0) return rc;
1927  }
1928 
1929  // create the key entry
1930  if (iKey == keyval.end() || bForceCreateNewKey) {
1931  Entry oKey(a_pKey, nLoadOrder);
1932  if (a_pComment) {
1933  oKey.pComment = a_pComment;
1934  }
1935  typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
1936  iKey = keyval.insert(oEntry);
1937  bInserted = true;
1938  }
1939  iKey->second = a_pValue;
1940  return bInserted ? SI_INSERTED : SI_UPDATED;
1941 }
1942 
1943 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1944 const SI_CHAR *
1946  const SI_CHAR * a_pSection,
1947  const SI_CHAR * a_pKey,
1948  const SI_CHAR * a_pDefault,
1949  bool * a_pHasMultiple
1950  ) const
1951 {
1952  if (a_pHasMultiple) {
1953  *a_pHasMultiple = false;
1954  }
1955  if (!a_pSection || !a_pKey) {
1956  return a_pDefault;
1957  }
1958  typename TSection::const_iterator iSection = m_data.find(a_pSection);
1959  if (iSection == m_data.end()) {
1960  return a_pDefault;
1961  }
1962  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1963  if (iKeyVal == iSection->second.end()) {
1964  return a_pDefault;
1965  }
1966 
1967  // check for multiple entries with the same key
1968  if (m_bAllowMultiKey && a_pHasMultiple) {
1969  typename TKeyVal::const_iterator iTemp = iKeyVal;
1970  if (++iTemp != iSection->second.end()) {
1971  if (!IsLess(a_pKey, iTemp->first.pItem)) {
1972  *a_pHasMultiple = true;
1973  }
1974  }
1975  }
1976 
1977  return iKeyVal->second;
1978 }
1979 
1980 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1981 long
1983  const SI_CHAR * a_pSection,
1984  const SI_CHAR * a_pKey,
1985  long a_nDefault,
1986  bool * a_pHasMultiple
1987  ) const
1988 {
1989  // return the default if we don't have a value
1990  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
1991  if (!pszValue || !*pszValue) return a_nDefault;
1992 
1993  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
1994  char szValue[64] = { 0 };
1995  SI_CONVERTER c(m_bStoreIsUtf8);
1996  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
1997  return a_nDefault;
1998  }
1999 
2000  // handle the value as hex if prefaced with "0x"
2001  long nValue = a_nDefault;
2002  char * pszSuffix = szValue;
2003  if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
2004  if (!szValue[2]) return a_nDefault;
2005  nValue = strtol(&szValue[2], &pszSuffix, 16);
2006  }
2007  else {
2008  nValue = strtol(szValue, &pszSuffix, 10);
2009  }
2010 
2011  // any invalid strings will return the default value
2012  if (*pszSuffix) {
2013  return a_nDefault;
2014  }
2015 
2016  return nValue;
2017 }
2018 
2019 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2020 SI_Error
2022  const SI_CHAR * a_pSection,
2023  const SI_CHAR * a_pKey,
2024  long a_nValue,
2025  const SI_CHAR * a_pComment,
2026  bool a_bUseHex,
2027  bool a_bForceReplace
2028  )
2029 {
2030  // use SetValue to create sections
2031  if (!a_pSection || !a_pKey) return SI_FAIL;
2032 
2033  // convert to an ASCII string
2034  char szInput[64];
2035 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2036  sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2037 #else // !__STDC_WANT_SECURE_LIB__
2038  sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
2039 #endif // __STDC_WANT_SECURE_LIB__
2040 
2041  // convert to output text
2042  SI_CHAR szOutput[64];
2043  SI_CONVERTER c(m_bStoreIsUtf8);
2044  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2045  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2046 
2047  // actually add it
2048  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2049 }
2050 
2051 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2052 double
2054  const SI_CHAR * a_pSection,
2055  const SI_CHAR * a_pKey,
2056  double a_nDefault,
2057  bool * a_pHasMultiple
2058  ) const
2059 {
2060  // return the default if we don't have a value
2061  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2062  if (!pszValue || !*pszValue) return a_nDefault;
2063 
2064  // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII
2065  char szValue[64] = { 0 };
2066  SI_CONVERTER c(m_bStoreIsUtf8);
2067  if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) {
2068  return a_nDefault;
2069  }
2070 
2071  char * pszSuffix = NULL;
2072  double nValue = strtod(szValue, &pszSuffix);
2073 
2074  // any invalid strings will return the default value
2075  if (!pszSuffix || *pszSuffix) {
2076  return a_nDefault;
2077  }
2078 
2079  return nValue;
2080 }
2081 
2082 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2083 SI_Error
2085  const SI_CHAR * a_pSection,
2086  const SI_CHAR * a_pKey,
2087  double a_nValue,
2088  const SI_CHAR * a_pComment,
2089  bool a_bForceReplace
2090  )
2091 {
2092  // use SetValue to create sections
2093  if (!a_pSection || !a_pKey) return SI_FAIL;
2094 
2095  // convert to an ASCII string
2096  char szInput[64];
2097 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2098  sprintf_s(szInput, "%f", a_nValue);
2099 #else // !__STDC_WANT_SECURE_LIB__
2100  sprintf(szInput, "%f", a_nValue);
2101 #endif // __STDC_WANT_SECURE_LIB__
2102 
2103  // convert to output text
2104  SI_CHAR szOutput[64];
2105  SI_CONVERTER c(m_bStoreIsUtf8);
2106  c.ConvertFromStore(szInput, strlen(szInput) + 1,
2107  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2108 
2109  // actually add it
2110  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2111 }
2112 
2113 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2114 bool
2116  const SI_CHAR * a_pSection,
2117  const SI_CHAR * a_pKey,
2118  bool a_bDefault,
2119  bool * a_pHasMultiple
2120  ) const
2121 {
2122  // return the default if we don't have a value
2123  const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple);
2124  if (!pszValue || !*pszValue) return a_bDefault;
2125 
2126  // we only look at the minimum number of characters
2127  switch (pszValue[0]) {
2128  case 't': case 'T': // true
2129  case 'y': case 'Y': // yes
2130  case '1': // 1 (one)
2131  return true;
2132 
2133  case 'f': case 'F': // false
2134  case 'n': case 'N': // no
2135  case '0': // 0 (zero)
2136  return false;
2137 
2138  case 'o': case 'O':
2139  if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on
2140  if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off
2141  break;
2142  }
2143 
2144  // no recognized value, return the default
2145  return a_bDefault;
2146 }
2147 
2148 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2149 SI_Error
2151  const SI_CHAR * a_pSection,
2152  const SI_CHAR * a_pKey,
2153  bool a_bValue,
2154  const SI_CHAR * a_pComment,
2155  bool a_bForceReplace
2156  )
2157 {
2158  // use SetValue to create sections
2159  if (!a_pSection || !a_pKey) return SI_FAIL;
2160 
2161  // convert to an ASCII string
2162  const char * pszInput = a_bValue ? "true" : "false";
2163 
2164  // convert to output text
2165  SI_CHAR szOutput[64];
2166  SI_CONVERTER c(m_bStoreIsUtf8);
2167  c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
2168  szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
2169 
2170  // actually add it
2171  return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
2172 }
2173 
2174 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2175 bool
2177  const SI_CHAR * a_pSection,
2178  const SI_CHAR * a_pKey,
2179  TNamesDepend & a_values
2180  ) const
2181 {
2182  a_values.clear();
2183 
2184  if (!a_pSection || !a_pKey) {
2185  return false;
2186  }
2187  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2188  if (iSection == m_data.end()) {
2189  return false;
2190  }
2191  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
2192  if (iKeyVal == iSection->second.end()) {
2193  return false;
2194  }
2195 
2196  // insert all values for this key
2197  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2198  if (m_bAllowMultiKey) {
2199  ++iKeyVal;
2200  while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
2201  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder));
2202  ++iKeyVal;
2203  }
2204  }
2205 
2206  return true;
2207 }
2208 
2209 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2210 int
2212  const SI_CHAR * a_pSection
2213  ) const
2214 {
2215  if (!a_pSection) {
2216  return -1;
2217  }
2218 
2219  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2220  if (iSection == m_data.end()) {
2221  return -1;
2222  }
2223  const TKeyVal & section = iSection->second;
2224 
2225  // if multi-key isn't permitted then the section size is
2226  // the number of keys that we have.
2227  if (!m_bAllowMultiKey || section.empty()) {
2228  return (int) section.size();
2229  }
2230 
2231  // otherwise we need to count them
2232  int nCount = 0;
2233  const SI_CHAR * pLastKey = NULL;
2234  typename TKeyVal::const_iterator iKeyVal = section.begin();
2235  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
2236  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2237  ++nCount;
2238  pLastKey = iKeyVal->first.pItem;
2239  }
2240  }
2241  return nCount;
2242 }
2243 
2244 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2247  const SI_CHAR * a_pSection
2248  ) const
2249 {
2250  if (a_pSection) {
2251  typename TSection::const_iterator i = m_data.find(a_pSection);
2252  if (i != m_data.end()) {
2253  return &(i->second);
2254  }
2255  }
2256  return 0;
2257 }
2258 
2259 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2260 void
2262  TNamesDepend & a_names
2263  ) const
2264 {
2265  a_names.clear();
2266  typename TSection::const_iterator i = m_data.begin();
2267  for (int n = 0; i != m_data.end(); ++i, ++n ) {
2268  a_names.push_back(i->first);
2269  }
2270 }
2271 
2272 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2273 bool
2275  const SI_CHAR * a_pSection,
2276  TNamesDepend & a_names
2277  ) const
2278 {
2279  a_names.clear();
2280 
2281  if (!a_pSection) {
2282  return false;
2283  }
2284 
2285  typename TSection::const_iterator iSection = m_data.find(a_pSection);
2286  if (iSection == m_data.end()) {
2287  return false;
2288  }
2289 
2290  const TKeyVal & section = iSection->second;
2291  const SI_CHAR * pLastKey = NULL;
2292  typename TKeyVal::const_iterator iKeyVal = section.begin();
2293  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
2294  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
2295  a_names.push_back(iKeyVal->first);
2296  pLastKey = iKeyVal->first.pItem;
2297  }
2298  }
2299 
2300  return true;
2301 }
2302 
2303 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2304 SI_Error
2306  const char * a_pszFile,
2307  bool a_bAddSignature
2308  ) const
2309 {
2310  FILE * fp = NULL;
2311 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2312  fopen_s(&fp, a_pszFile, "wb");
2313 #else // !__STDC_WANT_SECURE_LIB__
2314  fp = fopen(a_pszFile, "wb");
2315 #endif // __STDC_WANT_SECURE_LIB__
2316  if (!fp) return SI_FILE;
2317  SI_Error rc = SaveFile(fp, a_bAddSignature);
2318  fclose(fp);
2319  return rc;
2320 }
2321 
2322 #ifdef SI_HAS_WIDE_FILE
2323 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2324 SI_Error
2326  const SI_WCHAR_T * a_pwszFile,
2327  bool a_bAddSignature
2328  ) const
2329 {
2330 #ifdef _WIN32
2331  FILE * fp = NULL;
2332 #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
2333  _wfopen_s(&fp, a_pwszFile, L"wb");
2334 #else // !__STDC_WANT_SECURE_LIB__
2335  fp = _wfopen(a_pwszFile, L"wb");
2336 #endif // __STDC_WANT_SECURE_LIB__
2337  if (!fp) return SI_FILE;
2338  SI_Error rc = SaveFile(fp, a_bAddSignature);
2339  fclose(fp);
2340  return rc;
2341 #else // !_WIN32 (therefore SI_CONVERT_ICU)
2342  char szFile[256];
2343  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
2344  return SaveFile(szFile, a_bAddSignature);
2345 #endif // _WIN32
2346 }
2347 #endif // SI_HAS_WIDE_FILE
2348 
2349 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2350 SI_Error
2352  FILE * a_pFile,
2353  bool a_bAddSignature
2354  ) const
2355 {
2356  FileWriter writer(a_pFile);
2357  return Save(writer, a_bAddSignature);
2358 }
2359 
2360 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2361 SI_Error
2363  OutputWriter & a_oOutput,
2364  bool a_bAddSignature
2365  ) const
2366 {
2367  Converter convert(m_bStoreIsUtf8);
2368 
2369  // add the UTF-8 signature if it is desired
2370  if (m_bStoreIsUtf8 && a_bAddSignature) {
2371  a_oOutput.Write(SI_UTF8_SIGNATURE);
2372  }
2373 
2374  // get all of the sections sorted in load order
2375  TNamesDepend oSections;
2376  GetAllSections(oSections);
2377 #if defined(_MSC_VER) && _MSC_VER <= 1200
2378  oSections.sort();
2379 #elif defined(__BORLANDC__)
2380  oSections.sort(Entry::LoadOrder());
2381 #else
2382  oSections.sort(typename Entry::LoadOrder());
2383 #endif
2384 
2385  // write the file comment if we have one
2386  bool bNeedNewLine = false;
2387  if (m_pFileComment) {
2388  if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
2389  return SI_FAIL;
2390  }
2391  bNeedNewLine = true;
2392  }
2393 
2394  // iterate through our sections and output the data
2395  typename TNamesDepend::const_iterator iSection = oSections.begin();
2396  for ( ; iSection != oSections.end(); ++iSection ) {
2397  // write out the comment if there is one
2398  if (iSection->pComment) {
2399  if (bNeedNewLine) {
2400  a_oOutput.Write(SI_NEWLINE_A);
2401  a_oOutput.Write(SI_NEWLINE_A);
2402  }
2403  if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) {
2404  return SI_FAIL;
2405  }
2406  bNeedNewLine = false;
2407  }
2408 
2409  if (bNeedNewLine) {
2410  a_oOutput.Write(SI_NEWLINE_A);
2411  a_oOutput.Write(SI_NEWLINE_A);
2412  bNeedNewLine = false;
2413  }
2414 
2415  // write the section (unless there is no section name)
2416  if (*iSection->pItem) {
2417  if (!convert.ConvertToStore(iSection->pItem)) {
2418  return SI_FAIL;
2419  }
2420  a_oOutput.Write("[");
2421  a_oOutput.Write(convert.Data());
2422  a_oOutput.Write("]");
2423  a_oOutput.Write(SI_NEWLINE_A);
2424  }
2425 
2426  // get all of the keys sorted in load order
2427  TNamesDepend oKeys;
2428  GetAllKeys(iSection->pItem, oKeys);
2429 #if defined(_MSC_VER) && _MSC_VER <= 1200
2430  oKeys.sort();
2431 #elif defined(__BORLANDC__)
2432  oKeys.sort(Entry::LoadOrder());
2433 #else
2434  oKeys.sort(typename Entry::LoadOrder());
2435 #endif
2436 
2437  // write all keys and values
2438  typename TNamesDepend::const_iterator iKey = oKeys.begin();
2439  for ( ; iKey != oKeys.end(); ++iKey) {
2440  // get all values for this key
2441  TNamesDepend oValues;
2442  GetAllValues(iSection->pItem, iKey->pItem, oValues);
2443 
2444  typename TNamesDepend::const_iterator iValue = oValues.begin();
2445  for ( ; iValue != oValues.end(); ++iValue) {
2446  // write out the comment if there is one
2447  if (iValue->pComment) {
2448  a_oOutput.Write(SI_NEWLINE_A);
2449  if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) {
2450  return SI_FAIL;
2451  }
2452  }
2453 
2454  // write the key
2455  if (!convert.ConvertToStore(iKey->pItem)) {
2456  return SI_FAIL;
2457  }
2458  a_oOutput.Write(convert.Data());
2459 
2460  // write the value
2461  if (!convert.ConvertToStore(iValue->pItem)) {
2462  return SI_FAIL;
2463  }
2464  a_oOutput.Write(m_bSpaces ? " = " : "=");
2465  if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
2466  // multi-line data needs to be processed specially to ensure
2467  // that we use the correct newline format for the current system
2468  a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
2469  if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
2470  return SI_FAIL;
2471  }
2472  a_oOutput.Write("END_OF_TEXT");
2473  }
2474  else {
2475  a_oOutput.Write(convert.Data());
2476  }
2477  a_oOutput.Write(SI_NEWLINE_A);
2478  }
2479  }
2480 
2481  bNeedNewLine = true;
2482  }
2483 
2484  return SI_OK;
2485 }
2486 
2487 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2488 bool
2490  OutputWriter & a_oOutput,
2491  Converter & a_oConverter,
2492  const SI_CHAR * a_pText
2493  ) const
2494 {
2495  const SI_CHAR * pEndOfLine;
2496  SI_CHAR cEndOfLineChar = *a_pText;
2497  while (cEndOfLineChar) {
2498  // find the end of this line
2499  pEndOfLine = a_pText;
2500  for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
2501  cEndOfLineChar = *pEndOfLine;
2502 
2503  // temporarily null terminate, convert and output the line
2504  *const_cast<SI_CHAR*>(pEndOfLine) = 0;
2505  if (!a_oConverter.ConvertToStore(a_pText)) {
2506  return false;
2507  }
2508  *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
2509  a_pText += (pEndOfLine - a_pText) + 1;
2510  a_oOutput.Write(a_oConverter.Data());
2511  a_oOutput.Write(SI_NEWLINE_A);
2512  }
2513  return true;
2514 }
2515 
2516 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2517 bool
2519  const SI_CHAR * a_pSection,
2520  const SI_CHAR * a_pKey,
2521  bool a_bRemoveEmpty
2522  )
2523 {
2524  if (!a_pSection) {
2525  return false;
2526  }
2527 
2528  typename TSection::iterator iSection = m_data.find(a_pSection);
2529  if (iSection == m_data.end()) {
2530  return false;
2531  }
2532 
2533  // remove a single key if we have a keyname
2534  if (a_pKey) {
2535  typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
2536  if (iKeyVal == iSection->second.end()) {
2537  return false;
2538  }
2539 
2540  // remove any copied strings and then the key
2541  typename TKeyVal::iterator iDelete;
2542  do {
2543  iDelete = iKeyVal++;
2544 
2545  DeleteString(iDelete->first.pItem);
2546  DeleteString(iDelete->second);
2547  iSection->second.erase(iDelete);
2548  }
2549  while (iKeyVal != iSection->second.end()
2550  && !IsLess(a_pKey, iKeyVal->first.pItem));
2551 
2552  // done now if the section is not empty or we are not pruning away
2553  // the empty sections. Otherwise let it fall through into the section
2554  // deletion code
2555  if (!a_bRemoveEmpty || !iSection->second.empty()) {
2556  return true;
2557  }
2558  }
2559  else {
2560  // delete all copied strings from this section. The actual
2561  // entries will be removed when the section is removed.
2562  typename TKeyVal::iterator iKeyVal = iSection->second.begin();
2563  for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
2564  DeleteString(iKeyVal->first.pItem);
2565  DeleteString(iKeyVal->second);
2566  }
2567  }
2568 
2569  // delete the section itself
2570  DeleteString(iSection->first.pItem);
2571  m_data.erase(iSection);
2572 
2573  return true;
2574 }
2575 
2576 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
2577 void
2579  const SI_CHAR * a_pString
2580  )
2581 {
2582  // strings may exist either inside the data block, or they will be
2583  // individually allocated and stored in m_strings. We only physically
2584  // delete those stored in m_strings.
2585  if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
2586  typename TNamesDepend::iterator i = m_strings.begin();
2587  for (;i != m_strings.end(); ++i) {
2588  if (a_pString == i->pItem) {
2589  delete[] const_cast<SI_CHAR*>(i->pItem);
2590  m_strings.erase(i);
2591  break;
2592  }
2593  }
2594  }
2595 }
2596 
2597 // ---------------------------------------------------------------------------
2598 // CONVERSION FUNCTIONS
2599 // ---------------------------------------------------------------------------
2600 
2601 // Defines the conversion classes for different libraries. Before including
2602 // SimpleIni.h, set the converter that you wish you use by defining one of the
2603 // following symbols.
2604 //
2605 // SI_CONVERT_GENERIC Use the Unicode reference conversion library in
2606 // the accompanying files ConvertUTF.h/c
2607 // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
2608 // ICU headers on include path and icuuc.lib
2609 // SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
2610 
2611 #if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
2612 # ifdef _WIN32
2613 # define SI_CONVERT_WIN32
2614 # else
2615 # define SI_CONVERT_GENERIC
2616 # endif
2617 #endif
2618 
2624 template<class SI_CHAR>
2625 struct SI_GenericCase {
2626  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2627  long cmp;
2628  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2629  cmp = (long) *pLeft - (long) *pRight;
2630  if (cmp != 0) {
2631  return cmp < 0;
2632  }
2633  }
2634  return *pRight != 0;
2635  }
2636 };
2637 
2644 template<class SI_CHAR>
2645 struct SI_GenericNoCase {
2646  inline SI_CHAR locase(SI_CHAR ch) const {
2647  return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
2648  }
2649  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
2650  long cmp;
2651  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
2652  cmp = (long) locase(*pLeft) - (long) locase(*pRight);
2653  if (cmp != 0) {
2654  return cmp < 0;
2655  }
2656  }
2657  return *pRight != 0;
2658  }
2659 };
2660 
2664 template<class SI_CHAR>
2665 class SI_ConvertA {
2666  bool m_bStoreIsUtf8;
2667 protected:
2668  SI_ConvertA() { }
2669 public:
2670  SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2671 
2672  /* copy and assignment */
2673  SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
2674  SI_ConvertA & operator=(const SI_ConvertA & rhs) {
2675  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2676  return *this;
2677  }
2678 
2692  size_t SizeFromStore(
2693  const char * a_pInputData,
2694  size_t a_uInputDataLen)
2695  {
2696  (void)a_pInputData;
2697  SI_ASSERT(a_uInputDataLen != (size_t) -1);
2698 
2699  // ASCII/MBCS/UTF-8 needs no conversion
2700  return a_uInputDataLen;
2701  }
2702 
2716  bool ConvertFromStore(
2717  const char * a_pInputData,
2718  size_t a_uInputDataLen,
2719  SI_CHAR * a_pOutputData,
2720  size_t a_uOutputDataSize)
2721  {
2722  // ASCII/MBCS/UTF-8 needs no conversion
2723  if (a_uInputDataLen > a_uOutputDataSize) {
2724  return false;
2725  }
2726  memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2727  return true;
2728  }
2729 
2740  size_t SizeToStore(
2741  const SI_CHAR * a_pInputData)
2742  {
2743  // ASCII/MBCS/UTF-8 needs no conversion
2744  return strlen((const char *)a_pInputData) + 1;
2745  }
2746 
2760  bool ConvertToStore(
2761  const SI_CHAR * a_pInputData,
2762  char * a_pOutputData,
2763  size_t a_uOutputDataSize)
2764  {
2765  // calc input string length (SI_CHAR type and size independent)
2766  size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2767  if (uInputLen > a_uOutputDataSize) {
2768  return false;
2769  }
2770 
2771  // ascii/UTF-8 needs no conversion
2772  memcpy(a_pOutputData, a_pInputData, uInputLen);
2773  return true;
2774  }
2775 };
2776 
2777 
2778 // ---------------------------------------------------------------------------
2779 // SI_CONVERT_GENERIC
2780 // ---------------------------------------------------------------------------
2781 #ifdef SI_CONVERT_GENERIC
2782 
2783 #define SI_Case SI_GenericCase
2784 #define SI_NoCase SI_GenericNoCase
2785 
2786 #include <wchar.h>
2787 #include "ConvertUTF.h"
2788 
2793 template<class SI_CHAR>
2794 class SI_ConvertW {
2795  bool m_bStoreIsUtf8;
2796 protected:
2797  SI_ConvertW() { }
2798 public:
2799  SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { }
2800 
2801  /* copy and assignment */
2802  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
2803  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
2804  m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8;
2805  return *this;
2806  }
2807 
2821  size_t SizeFromStore(
2822  const char * a_pInputData,
2823  size_t a_uInputDataLen)
2824  {
2825  SI_ASSERT(a_uInputDataLen != (size_t) -1);
2826 
2827  if (m_bStoreIsUtf8) {
2828  // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t
2829  // so we just return the same number of characters required as for
2830  // the source text.
2831  return a_uInputDataLen;
2832  }
2833  else {
2834  return mbstowcs(NULL, a_pInputData, a_uInputDataLen);
2835  }
2836  }
2837 
2851  bool ConvertFromStore(
2852  const char * a_pInputData,
2853  size_t a_uInputDataLen,
2854  SI_CHAR * a_pOutputData,
2855  size_t a_uOutputDataSize)
2856  {
2857  if (m_bStoreIsUtf8) {
2858  // This uses the Unicode reference implementation to do the
2859  // conversion from UTF-8 to wchar_t. The required files are
2860  // ConvertUTF.h and ConvertUTF.c which should be included in
2861  // the distribution but are publically available from unicode.org
2862  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2863  ConversionResult retval;
2864  const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
2865  if (sizeof(wchar_t) == sizeof(UTF32)) {
2866  UTF32 * pUtf32 = (UTF32 *) a_pOutputData;
2867  retval = ConvertUTF8toUTF32(
2868  &pUtf8, pUtf8 + a_uInputDataLen,
2869  &pUtf32, pUtf32 + a_uOutputDataSize,
2870  lenientConversion);
2871  }
2872  else if (sizeof(wchar_t) == sizeof(UTF16)) {
2873  UTF16 * pUtf16 = (UTF16 *) a_pOutputData;
2874  retval = ConvertUTF8toUTF16(
2875  &pUtf8, pUtf8 + a_uInputDataLen,
2876  &pUtf16, pUtf16 + a_uOutputDataSize,
2877  lenientConversion);
2878  }
2879  return retval == conversionOK;
2880  }
2881  else {
2882  size_t retval = mbstowcs(a_pOutputData,
2883  a_pInputData, a_uOutputDataSize);
2884  return retval != (size_t)(-1);
2885  }
2886  }
2887 
2898  size_t SizeToStore(
2899  const SI_CHAR * a_pInputData)
2900  {
2901  if (m_bStoreIsUtf8) {
2902  // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char
2903  size_t uLen = 0;
2904  while (a_pInputData[uLen]) {
2905  ++uLen;
2906  }
2907  return (6 * uLen) + 1;
2908  }
2909  else {
2910  size_t uLen = wcstombs(NULL, a_pInputData, 0);
2911  if (uLen == (size_t)(-1)) {
2912  return uLen;
2913  }
2914  return uLen + 1; // include NULL terminator
2915  }
2916  }
2917 
2931  bool ConvertToStore(
2932  const SI_CHAR * a_pInputData,
2933  char * a_pOutputData,
2934  size_t a_uOutputDataSize
2935  )
2936  {
2937  if (m_bStoreIsUtf8) {
2938  // calc input string length (SI_CHAR type and size independent)
2939  size_t uInputLen = 0;
2940  while (a_pInputData[uInputLen]) {
2941  ++uInputLen;
2942  }
2943  ++uInputLen; // include the NULL char
2944 
2945  // This uses the Unicode reference implementation to do the
2946  // conversion from wchar_t to UTF-8. The required files are
2947  // ConvertUTF.h and ConvertUTF.c which should be included in
2948  // the distribution but are publically available from unicode.org
2949  // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
2950  ConversionResult retval;
2951  UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
2952  if (sizeof(wchar_t) == sizeof(UTF32)) {
2953  const UTF32 * pUtf32 = (const UTF32 *) a_pInputData;
2954  retval = ConvertUTF32toUTF8(
2955  &pUtf32, pUtf32 + uInputLen,
2956  &pUtf8, pUtf8 + a_uOutputDataSize,
2957  lenientConversion);
2958  }
2959  else if (sizeof(wchar_t) == sizeof(UTF16)) {
2960  const UTF16 * pUtf16 = (const UTF16 *) a_pInputData;
2961  retval = ConvertUTF16toUTF8(
2962  &pUtf16, pUtf16 + uInputLen,
2963  &pUtf8, pUtf8 + a_uOutputDataSize,
2964  lenientConversion);
2965  }
2966  return retval == conversionOK;
2967  }
2968  else {
2969  size_t retval = wcstombs(a_pOutputData,
2970  a_pInputData, a_uOutputDataSize);
2971  return retval != (size_t) -1;
2972  }
2973  }
2974 };
2975 
2976 #endif // SI_CONVERT_GENERIC
2977 
2978 
2979 // ---------------------------------------------------------------------------
2980 // SI_CONVERT_ICU
2981 // ---------------------------------------------------------------------------
2982 #ifdef SI_CONVERT_ICU
2983 
2984 #define SI_Case SI_GenericCase
2985 #define SI_NoCase SI_GenericNoCase
2986 
2987 #include <unicode/ucnv.h>
2988 
2992 template<class SI_CHAR>
2993 class SI_ConvertW {
2994  const char * m_pEncoding;
2995  UConverter * m_pConverter;
2996 protected:
2997  SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { }
2998 public:
2999  SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) {
3000  m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL;
3001  }
3002 
3003  /* copy and assignment */
3004  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3005  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3006  m_pEncoding = rhs.m_pEncoding;
3007  m_pConverter = NULL;
3008  return *this;
3009  }
3010  ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); }
3011 
3025  size_t SizeFromStore(
3026  const char * a_pInputData,
3027  size_t a_uInputDataLen)
3028  {
3029  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3030 
3031  UErrorCode nError;
3032 
3033  if (!m_pConverter) {
3034  nError = U_ZERO_ERROR;
3035  m_pConverter = ucnv_open(m_pEncoding, &nError);
3036  if (U_FAILURE(nError)) {
3037  return (size_t) -1;
3038  }
3039  }
3040 
3041  nError = U_ZERO_ERROR;
3042  int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0,
3043  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3044  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3045  return (size_t) -1;
3046  }
3047 
3048  return (size_t) nLen;
3049  }
3050 
3064  bool ConvertFromStore(
3065  const char * a_pInputData,
3066  size_t a_uInputDataLen,
3067  UChar * a_pOutputData,
3068  size_t a_uOutputDataSize)
3069  {
3070  UErrorCode nError;
3071 
3072  if (!m_pConverter) {
3073  nError = U_ZERO_ERROR;
3074  m_pConverter = ucnv_open(m_pEncoding, &nError);
3075  if (U_FAILURE(nError)) {
3076  return false;
3077  }
3078  }
3079 
3080  nError = U_ZERO_ERROR;
3081  ucnv_toUChars(m_pConverter,
3082  a_pOutputData, (int32_t) a_uOutputDataSize,
3083  a_pInputData, (int32_t) a_uInputDataLen, &nError);
3084  if (U_FAILURE(nError)) {
3085  return false;
3086  }
3087 
3088  return true;
3089  }
3090 
3101  size_t SizeToStore(
3102  const UChar * a_pInputData)
3103  {
3104  UErrorCode nError;
3105 
3106  if (!m_pConverter) {
3107  nError = U_ZERO_ERROR;
3108  m_pConverter = ucnv_open(m_pEncoding, &nError);
3109  if (U_FAILURE(nError)) {
3110  return (size_t) -1;
3111  }
3112  }
3113 
3114  nError = U_ZERO_ERROR;
3115  int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0,
3116  a_pInputData, -1, &nError);
3117  if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) {
3118  return (size_t) -1;
3119  }
3120 
3121  return (size_t) nLen + 1;
3122  }
3123 
3137  bool ConvertToStore(
3138  const UChar * a_pInputData,
3139  char * a_pOutputData,
3140  size_t a_uOutputDataSize)
3141  {
3142  UErrorCode nError;
3143 
3144  if (!m_pConverter) {
3145  nError = U_ZERO_ERROR;
3146  m_pConverter = ucnv_open(m_pEncoding, &nError);
3147  if (U_FAILURE(nError)) {
3148  return false;
3149  }
3150  }
3151 
3152  nError = U_ZERO_ERROR;
3153  ucnv_fromUChars(m_pConverter,
3154  a_pOutputData, (int32_t) a_uOutputDataSize,
3155  a_pInputData, -1, &nError);
3156  if (U_FAILURE(nError)) {
3157  return false;
3158  }
3159 
3160  return true;
3161  }
3162 };
3163 
3164 #endif // SI_CONVERT_ICU
3165 
3166 
3167 // ---------------------------------------------------------------------------
3168 // SI_CONVERT_WIN32
3169 // ---------------------------------------------------------------------------
3170 #ifdef SI_CONVERT_WIN32
3171 
3172 #define SI_Case SI_GenericCase
3173 
3174 // Windows CE doesn't have errno or MBCS libraries
3175 #ifdef _WIN32_WCE
3176 # ifndef SI_NO_MBCS
3177 # define SI_NO_MBCS
3178 # endif
3179 #endif
3180 
3181 #include <windows.h>
3182 #ifdef SI_NO_MBCS
3183 # define SI_NoCase SI_GenericNoCase
3184 #else // !SI_NO_MBCS
3185 
3193 #include <mbstring.h>
3194 template<class SI_CHAR>
3195 struct SI_NoCase {
3196  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
3197  if (sizeof(SI_CHAR) == sizeof(char)) {
3198  return _mbsicmp((const unsigned char *)pLeft,
3199  (const unsigned char *)pRight) < 0;
3200  }
3201  if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
3202  return _wcsicmp((const wchar_t *)pLeft,
3203  (const wchar_t *)pRight) < 0;
3204  }
3205  return SI_GenericNoCase<SI_CHAR>()(pLeft, pRight);
3206  }
3207 };
3208 #endif // SI_NO_MBCS
3209 
3216 template<class SI_CHAR>
3217 class SI_ConvertW {
3218  UINT m_uCodePage;
3219 protected:
3220  SI_ConvertW() { }
3221 public:
3222  SI_ConvertW(bool a_bStoreIsUtf8) {
3223  m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP;
3224  }
3225 
3226  /* copy and assignment */
3227  SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); }
3228  SI_ConvertW & operator=(const SI_ConvertW & rhs) {
3229  m_uCodePage = rhs.m_uCodePage;
3230  return *this;
3231  }
3232 
3246  size_t SizeFromStore(
3247  const char * a_pInputData,
3248  size_t a_uInputDataLen)
3249  {
3250  SI_ASSERT(a_uInputDataLen != (size_t) -1);
3251 
3252  int retval = MultiByteToWideChar(
3253  m_uCodePage, 0,
3254  a_pInputData, (int) a_uInputDataLen,
3255  0, 0);
3256  return (size_t)(retval > 0 ? retval : -1);
3257  }
3258 
3272  bool ConvertFromStore(
3273  const char * a_pInputData,
3274  size_t a_uInputDataLen,
3275  SI_CHAR * a_pOutputData,
3276  size_t a_uOutputDataSize)
3277  {
3278  int nSize = MultiByteToWideChar(
3279  m_uCodePage, 0,
3280  a_pInputData, (int) a_uInputDataLen,
3281  (wchar_t *) a_pOutputData, (int) a_uOutputDataSize);
3282  return (nSize > 0);
3283  }
3284 
3295  size_t SizeToStore(
3296  const SI_CHAR * a_pInputData)
3297  {
3298  int retval = WideCharToMultiByte(
3299  m_uCodePage, 0,
3300  (const wchar_t *) a_pInputData, -1,
3301  0, 0, 0, 0);
3302  return (size_t) (retval > 0 ? retval : -1);
3303  }
3304 
3318  bool ConvertToStore(
3319  const SI_CHAR * a_pInputData,
3320  char * a_pOutputData,
3321  size_t a_uOutputDataSize)
3322  {
3323  int retval = WideCharToMultiByte(
3324  m_uCodePage, 0,
3325  (const wchar_t *) a_pInputData, -1,
3326  a_pOutputData, (int) a_uOutputDataSize, 0, 0);
3327  return retval > 0;
3328  }
3329 };
3330 
3331 #endif // SI_CONVERT_WIN32
3332 
3333 
3334 // ---------------------------------------------------------------------------
3335 // TYPE DEFINITIONS
3336 // ---------------------------------------------------------------------------
3337 
3338 typedef CSimpleIniTempl<char,
3339  SI_NoCase<char>,SI_ConvertA<char> > CSimpleIniA;
3340 typedef CSimpleIniTempl<char,
3341  SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
3342 
3343 #if defined(SI_CONVERT_ICU)
3344 typedef CSimpleIniTempl<UChar,
3345  SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
3346 typedef CSimpleIniTempl<UChar,
3347  SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
3348 #else
3349 typedef CSimpleIniTempl<wchar_t,
3350  SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
3351 typedef CSimpleIniTempl<wchar_t,
3352  SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
3353 #endif
3354 
3355 #ifdef _UNICODE
3356 # define CSimpleIni CSimpleIniW
3357 # define CSimpleIniCase CSimpleIniCaseW
3358 # define SI_NEWLINE SI_NEWLINE_W
3359 #else // !_UNICODE
3360 # define CSimpleIni CSimpleIniA
3361 # define CSimpleIniCase CSimpleIniCaseA
3362 # define SI_NEWLINE SI_NEWLINE_A
3363 #endif // _UNICODE
3364 
3365 #ifdef _MSC_VER
3366 # pragma warning (pop)
3367 #endif
3368 
3369 #endif // INCLUDED_SimpleIni_h
3370