Main MRPT website > C++ reference for MRPT 1.5.7
SimpleIni.h
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #ifndef INCLUDED_SimpleIni_h
11 #define INCLUDED_SimpleIni_h
12 
13 
14 // Disable these warnings in MSVC:
15 // 4127 "conditional expression is constant" as the conversion classes trigger
16 // it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
17 // be optimized away in a release build.
18 // 4503 'insert' : decorated name length exceeded, name was truncated
19 // 4702 "unreachable code" as the MS STL header causes it in release mode.
20 // Again, the code causing the warning will be cleaned up by the compiler.
21 // 4786 "identifier truncated to 256 characters" as this is thrown hundreds
22 // of times VC6 as soon as STL is used.
23 #ifdef _MSC_VER
24 # pragma warning (disable: 4127 4503 4702 4786)
25 #endif
26 
27 #include <string>
28 #include <map>
29 #include <list>
30 #include <algorithm>
31 #include <stdio.h>
32 #include <cstring>
33 #include <mrpt/utils/utils_defs.h>
36 
37 #define SI_SUPPORT_IOSTREAMS
38 
39 #ifdef SI_SUPPORT_IOSTREAMS
40 # include <iostream>
41 #endif // SI_SUPPORT_IOSTREAMS
42 
43 #ifdef _DEBUG
44 # include <assert.h>
45 # define SI_ASSERT(x) assert(x)
46 #else
47 # define SI_ASSERT(x)
48 #endif
49 
50 
51 namespace mrpt
52 {
53  namespace utils
54  {
55  namespace simpleini
56  {
57 
58 enum SI_Error {
59  SI_OK = 0, //!< No error
60  SI_UPDATED = 1, //!< An existing value was updated
61  SI_INSERTED = 2, //!< A new value was inserted
62 
63  // note: test for any error with (retval < 0)
64  SI_FAIL = -1, //!< Generic failure
65  SI_NOMEM = -2, //!< Out of memory error
66  SI_FILE = -3 //!< File error (see errno for detail error)
67 };
68 
69 #ifdef _WIN32
70 # define SI_NEWLINE_A "\r\n"
71 # define SI_NEWLINE_W L"\r\n"
72 #else // !_WIN32
73 # define SI_NEWLINE_A "\n"
74 # define SI_NEWLINE_W L"\n"
75 #endif // _WIN32
76 
77 #if defined(_WIN32)
78 # define SI_HAS_WIDE_FILE
79 # define SI_WCHAR_T wchar_t
80 #elif defined(SI_CONVERT_ICU)
81 # define SI_HAS_WIDE_FILE
82 # define SI_WCHAR_T UChar
83 #endif
84 
85 
86 // ---------------------------------------------------------------------------
87 // MAIN TEMPLATE CLASS
88 // ---------------------------------------------------------------------------
89 
90 /** Simple INI file reader.
91 
92  This can be instantiated with the choice of unicode or native characterset,
93  and case sensitive or insensitive comparisons of section and key names.
94  The supported combinations are pre-defined with the following typedefs:
95 
96  <table>
97  <tr><th>Interface <th>Case-sensitive <th>Typedef
98  <tr><td>char <td>No <td>CSimpleIniA
99  <tr><td>char <td>Yes <td>CSimpleIniCaseA
100  <tr><td>wchar_t <td>No <td>CSimpleIniW
101  <tr><td>wchar_t <td>Yes <td>CSimpleIniCaseW
102  </table>
103 
104  Note that using other types for the SI_CHAR is supported. For instance,
105  unsigned char, unsigned short, etc. Note that where the alternative type
106  is a different size to char/wchar_t you may need to supply new helper
107  classes for SI_STRLESS and SI_CONVERTER.
108  */
109 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
111 {
112 public:
113  /** key entry */
114  struct Entry {
115  const SI_CHAR * pItem;
116  const SI_CHAR * pComment;
117  int nOrder;
118 
119  Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0)
120  : pItem(a_pszItem)
121  , pComment(NULL)
122  , nOrder(a_nOrder)
123  { }
124  Entry(const Entry & rhs) { operator=(rhs); }
125  Entry & operator=(const Entry & rhs) {
126  pItem = rhs.pItem;
127  pComment = rhs.pComment;
128  nOrder = rhs.nOrder;
129  return *this;
130  }
131 
132 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__BORLANDC__)
133  /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */
134  bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); }
135  bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); }
136 #endif
137 
138  /** Strict less ordering by name of key only */
139  struct KeyOrder : std::binary_function<Entry, Entry, bool> {
140  bool operator()(const Entry & lhs, const Entry & rhs) const {
141  const static SI_STRLESS isLess = SI_STRLESS();
142  return isLess(lhs.pItem, rhs.pItem);
143  }
144  };
145 
146  /** Strict less ordering by order, and then name of key */
147  struct LoadOrder : std::binary_function<Entry, Entry, bool> {
148  bool operator()(const Entry & lhs, const Entry & rhs) const {
149  if (lhs.nOrder != rhs.nOrder) {
150  return lhs.nOrder < rhs.nOrder;
151  }
152  return KeyOrder()(lhs.pItem, rhs.pItem);
153  }
154  };
155  };
156 
157  /** map keys to values */
158  typedef std::multimap<Entry,const SI_CHAR *,typename Entry::KeyOrder> TKeyVal;
159 
160  /** map sections to key/value map */
161  typedef std::map<Entry,TKeyVal,typename Entry::KeyOrder> TSection;
162 
163  /** set of dependent string pointers. Note that these pointers are
164  dependent on memory owned by CSimpleIni.
165  */
166  typedef std::list<Entry> TNamesDepend;
167 
168  /** interface definition for the OutputWriter object to pass to Save()
169  in order to output the INI file data.
170  */
171  class OutputWriter {
172  public:
174  virtual ~OutputWriter() { }
175  virtual void Write(const char * a_pBuf) = 0;
176  private:
177  OutputWriter(const OutputWriter &); // disable
178  OutputWriter & operator=(const OutputWriter &); // disable
179  };
180 
181  /** OutputWriter class to write the INI data to a file */
182  class FileWriter : public OutputWriter {
183  FILE * m_file;
184  public:
185  FileWriter(FILE * a_file) : m_file(a_file) { }
186  void Write(const char * a_pBuf) MRPT_OVERRIDE {
187  fputs(a_pBuf, m_file);
188  }
189  private:
190  FileWriter(const FileWriter &); // disable
191  FileWriter & operator=(const FileWriter &); // disable
192  };
193 
194  /** OutputWriter class to write the INI data to a string */
195  class StringWriter : public OutputWriter {
197  public:
198  StringWriter(std::string & a_string) : m_string(a_string) { }
199  void Write(const char * a_pBuf) MRPT_OVERRIDE {
200  m_string.append(a_pBuf);
201  }
202  private:
203  StringWriter(const StringWriter &); // disable
204  StringWriter & operator=(const StringWriter &); // disable
205  };
206 
207 #ifdef SI_SUPPORT_IOSTREAMS
208  /** OutputWriter class to write the INI data to an ostream */
209  class StreamWriter : public OutputWriter {
210  std::ostream & m_ostream;
211  public:
212  StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { }
213  void Write(const char * a_pBuf) {
214  m_ostream << a_pBuf;
215  }
216  private:
217  StreamWriter(const StreamWriter &); // disable
218  StreamWriter & operator=(const StreamWriter &); // disable
219  };
220 #endif // SI_SUPPORT_IOSTREAMS
221 
222  /** Characterset conversion utility class to convert strings to the
223  same format as is used for the storage.
224  */
225  class Converter : private SI_CONVERTER {
226  public:
227  Converter() : SI_CONVERTER() {
228  m_scratch.resize(1024);
229  }
230  Converter(const Converter & rhs) { operator=(rhs); }
231  Converter & operator=(const Converter & rhs) {
232  m_scratch = rhs.m_scratch;
233  return *this;
234  }
235  bool ConvertToStore(const SI_CHAR * a_pszString) {
236  size_t uLen = this->SizeToStore(a_pszString);
237  if (uLen == (size_t)(-1)) {
238  return false;
239  }
240  while (uLen > m_scratch.size()) {
241  m_scratch.resize(m_scratch.size() * 2);
242  }
243  return SI_CONVERTER::ConvertToStore(
244  a_pszString,
245  const_cast<char*>(m_scratch.data()),
246  m_scratch.size());
247  }
248  const char * Data() { return m_scratch.data(); }
249  private:
251  };
252 
253 public:
254  /*-----------------------------------------------------------------------*/
255 
256  /** Default constructor.
257 
258  @param a_bMultiKey See the method SetMultiKey() for details.
259  @param a_bMultiLine See the method SetMultiLine() for details.
260  */
262  bool a_bMultiKey = false,
263  bool a_bMultiLine = false
264  );
265 
266  /** Destructor */
268 
269  /** Deallocate all memory stored by this object */
270  void Reset();
271 
272  /*-----------------------------------------------------------------------*/
273  /** @{ @name Settings */
274 
275  /** Should multiple identical keys be permitted in the file. If set to false
276  then the last value encountered will be used as the value of the key.
277  If set to true, then all values will be available to be queried. For
278  example, with the following input:
279 
280  <pre>
281  [section]
282  test=value1
283  test=value2
284  </pre>
285 
286  Then with SetMultiKey(true), both of the values "value1" and "value2"
287  will be returned for the key test. If SetMultiKey(false) is used, then
288  the value for "test" will only be "value2". This value may be changed
289  at any time.
290 
291  \param a_bAllowMultiKey Allow multi-keys in the source?
292  */
293  void SetMultiKey(bool a_bAllowMultiKey = true) {
294  m_bAllowMultiKey = a_bAllowMultiKey;
295  }
296 
297  /** Get the storage format of the INI data. */
298  bool IsMultiKey() const { return m_bAllowMultiKey; }
299 
300  /** Should data values be permitted to span multiple lines in the file. If
301  set to false then the multi-line construct <<<TAG as a value will be
302  returned as is instead of loading the data. This value may be changed
303  at any time.
304 
305  \param a_bAllowMultiLine Allow multi-line values in the source?
306  */
307  void SetMultiLine(bool a_bAllowMultiLine = true) {
308  m_bAllowMultiLine = a_bAllowMultiLine;
309  }
310 
311  /** Query the status of multi-line data */
312  bool IsMultiLine() const { return m_bAllowMultiLine; }
313 
314  /*-----------------------------------------------------------------------*/
315  /** @}
316  @{ @name Loading INI Data */
317 
318  /** Load an INI file from disk into memory
319 
320  @param a_pszFile Path of the file to be loaded. This will be passed
321  to fopen() and so must be a valid path for the
322  current platform.
323 
324  @return SI_Error See error definitions
325  */
327  const char * a_pszFile
328  );
329 
330 #ifdef SI_HAS_WIDE_FILE
331  /** Load an INI file from disk into memory
332 
333  @param a_pwszFile Path of the file to be loaded in UTF-16.
334 
335  @return SI_Error See error definitions
336  */
338  const SI_WCHAR_T * a_pwszFile
339  );
340 #endif // SI_HAS_WIDE_FILE
341 
342  /** Load the file from a file pointer.
343 
344  @param a_fpFile Valid file pointer to read the file data from. The
345  file will be read until end of file.
346 
347  @return SI_Error See error definitions
348  */
350  FILE * a_fpFile
351  );
352 
353 #ifdef SI_SUPPORT_IOSTREAMS
354  /** Load INI file data from an istream.
355 
356  @param a_istream Stream to read from
357 
358  @return SI_Error See error definitions
359  */
360  SI_Error Load(
361  std::istream & a_istream
362  );
363 #endif // SI_SUPPORT_IOSTREAMS
364 
365  /** Load INI file data direct from a std::string
366 
367  @param a_strData Data to be loaded
368 
369  @return SI_Error See error definitions
370  */
371  SI_Error Load(const std::string & a_strData) {
372  return Load(a_strData.c_str(), a_strData.size());
373  }
374 
375  /** Load INI file data direct from memory
376 
377  @param a_pData Data to be loaded
378  @param a_uDataLen Length of the data in bytes
379 
380  @return SI_Error See error definitions
381  */
382  SI_Error Load(
383  const char * a_pData,
384  size_t a_uDataLen
385  );
386 
387  /*-----------------------------------------------------------------------*/
388  /** @}
389  @{ @name Saving INI Data */
390 
391  /** Save an INI file from memory to disk
392 
393  @param a_pszFile Path of the file to be saved. This will be passed
394  to fopen() and so must be a valid path for the
395  current platform.
396 
397  @return SI_Error See error definitions
398  */
400  const char * a_pszFile
401  ) const;
402 
403 #ifdef SI_HAS_WIDE_FILE
404  /** Save an INI file from memory to disk
405 
406  @param a_pwszFile Path of the file to be saved in UTF-16.
407 
408  @return SI_Error See error definitions
409  */
411  const SI_WCHAR_T * a_pwszFile
412  ) const;
413 #endif // _WIN32
414 
415  /** Save the INI data to a file. See Save() for details.
416 
417  @param a_pFile Handle to a file. File should be opened for
418  binary output.
419 
420  @return SI_Error See error definitions
421  */
423  FILE * a_pFile
424  ) const;
425 
426  /** Save the INI data. The data will be written to the output device
427  in a format appropriate to the current data, selected by:
428 
429  <table>
430  <tr><th>SI_CHAR <th>FORMAT
431  <tr><td>char <td>same format as when loaded (MBCS or UTF-8)
432  <tr><td>wchar_t <td>UTF-8
433  <tr><td>other <td>UTF-8
434  </table>
435 
436  Note that comments, etc from the original data are not preserved. Only
437  valid data contents stored in the file are written out. The order of
438  the sections and values from the original file will be preserved.
439 
440  Any data prepended or appended to the output device must use the the
441  same format (MBCS or UTF-8). You may use the GetConverter() method to
442  convert text to the correct format regardless of the output format
443  being used by SimpleIni.
444 
445  To add a BOM to UTF-8 data, write it out manually at the very beginning
446  like is done in SaveFile when a_bUseBOM is true.
447 
448  @param a_oOutput Output writer to write the data to.
449 
450  @return SI_Error See error definitions
451  */
452  SI_Error Save(
453  OutputWriter & a_oOutput
454  ) const;
455 
456 #ifdef SI_SUPPORT_IOSTREAMS
457  /** Save the INI data to an ostream. See Save() for details.
458 
459  @param a_ostream String to have the INI data appended to.
460 
461  @return SI_Error See error definitions
462  */
463  SI_Error Save(
464  std::ostream & a_ostream
465  ) const
466  {
467  StreamWriter writer(a_ostream);
468  return Save(writer);
469  }
470 #endif // SI_SUPPORT_IOSTREAMS
471 
472  /** Append the INI data to a string. See Save() for details.
473 
474  @param a_sBuffer String to have the INI data appended to.
475 
476  @return SI_Error See error definitions
477  */
478  SI_Error Save(
479  std::string & a_sBuffer
480  ) const
481  {
482  StringWriter writer(a_sBuffer);
483  return Save(writer);
484  }
485 
486  /*-----------------------------------------------------------------------*/
487  /** @}
488  @{ @name Accessing INI Data */
489 
490  /** Retrieve all section names. The list is returned as an STL vector of
491  names and can be iterated or searched as necessary. Note that the
492  collation order of the returned strings is NOT DEFINED.
493 
494  NOTE! This structure contains only pointers to strings. The actual
495  string data is stored in memory owned by CSimpleIni. Ensure that the
496  CSimpleIni object is not destroyed or Reset() while these pointers
497  are in use!
498 
499  @param a_names Vector that will receive all of the section
500  names. See note above!
501  */
502  void GetAllSections(
503  TNamesDepend & a_names
504  ) const;
505 
506  /** Retrieve all unique key names in a section. The collation order of the
507  returned strings is NOT DEFINED. Only unique key names are returned.
508 
509  NOTE! This structure contains only pointers to strings. The actual
510  string data is stored in memory owned by CSimpleIni. Ensure that the
511  CSimpleIni object is not destroyed or Reset() while these strings
512  are in use!
513 
514  @param a_pSection Section to request data for
515  @param a_names List that will receive all of the key
516  names. See note above!
517 
518  @return true Section was found.
519  @return false Matching section was not found.
520  */
521  bool GetAllKeys(
522  const SI_CHAR * a_pSection,
523  TNamesDepend & a_names
524  ) const;
525 
526  /** Retrieve all values for a specific key. This method can be used when
527  multiple keys are both enabled and disabled.
528 
529  NOTE! The returned values are pointers to string data stored in memory
530  owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
531  or Reset while you are using this pointer!
532 
533  @param a_pSection Section to search
534  @param a_pKey Key to search for
535  @param a_values List to return if the key is not found
536 
537  @return true Key was found.
538  @return false Matching section/key was not found.
539  */
540  bool GetAllValues(
541  const SI_CHAR * a_pSection,
542  const SI_CHAR * a_pKey,
543  TNamesDepend & a_values
544  ) const;
545 
546  /** Query the number of keys in a specific section. Note that if multiple
547  keys are enabled, then this value may be different to the number of
548  keys returned by GetAllKeys.
549 
550  @param a_pSection Section to request data for
551 
552  @return -1 Section does not exist in the file
553  @return >=0 Number of keys in the section
554  */
555  int GetSectionSize(
556  const SI_CHAR * a_pSection
557  ) const;
558 
559  /** Retrieve all key and value pairs for a section. The data is returned
560  as a pointer to an STL map and can be iterated or searched as
561  desired. Note that multiple entries for the same key may exist when
562  multiple keys have been enabled.
563 
564  NOTE! This structure contains only pointers to strings. The actual
565  string data is stored in memory owned by CSimpleIni. Ensure that the
566  CSimpleIni object is not destroyed or Reset() while these strings
567  are in use!
568 
569  @param a_pSection Name of the section to return
570  @return boolean Was a section matching the supplied
571  name found.
572  */
573  const TKeyVal * GetSection(
574  const SI_CHAR * a_pSection
575  ) const;
576 
577  /** Retrieve the value for a specific key. If multiple keys are enabled
578  (see SetMultiKey) then only the first value associated with that key
579  will be returned, see GetAllValues for getting all values with multikey.
580 
581  NOTE! The returned value is a pointer to string data stored in memory
582  owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed
583  or Reset while you are using this pointer!
584 
585  @param a_pSection Section to search
586  @param a_pKey Key to search for
587  @param a_pDefault Value to return if the key is not found
588  @param a_pHasMultiple Optionally receive notification of if there are
589  multiple entries for this key.
590 
591  @return a_pDefault Key was not found in the section
592  @return other Value of the key
593  */
594  const SI_CHAR * GetValue(
595  const SI_CHAR * a_pSection,
596  const SI_CHAR * a_pKey,
597  const SI_CHAR * a_pDefault = NULL,
598  bool * a_pHasMultiple = NULL
599  ) const;
600 
601  /** Add or update a section or value. This will always insert
602  when multiple keys are enabled.
603 
604  @param a_pSection Section to add or update
605  @param a_pKey Key to add or update. Set to NULL to
606  create an empty section.
607  @param a_pValue Value to set. Set to NULL to create an
608  empty section.
609  @param a_pComment Comment to be associated with the section or the
610  key. If a_pKey is NULL then it will be associated
611  with the section, otherwise the key. Note that a
612  comment may be set ONLY when the section or key is
613  first created (i.e. when this function returns the
614  value SI_INSERTED). If you wish to create a section
615  with a comment then you need to create the section
616  separately to the key. The comment string must be
617  in full comment form already (have a comment
618  character starting every line).
619 
620  @return SI_Error See error definitions
621  @return SI_UPDATED Value was updated
622  @return SI_INSERTED Value was inserted
623  */
625  const SI_CHAR * a_pSection,
626  const SI_CHAR * a_pKey,
627  const SI_CHAR * a_pValue,
628  const SI_CHAR * a_pComment = NULL
629  )
630  {
631  return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, true);
632  }
633 
634  /** Delete an entire section, or a key from a section. Note that the
635  data returned by GetSection is invalid and must not be used after
636  anything has been deleted from that section using this method.
637  Note when multiple keys is enabled, this will delete all keys with
638  that name; there is no way to selectively delete individual key/values
639  in this situation.
640 
641  @param a_pSection Section to delete key from, or if
642  a_pKey is NULL, the section to remove.
643  @param a_pKey Key to remove from the section. Set to
644  NULL to remove the entire section.
645  @param a_bRemoveEmpty If the section is empty after this key has
646  been deleted, should the empty section be
647  removed?
648 
649  @return true Key or section was deleted.
650  @return false Key or section was not found.
651  */
652  bool Delete(
653  const SI_CHAR * a_pSection,
654  const SI_CHAR * a_pKey,
655  bool a_bRemoveEmpty = false
656  );
657 
658  /*-----------------------------------------------------------------------*/
659  /** @}
660  @{ @name Converter */
661 
662  /** Return a conversion object to convert text to the same encoding
663  as is used by the Save(), SaveFile() and SaveString() functions.
664  Use this to prepare the strings that you wish to append or prepend
665  to the output INI data.
666  */
667  Converter GetConverter() const {
668  return Converter();
669  }
670 
671  /*-----------------------------------------------------------------------*/
672  /** @} */
673 
674 private:
675  /** Parse the data looking for a file comment and store it if found.
676  */
678  SI_CHAR *& a_pData,
679  bool a_bCopyStrings
680  );
681 
682  /** Parse the data looking for the next valid entry. The memory pointed to
683  by a_pData is modified by inserting NULL characters. The pointer is
684  updated to the current location in the block of text.
685  */
686  bool FindEntry(
687  SI_CHAR *& a_pData,
688  const SI_CHAR *& a_pSection,
689  const SI_CHAR *& a_pKey,
690  const SI_CHAR *& a_pVal,
691  const SI_CHAR *& a_pComment
692  ) const;
693 
694  /** Add the section/key/value to our data.
695 
696  @param a_pSection Section name. Sections will be created if they
697  don't already exist.
698  @param a_pKey Key name. May be NULL to create an empty section.
699  Existing entries will be updated. New entries will
700  be created.
701  @param a_pValue Value for the key.
702  @param a_pComment Comment to be associated with the section or the
703  key. If a_pKey is NULL then it will be associated
704  with the section, otherwise the key. This must be
705  a string in full comment form already (have a
706  comment character starting every line).
707  @param a_bCopyStrings Should copies of the strings be made or not.
708  If false then the pointers will be used as is.
709  */
711  const SI_CHAR * a_pSection,
712  const SI_CHAR * a_pKey,
713  const SI_CHAR * a_pValue,
714  const SI_CHAR * a_pComment,
715  bool a_bCopyStrings
716  );
717 
718  /** Is the supplied character a whitespace character? */
719  inline bool IsSpace(SI_CHAR ch) const {
720  return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
721  }
722 
723  /** Does the supplied character start a comment line? */
724  inline bool IsComment(SI_CHAR ch) const {
725  return (ch == ';' || ch == '#');
726  }
727 
728 
729  /** Skip over a newline character (or characters) for either DOS or UNIX */
730  inline void SkipNewLine(SI_CHAR *& a_pData) const {
731  a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1;
732  }
733 
734  /** Make a copy of the supplied string, replacing the original pointer */
735  SI_Error CopyString(const SI_CHAR *& a_pString);
736 
737  /** Delete a string from the copied strings buffer if necessary */
738  void DeleteString(const SI_CHAR * a_pString);
739 
740  /** Internal use of our string comparison function */
741  bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const {
742  const static SI_STRLESS isLess = SI_STRLESS();
743  return isLess(a_pLeft, a_pRight);
744  }
745 
746  bool IsMultiLineTag(const SI_CHAR * a_pData) const;
747  bool IsMultiLineData(const SI_CHAR * a_pData) const;
748  bool LoadMultiLineText(
749  SI_CHAR *& a_pData,
750  const SI_CHAR *& a_pVal,
751  const SI_CHAR * a_pTagName,
752  bool a_bAllowBlankLinesInComment = false
753  ) const;
754  bool IsNewLineChar(SI_CHAR a_c) const;
755 
756  bool OutputMultiLineText(
757  OutputWriter & a_oOutput,
758  Converter & a_oConverter,
759  const SI_CHAR * a_pText
760  ) const;
761 
762 private:
763  /** Copy of the INI file data in our character format. This will be
764  modified when parsed to have NULL characters added after all
765  interesting string entries. All of the string pointers to sections,
766  keys and values point into this block of memory.
767  */
768  SI_CHAR * m_pData;
769 
770  /** Length of the data that we have stored. Used when deleting strings
771  to determine if the string is stored here or in the allocated string
772  buffer.
773  */
774  size_t m_uDataLen;
775 
776  /** File comment for this data, if one exists. */
777  const SI_CHAR * m_pFileComment;
778 
779  /** Parsed INI data. Section -> (Key -> Value). */
781 
782  /** This vector stores allocated memory for copies of strings that have
783  been supplied after the file load. It will be empty unless SetValue()
784  has been called.
785  */
787 
788  /** Are multiple values permitted for the same key? */
789  bool m_bAllowMultiKey;
790 
791  /** Are data values permitted to span multiple lines? */
792  bool m_bAllowMultiLine;
794  /** Next order value, used to ensure sections and keys are output in the
795  same order that they are loaded/added.
796  */
797  int m_nOrder;
798 };
799 
800 // ---------------------------------------------------------------------------
801 // IMPLEMENTATION
802 // ---------------------------------------------------------------------------
803 
804 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
806  bool a_bAllowMultiKey,
807  bool a_bAllowMultiLine
808  )
809  : m_pData(0)
810  , m_uDataLen(0)
811  , m_pFileComment(NULL)
812  , m_bAllowMultiKey(a_bAllowMultiKey)
813  , m_bAllowMultiLine(a_bAllowMultiLine)
814  , m_nOrder(0)
815 { }
816 
817 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
819 {
820  Reset();
821 }
822 
823 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
824 void
826 {
827  // remove all data
828  delete[] m_pData;
829  m_pData = NULL;
830  m_uDataLen = 0;
831  m_pFileComment = NULL;
832  if (!m_data.empty()) {
833  m_data.erase(m_data.begin(), m_data.end());
834  }
835 
836  // remove all strings
837  if (!m_strings.empty()) {
838  typename TNamesDepend::iterator i = m_strings.begin();
839  for (; i != m_strings.end(); ++i) {
840  delete[] const_cast<SI_CHAR*>(i->pItem);
841  }
842  m_strings.erase(m_strings.begin(), m_strings.end());
843  }
844 }
845 
846 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
847 SI_Error
849  const char * a_pszFile
850  )
851 {
852  FILE * fp = NULL;
853 #if __STDC_WANT_SECURE_LIB__
854  fopen_s(&fp, a_pszFile, "rb");
855 #else
856  fp = fopen(a_pszFile, "rb");
857 #endif
858  if (!fp) {
859  return SI_FILE;
860  }
861  SI_Error rc = LoadFile(fp);
862  fclose(fp);
863  return rc;
864 }
865 
866 #ifdef SI_HAS_WIDE_FILE
867 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
868 SI_Error
870  const SI_WCHAR_T * a_pwszFile
871  )
872 {
873 #ifdef _WIN32
874  FILE * fp = NULL;
875 #if __STDC_WANT_SECURE_LIB__
876  _wfopen_s(&fp, a_pwszFile, L"rb");
877 #else
878  fp = _wfopen(a_pwszFile, L"rb");
879 #endif
880  if (!fp) return SI_FILE;
881  SI_Error rc = LoadFile(fp);
882  fclose(fp);
883  return rc;
884 #else // SI_CONVERT_ICU
885  char szFile[256];
886  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
887  return LoadFile(szFile);
888 #endif
889 }
890 #endif // SI_HAS_WIDE_FILE
891 
892 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
893 SI_Error
895  FILE * a_fpFile
896  )
897 {
898  // load the raw file data
899  int retval = fseek(a_fpFile, 0, SEEK_END);
900  if (retval != 0) {
901  return SI_FILE;
902  }
903  long lSize = ftell(a_fpFile);
904  if (lSize < 0) {
905  return SI_FILE;
906  }
907  char * pData = new char[lSize];
908  if (!pData) {
909  return SI_NOMEM;
910  }
911  fseek(a_fpFile, 0, SEEK_SET);
912  size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
913  if (uRead != (size_t) lSize) {
914  delete[] pData;
915  return SI_FILE;
916  }
917 
918  // convert the raw data to unicode
919  SI_Error rc = Load(pData, uRead);
920  delete[] pData;
921  return rc;
922 }
923 
924 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
925 SI_Error
927  const char * a_pData,
928  size_t a_uDataLen
929  )
930 {
931  SI_CONVERTER converter;
932 
933  // determine the length of the converted data
934  size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
935  if (uLen == (size_t)(-1)) {
936  return SI_FAIL;
937  }
938 
939  // allocate memory for the data, ensure that there is a NULL
940  // terminator wherever the converted data ends
941  SI_CHAR * pData = new SI_CHAR[uLen+1];
942  if (!pData) {
943  return SI_NOMEM;
944  }
945  memset(pData, 0, sizeof(SI_CHAR)*(uLen+1));
946 
947  // convert the data
948  if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) {
949  delete[] pData;
950  return SI_FAIL;
951  }
952 
953  // parse it
954  const static SI_CHAR empty = 0;
955  SI_CHAR * pWork = pData;
956  const SI_CHAR * pSection = &empty;
957  const SI_CHAR * pItem = NULL;
958  const SI_CHAR * pVal = NULL;
959  const SI_CHAR * pComment = NULL;
960 
961  // We copy the strings if we are loading data into this class when we
962  // already have stored some.
963  bool bCopyStrings = (m_pData != NULL);
964 
965  // find a file comment if it exists, this is a comment that starts at the
966  // beginning of the file and continues until the first blank line.
967  SI_Error rc = FindFileComment(pWork, bCopyStrings);
968  if (rc < 0) return rc;
969 
970  // add every entry in the file to the data table
971  while (FindEntry(pWork, pSection, pItem, pVal, pComment)) {
972  rc = AddEntry(pSection, pItem, pVal, pComment, bCopyStrings);
973  if (rc < 0) return rc;
974  }
975 
976  // store these strings if we didn't copy them
977  if (bCopyStrings) {
978  delete[] pData;
979  }
980  else {
981  m_pData = pData;
982  m_uDataLen = uLen+1;
983  }
984 
985  return SI_OK;
986 }
987 
988 #ifdef SI_SUPPORT_IOSTREAMS
989 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
990 SI_Error
992  std::istream & a_istream
993  )
994 {
995  std::string strData;
996  char szBuf[512];
997  do {
998  a_istream.get(szBuf, sizeof(szBuf), '\0');
999  strData.append(szBuf);
1000  }
1001  while (a_istream.good());
1002  return Load(strData);
1003 }
1004 #endif // SI_SUPPORT_IOSTREAMS
1005 
1006 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1007 SI_Error
1009  SI_CHAR *& a_pData,
1010  bool a_bCopyStrings
1011  )
1012 {
1013  // there can only be a single file comment
1014  if (m_pFileComment) {
1015  return SI_OK;
1016  }
1017 
1018  // Load the file comment as multi-line text, this will modify all of
1019  // the newline characters to be single \n chars
1020  if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) {
1021  return SI_OK;
1022  }
1023 
1024  // copy the string if necessary
1025  if (a_bCopyStrings) {
1026  SI_Error rc = CopyString(m_pFileComment);
1027  if (rc < 0) return rc;
1028  }
1029 
1030  return SI_OK;
1031 }
1032 
1033 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1034 bool
1036  SI_CHAR *& a_pData,
1037  const SI_CHAR *& a_pSection,
1038  const SI_CHAR *& a_pKey,
1039  const SI_CHAR *& a_pVal,
1040  const SI_CHAR *& a_pComment
1041  ) const
1042 {
1043  a_pComment = NULL;
1044 
1045  SI_CHAR * pTrail = NULL;
1046  while (*a_pData) {
1047  // skip spaces and empty lines
1048  while (*a_pData && IsSpace(*a_pData)) {
1049  ++a_pData;
1050  }
1051  if (!*a_pData) {
1052  break;
1053  }
1054 
1055  // skip processing of comment lines but keep a pointer to
1056  // the start of the comment.
1057  if (IsComment(*a_pData)) {
1058  LoadMultiLineText(a_pData, a_pComment, NULL, true);
1059  continue;
1060  }
1061 
1062  // process section names
1063  if (*a_pData == '[') {
1064  // skip leading spaces
1065  ++a_pData;
1066  while (*a_pData && IsSpace(*a_pData)) {
1067  ++a_pData;
1068  }
1069 
1070  // find the end of the section name (it may contain spaces)
1071  // and convert it to lowercase as necessary
1072  a_pSection = a_pData;
1073  while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) {
1074  ++a_pData;
1075  }
1076 
1077  // if it's an invalid line, just skip it
1078  if (*a_pData != ']') {
1079  continue;
1080  }
1081 
1082  // remove trailing spaces from the section
1083  pTrail = a_pData - 1;
1084  while (pTrail >= a_pSection && IsSpace(*pTrail)) {
1085  --pTrail;
1086  }
1087  ++pTrail;
1088  *pTrail = 0;
1089 
1090  // skip to the end of the line
1091  ++a_pData; // safe as checked that it == ']' above
1092  while (*a_pData && !IsNewLineChar(*a_pData)) {
1093  ++a_pData;
1094  }
1095 
1096  a_pKey = NULL;
1097  a_pVal = NULL;
1098  return true;
1099  }
1100 
1101  // find the end of the key name (it may contain spaces)
1102  // and convert it to lowercase as necessary
1103  a_pKey = a_pData;
1104  while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
1105  ++a_pData;
1106  }
1107 
1108  // if it's an invalid line, just skip it
1109  if (*a_pData != '=') {
1110  continue;
1111  }
1112 
1113  // empty keys are invalid
1114  if (a_pKey == a_pData) {
1115  while (*a_pData && !IsNewLineChar(*a_pData)) {
1116  ++a_pData;
1117  }
1118  continue;
1119  }
1120 
1121  // remove trailing spaces from the key
1122  pTrail = a_pData - 1;
1123  while (pTrail >= a_pKey && IsSpace(*pTrail)) {
1124  --pTrail;
1125  }
1126  ++pTrail;
1127  *pTrail = 0;
1128 
1129  // skip leading whitespace on the value
1130  ++a_pData; // safe as checked that it == '=' above
1131  while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
1132  ++a_pData;
1133  }
1134 
1135  // find the end of the value which is the end of this line
1136  a_pVal = a_pData;
1137  while (*a_pData && !IsNewLineChar(*a_pData)) {
1138  ++a_pData;
1139  }
1140 
1141  // remove trailing spaces from the value
1142  pTrail = a_pData - 1;
1143  if (*a_pData) { // prepare for the next round
1144  SkipNewLine(a_pData);
1145  }
1146  while (pTrail >= a_pVal && IsSpace(*pTrail)) {
1147  --pTrail;
1148  }
1149  ++pTrail;
1150  *pTrail = 0;
1151 
1152  // check for multi-line entries
1153  if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
1154  // skip the "<<<" to get the tag that will end the multiline
1155  const SI_CHAR * pTagName = a_pVal + 3;
1156  return LoadMultiLineText(a_pData, a_pVal, pTagName);
1157  }
1158 
1159  // return the standard entry
1160  return true;
1161  }
1162 
1163  return false;
1164 }
1165 
1166 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1167 bool
1169  const SI_CHAR * a_pVal
1170  ) const
1171 {
1172  // check for the "<<<" prefix for a multi-line entry
1173  if (*a_pVal++ != '<') return false;
1174  if (*a_pVal++ != '<') return false;
1175  if (*a_pVal++ != '<') return false;
1176  return true;
1177 }
1178 
1179 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1180 bool
1182  const SI_CHAR * a_pData
1183  ) const
1184 {
1185  // data is multi-line if it has any of the following features:
1186  // * whitespace prefix
1187  // * embedded newlines
1188  // * whitespace suffix
1189 
1190  // empty string
1191  if (!*a_pData) {
1192  return false;
1193  }
1194 
1195  // check for prefix
1196  if (IsSpace(*a_pData)) {
1197  return true;
1198  }
1199 
1200  // embedded newlines
1201  while (*a_pData) {
1202  if (IsNewLineChar(*a_pData)) {
1203  return true;
1204  }
1205  ++a_pData;
1206  }
1207 
1208  // check for suffix
1209  if (IsSpace(*--a_pData)) {
1210  return true;
1211  }
1212 
1213  return false;
1214 }
1215 
1216 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1217 bool
1219  SI_CHAR a_c
1220  ) const
1221 {
1222  return (a_c == '\n' || a_c == '\r');
1223 }
1224 
1225 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1226 bool
1228  SI_CHAR *& a_pData,
1229  const SI_CHAR *& a_pVal,
1230  const SI_CHAR * a_pTagName,
1231  bool a_bAllowBlankLinesInComment
1232  ) const
1233 {
1234  // we modify this data to strip all newlines down to a single '\n'
1235  // character. This means that on Windows we need to strip out some
1236  // characters which will make the data shorter.
1237  // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become
1238  // LINE1-LINE1\nLINE2-LINE2\0
1239  // The pDataLine entry is the pointer to the location in memory that
1240  // the current line needs to start to run following the existing one.
1241  // This may be the same as pCurrLine in which case no move is needed.
1242  SI_CHAR * pDataLine = a_pData;
1243  SI_CHAR * pCurrLine;
1244 
1245  // value starts at the current line
1246  a_pVal = a_pData;
1247 
1248  // find the end tag. This tag must start in column 1 and be
1249  // followed by a newline. No whitespace removal is done while
1250  // searching for this tag.
1251  SI_CHAR cEndOfLineChar = *a_pData;
1252  for(;;) {
1253  // if we are loading comments then we need a comment character as
1254  // the first character on every line
1255  if (!a_pTagName && !IsComment(*a_pData)) {
1256  // if we aren't allowing blank lines then we're done
1257  if (!a_bAllowBlankLinesInComment) {
1258  break;
1259  }
1260 
1261  // if we are allowing blank lines then we only include them
1262  // in this comment if another comment follows, so read ahead
1263  // to find out.
1264  SI_CHAR * pCurr = a_pData;
1265  int nNewLines = 0;
1266  while (IsSpace(*pCurr)) {
1267  if (IsNewLineChar(*pCurr)) {
1268  ++nNewLines;
1269  SkipNewLine(pCurr);
1270  }
1271  else {
1272  ++pCurr;
1273  }
1274  }
1275 
1276  // we have a comment, add the blank lines to the output
1277  // and continue processing from here
1278  if (IsComment(*pCurr)) {
1279  for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n';
1280  a_pData = pCurr;
1281  continue;
1282  }
1283 
1284  // the comment ends here
1285  break;
1286  }
1287 
1288  // find the end of this line
1289  pCurrLine = a_pData;
1290  while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData;
1291 
1292  // move this line down to the location that it should be if necessary
1293  if (pDataLine < pCurrLine) {
1294  memmove(pDataLine, pCurrLine, a_pData - pCurrLine);
1295  pDataLine[a_pData - pCurrLine] = '\0';
1296  }
1297 
1298  // end the line with a NULL
1299  cEndOfLineChar = *a_pData;
1300  *a_pData = 0;
1301 
1302  // if are looking for a tag then do the check now. This is done before
1303  // checking for end of the data, so that if we have the tag at the end
1304  // of the data then the tag is removed correctly.
1305  if (a_pTagName &&
1306  (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
1307  {
1308  break;
1309  }
1310 
1311  // if we are at the end of the data then we just automatically end
1312  // this entry and return the current data.
1313  if (!cEndOfLineChar) {
1314  return true;
1315  }
1316 
1317  // otherwise we need to process this newline to ensure that it consists
1318  // of just a single \n character.
1319  pDataLine += (a_pData - pCurrLine);
1320  *a_pData = cEndOfLineChar;
1321  SkipNewLine(a_pData);
1322  *pDataLine++ = '\n';
1323  }
1324 
1325  // if we didn't find a comment at all then return false
1326  if (a_pVal == a_pData) {
1327  a_pVal = NULL;
1328  return false;
1329  }
1330 
1331  // the data (which ends at the end of the last line) needs to be
1332  // null-terminated BEFORE before the newline character(s). If the
1333  // user wants a new line in the multi-line data then they need to
1334  // add an empty line before the tag.
1335  *--pDataLine = '\0';
1336 
1337  // if looking for a tag and if we aren't at the end of the data,
1338  // then move a_pData to the start of the next line.
1339  if (a_pTagName && cEndOfLineChar) {
1340  SI_ASSERT(IsNewLineChar(cEndOfLineChar));
1341  *a_pData = cEndOfLineChar;
1342  SkipNewLine(a_pData);
1343  }
1344 
1345  return true;
1346 }
1347 
1348 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1349 SI_Error
1351  const SI_CHAR *& a_pString
1352  )
1353 {
1354  size_t uLen = 0;
1355  if (sizeof(SI_CHAR) == sizeof(char)) {
1356  uLen = strlen((const char *)a_pString);
1357  }
1358  else if (sizeof(SI_CHAR) == sizeof(wchar_t)) {
1359  uLen = wcslen((const wchar_t *)a_pString);
1360  }
1361  else {
1362  for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
1363  }
1364  ++uLen; // NULL character
1365  SI_CHAR * pCopy = new SI_CHAR[uLen];
1366  if (!pCopy) {
1367  return SI_NOMEM;
1368  }
1369  memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen);
1370  m_strings.push_back(pCopy);
1371  a_pString = pCopy;
1372  return SI_OK;
1373 }
1374 
1375 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1376 SI_Error
1378  const SI_CHAR * a_pSection,
1379  const SI_CHAR * a_pKey,
1380  const SI_CHAR * a_pValue,
1381  const SI_CHAR * a_pComment,
1382  bool a_bCopyStrings
1383  )
1384 {
1385  SI_Error rc;
1386  bool bInserted = false;
1387 
1388  SI_ASSERT(!a_pComment || IsComment(*a_pComment));
1389 
1390  // if we are copying strings then make a copy of the comment now
1391  // because we will need it when we add the entry.
1392  if (a_bCopyStrings && a_pComment) {
1393  rc = CopyString(a_pComment);
1394  if (rc < 0) return rc;
1395  }
1396 
1397  // check for existence of the section first if we need string copies
1398  typename TSection::iterator iSection = m_data.end();
1399  if (a_bCopyStrings) {
1400  iSection = m_data.find(a_pSection);
1401  if (iSection == m_data.end()) {
1402  // if the section doesn't exist then we need a copy as the
1403  // string needs to last beyond the end of this function
1404  // because we will be inserting the section next
1405  rc = CopyString(a_pSection);
1406  if (rc < 0) return rc;
1407  }
1408  }
1409 
1410  // create the section entry
1411  if (iSection == m_data.end()) {
1412  Entry oKey(a_pSection, ++m_nOrder);
1413  if (a_pComment && (!a_pKey || !a_pValue)) {
1414  oKey.pComment = a_pComment;
1415  }
1416  typename TSection::value_type oEntry(oKey, TKeyVal());
1417  typedef typename TSection::iterator SectionIterator;
1418  std::pair<SectionIterator,bool> i =
1419  m_data.insert(oEntry);
1420  iSection = i.first;
1421  bInserted = true;
1422  }
1423  if (!a_pKey || !a_pValue) {
1424  // section only entries are specified with pItem and pVal as NULL
1425  return bInserted ? SI_INSERTED : SI_UPDATED;
1426  }
1427 
1428  // check for existence of the key
1429  TKeyVal & keyval = iSection->second;
1430  typename TKeyVal::iterator iKey = keyval.find(a_pKey);
1431 
1432  // make string copies if necessary
1433  if (a_bCopyStrings) {
1434  if (m_bAllowMultiKey || iKey == keyval.end()) {
1435  // if the key doesn't exist then we need a copy as the
1436  // string needs to last beyond the end of this function
1437  // because we will be inserting the key next
1438  rc = CopyString(a_pKey);
1439  if (rc < 0) return rc;
1440  }
1441 
1442  // we always need a copy of the value
1443  rc = CopyString(a_pValue);
1444  if (rc < 0) return rc;
1445  }
1446 
1447  // create the key entry
1448  if (iKey == keyval.end() || m_bAllowMultiKey) {
1449  Entry oKey(a_pKey, ++m_nOrder);
1450  if (a_pComment) {
1451  oKey.pComment = a_pComment;
1452  }
1453  typename TKeyVal::value_type oEntry(oKey, static_cast<const char*>(NULL));
1454  iKey = keyval.insert(oEntry);
1455  bInserted = true;
1456  }
1457  iKey->second = a_pValue;
1458  return bInserted ? SI_INSERTED : SI_UPDATED;
1459 }
1460 
1461 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1462 const SI_CHAR *
1464  const SI_CHAR * a_pSection,
1465  const SI_CHAR * a_pKey,
1466  const SI_CHAR * a_pDefault,
1467  bool * a_pHasMultiple
1468  ) const
1469 {
1470  if (a_pHasMultiple) {
1471  *a_pHasMultiple = false;
1472  }
1473  if (!a_pSection || !a_pKey) {
1474  return a_pDefault;
1475  }
1476  typename TSection::const_iterator iSection = m_data.find(a_pSection);
1477  if (iSection == m_data.end()) {
1478  return a_pDefault;
1479  }
1480  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1481  if (iKeyVal == iSection->second.end()) {
1482  return a_pDefault;
1483  }
1484 
1485  // check for multiple entries with the same key
1486  if (m_bAllowMultiKey && a_pHasMultiple) {
1487  typename TKeyVal::const_iterator iTemp = iKeyVal;
1488  if (++iTemp != iSection->second.end()) {
1489  if (!IsLess(a_pKey, iTemp->first.pItem)) {
1490  *a_pHasMultiple = true;
1491  }
1492  }
1493  }
1494 
1495  return iKeyVal->second;
1496 }
1497 
1498 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1499 bool
1501  const SI_CHAR * a_pSection,
1502  const SI_CHAR * a_pKey,
1503  TNamesDepend & a_values
1504  ) const
1505 {
1506  if (!a_pSection || !a_pKey) {
1507  return false;
1508  }
1509  typename TSection::const_iterator iSection = m_data.find(a_pSection);
1510  if (iSection == m_data.end()) {
1511  return false;
1512  }
1513  typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey);
1514  if (iKeyVal == iSection->second.end()) {
1515  return false;
1516  }
1517 
1518  // insert all values for this key
1519  a_values.push_back(iKeyVal->second);
1520  if (m_bAllowMultiKey) {
1521  ++iKeyVal;
1522  while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) {
1523  a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.nOrder));
1524  ++iKeyVal;
1525  }
1526  }
1527 
1528  return true;
1529 }
1530 
1531 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1532 int
1534  const SI_CHAR * a_pSection
1535  ) const
1536 {
1537  if (!a_pSection) {
1538  return -1;
1539  }
1540 
1541  typename TSection::const_iterator iSection = m_data.find(a_pSection);
1542  if (iSection == m_data.end()) {
1543  return -1;
1544  }
1545  const TKeyVal & section = iSection->second;
1546 
1547  // if multi-key isn't permitted then the section size is
1548  // the number of keys that we have.
1549  if (!m_bAllowMultiKey || section.empty()) {
1550  return (int) section.size();
1551  }
1552 
1553  // otherwise we need to count them
1554  int nCount = 0;
1555  const SI_CHAR * pLastKey = NULL;
1556  typename TKeyVal::const_iterator iKeyVal = section.begin();
1557  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) {
1558  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1559  ++nCount;
1560  pLastKey = iKeyVal->first.pItem;
1561  }
1562  }
1563  return nCount;
1564 }
1565 
1566 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1569  const SI_CHAR * a_pSection
1570  ) const
1571 {
1572  if (a_pSection) {
1573  typename TSection::const_iterator i = m_data.find(a_pSection);
1574  if (i != m_data.end()) {
1575  return &(i->second);
1576  }
1577  }
1578  return 0;
1579 }
1580 
1581 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1582 void
1584  TNamesDepend & a_names
1585  ) const
1586 {
1587  typename TSection::const_iterator i = m_data.begin();
1588  for (int n = 0; i != m_data.end(); ++i, ++n ) {
1589  a_names.push_back(i->first);
1590  }
1591 }
1592 
1593 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1594 bool
1596  const SI_CHAR * a_pSection,
1597  TNamesDepend & a_names
1598  ) const
1599 {
1600  if (!a_pSection) {
1601  return false;
1602  }
1603 
1604  typename TSection::const_iterator iSection = m_data.find(a_pSection);
1605  if (iSection == m_data.end()) {
1606  return false;
1607  }
1608 
1609  const TKeyVal & section = iSection->second;
1610  const SI_CHAR * pLastKey = NULL;
1611  typename TKeyVal::const_iterator iKeyVal = section.begin();
1612  for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) {
1613  if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) {
1614  a_names.push_back(iKeyVal->first);
1615  pLastKey = iKeyVal->first.pItem;
1616  }
1617  }
1618 
1619  return true;
1620 }
1621 
1622 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1623 SI_Error
1625  const char * a_pszFile
1626  ) const
1627 {
1628  FILE * fp = NULL;
1629 #if __STDC_WANT_SECURE_LIB__
1630  fopen_s(&fp, a_pszFile, "wb");
1631 #else
1632  fp = fopen(a_pszFile, "wb");
1633 #endif
1634  if (!fp) return SI_FILE;
1635  SI_Error rc = SaveFile(fp);
1636  fclose(fp);
1637  return rc;
1638 }
1639 
1640 #ifdef SI_HAS_WIDE_FILE
1641 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1642 SI_Error
1644  const SI_WCHAR_T * a_pwszFile
1645  ) const
1646 {
1647 #ifdef _WIN32
1648  FILE * fp = _wfopen(a_pwszFile, L"wb");
1649  if (!fp) return SI_FILE;
1650  SI_Error rc = SaveFile(fp);
1651  fclose(fp);
1652  return rc;
1653 #else // SI_CONVERT_ICU
1654  char szFile[256];
1655  u_austrncpy(szFile, a_pwszFile, sizeof(szFile));
1656  return SaveFile(szFile);
1657 #endif
1658 }
1659 #endif // SI_HAS_WIDE_FILE
1660 
1661 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1662 SI_Error
1664  FILE * a_pFile
1665  ) const
1666 {
1667  FileWriter writer(a_pFile);
1668  return Save(writer);
1669 }
1670 
1671 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1672 SI_Error
1674  OutputWriter & a_oOutput
1675  ) const
1676 {
1677  Converter convert;
1678 
1679  // get all of the sections sorted in load order
1680  TNamesDepend oSections;
1681  GetAllSections(oSections);
1682 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__BORLANDC__)
1683  oSections.sort();
1684 #else
1685  oSections.sort(typename Entry::LoadOrder());
1686 #endif
1687 
1688  // write the file comment if we have one
1689  bool bNeedNewLine = false;
1690  if (m_pFileComment) {
1691  if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) {
1692  return SI_FAIL;
1693  }
1694  bNeedNewLine = true;
1695  }
1696 
1697  // iterate through our sections and output the data
1698  typename TNamesDepend::const_iterator iSection = oSections.begin();
1699  for ( ; iSection != oSections.end(); ++iSection ) {
1700  // write out the comment if there is one
1701  if (iSection->pComment) {
1702  if (!convert.ConvertToStore(iSection->pComment)) {
1703  return SI_FAIL;
1704  }
1705  if (bNeedNewLine) {
1706  a_oOutput.Write(SI_NEWLINE_A);
1707  a_oOutput.Write(SI_NEWLINE_A);
1708  }
1709  a_oOutput.Write(convert.Data());
1710  a_oOutput.Write(SI_NEWLINE_A);
1711  bNeedNewLine = false;
1712  }
1713 
1714  if (bNeedNewLine) {
1715  a_oOutput.Write(SI_NEWLINE_A);
1716  a_oOutput.Write(SI_NEWLINE_A);
1717  bNeedNewLine = false;
1718  }
1719 
1720  // write the section (unless there is no section name)
1721  if (*iSection->pItem) {
1722  if (!convert.ConvertToStore(iSection->pItem)) {
1723  return SI_FAIL;
1724  }
1725  a_oOutput.Write("[");
1726  a_oOutput.Write(convert.Data());
1727  a_oOutput.Write("]");
1728  a_oOutput.Write(SI_NEWLINE_A);
1729  }
1730 
1731  // get all of the keys sorted in load order
1732  TNamesDepend oKeys;
1733  GetAllKeys(iSection->pItem, oKeys);
1734 #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__BORLANDC__)
1735  oKeys.sort();
1736 #else
1737  oKeys.sort(typename Entry::LoadOrder());
1738 #endif
1739 
1740  // write all keys and values
1741  typename TNamesDepend::const_iterator iKey = oKeys.begin();
1742  for ( ; iKey != oKeys.end(); ++iKey) {
1743  // get all values for this key
1744  TNamesDepend oValues;
1745  GetAllValues(iSection->pItem, iKey->pItem, oValues);
1746 
1747  // write out the comment if there is one
1748  if (iKey->pComment) {
1749  a_oOutput.Write(SI_NEWLINE_A);
1750  if (!OutputMultiLineText(a_oOutput, convert, iKey->pComment)) {
1751  return SI_FAIL;
1752  }
1753  }
1754 
1755  typename TNamesDepend::const_iterator iValue = oValues.begin();
1756  for ( ; iValue != oValues.end(); ++iValue) {
1757  // write the key
1758  if (!convert.ConvertToStore(iKey->pItem)) {
1759  return SI_FAIL;
1760  }
1761  a_oOutput.Write(convert.Data());
1762 
1763  // write the value
1764  if (!convert.ConvertToStore(iValue->pItem)) {
1765  return SI_FAIL;
1766  }
1767  a_oOutput.Write("=");
1768  if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
1769  // multi-line data needs to be processed specially to ensure
1770  // that we use the correct newline format for the current system
1771  a_oOutput.Write("<<<SI-END-OF-MULTILINE-TEXT" SI_NEWLINE_A);
1772  if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
1773  return SI_FAIL;
1774  }
1775  a_oOutput.Write("SI-END-OF-MULTILINE-TEXT");
1776  }
1777  else {
1778  a_oOutput.Write(convert.Data());
1779  }
1780  a_oOutput.Write(SI_NEWLINE_A);
1781  }
1782  }
1783 
1784  bNeedNewLine = true;
1785  }
1786 
1787  return SI_OK;
1788 }
1789 
1790 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1791 bool
1793  OutputWriter & a_oOutput,
1794  Converter & a_oConverter,
1795  const SI_CHAR * a_pText
1796  ) const
1797 {
1798  const SI_CHAR * pEndOfLine;
1799  SI_CHAR cEndOfLineChar = *a_pText;
1800  while (cEndOfLineChar) {
1801  // find the end of this line
1802  pEndOfLine = a_pText;
1803  for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ;
1804  cEndOfLineChar = *pEndOfLine;
1805 
1806  // temporarily null terminate, convert and output the line
1807  *const_cast<SI_CHAR*>(pEndOfLine) = 0;
1808  if (!a_oConverter.ConvertToStore(a_pText)) {
1809  return false;
1810  }
1811  *const_cast<SI_CHAR*>(pEndOfLine) = cEndOfLineChar;
1812  a_pText += (pEndOfLine - a_pText) + 1;
1813  a_oOutput.Write(a_oConverter.Data());
1814  a_oOutput.Write(SI_NEWLINE_A);
1815  }
1816  return true;
1817 }
1818 
1819 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1820 bool
1822  const SI_CHAR * a_pSection,
1823  const SI_CHAR * a_pKey,
1824  bool a_bRemoveEmpty
1825  )
1826 {
1827  if (!a_pSection) {
1828  return false;
1829  }
1830 
1831  typename TSection::iterator iSection = m_data.find(a_pSection);
1832  if (iSection == m_data.end()) {
1833  return false;
1834  }
1835 
1836  // remove a single key if we have a keyname
1837  if (a_pKey) {
1838  typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey);
1839  if (iKeyVal == iSection->second.end()) {
1840  return false;
1841  }
1842 
1843  // remove any copied strings and then the key
1844  typename TKeyVal::iterator iDelete;
1845  do {
1846  iDelete = iKeyVal++;
1847 
1848  DeleteString(iDelete->first.pItem);
1849  DeleteString(iDelete->second);
1850  iSection->second.erase(iDelete);
1851  }
1852  while (iKeyVal != iSection->second.end()
1853  && !IsLess(a_pKey, iKeyVal->first.pItem));
1854 
1855  // done now if the section is not empty or we are not pruning away
1856  // the empty sections. Otherwise let it fall through into the section
1857  // deletion code
1858  if (!a_bRemoveEmpty || !iSection->second.empty()) {
1859  return true;
1860  }
1861  }
1862  else {
1863  // delete all copied strings from this section. The actual
1864  // entries will be removed when the section is removed.
1865  typename TKeyVal::iterator iKeyVal = iSection->second.begin();
1866  for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) {
1867  DeleteString(iKeyVal->first.pItem);
1868  DeleteString(iKeyVal->second);
1869  }
1870  }
1871 
1872  // delete the section itself
1873  DeleteString(iSection->first.pItem);
1874  m_data.erase(iSection);
1875 
1876  return true;
1877 }
1878 
1879 template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
1880 void
1882  const SI_CHAR * a_pString
1883  )
1884 {
1885  // strings may exist either inside the data block, or they will be
1886  // individually allocated and stored in m_strings. We only physically
1887  // delete those stored in m_strings.
1888  if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) {
1889  typename TNamesDepend::iterator i = m_strings.begin();
1890  for (;i != m_strings.end(); ++i) {
1891  if (a_pString == i->pItem) {
1892  delete[] const_cast<SI_CHAR*>(i->pItem);
1893  m_strings.erase(i);
1894  break;
1895  }
1896  }
1897  }
1898 }
1899 
1900 // ---------------------------------------------------------------------------
1901 // CONVERSION FUNCTIONS
1902 // ---------------------------------------------------------------------------
1903 
1904 /**
1905  * Generic case-sensitive less than comparison. This class returns numerically
1906  * ordered ASCII case-sensitive text for all possible sizes and types of
1907  * SI_CHAR.
1908  */
1909 template<class SI_CHAR>
1911  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
1912  long cmp;
1913  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1914  cmp = (long) *pLeft - (long) *pRight;
1915  if (cmp != 0) {
1916  return cmp < 0;
1917  }
1918  }
1919  return *pRight != 0;
1920  }
1921 };
1922 
1923 /**
1924  * Generic ASCII case-insensitive less than comparison. This class returns
1925  * numerically ordered ASCII case-insensitive text for all possible sizes
1926  * and types of SI_CHAR. It is not safe for MBCS text comparison where
1927  * ASCII A-Z characters are used in the encoding of multi-byte characters.
1928  */
1929 template<class SI_CHAR>
1931  inline SI_CHAR locase(SI_CHAR ch) const {
1932  return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a');
1933  }
1934  bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const {
1935  long cmp;
1936  for ( ;*pLeft && *pRight; ++pLeft, ++pRight) {
1937  cmp = (long) locase(*pLeft) - (long) locase(*pRight);
1938  if (cmp != 0) {
1939  return cmp < 0;
1940  }
1941  }
1942  return *pRight != 0;
1943  }
1944 };
1945 
1946 /**
1947  * Null conversion class for MBCS/UTF-8 to char (or equivalent).
1948  */
1949 template<class SI_CHAR>
1951 public:
1953 
1954  /* copy and assignment */
1955  SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); }
1957  return *this;
1958  }
1959 
1960  /** Calculate the number of SI_CHAR required for converting the input
1961  * from the storage format. The storage format is always UTF-8 or MBCS.
1962  *
1963  * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1964  * @param a_uInputDataLen Length of storage format data in bytes. This
1965  * must be the actual length of the data, including
1966  * NULL byte if NULL terminated string is required.
1967  * @return Number of SI_CHAR required by the string when
1968  * converted. If there are embedded NULL bytes in the
1969  * input data, only the string up and not including
1970  * the NULL byte will be converted.
1971  * @return -1 cast to size_t on a conversion error.
1972  */
1973  virtual size_t SizeFromStore(
1974  const char * a_pInputData,
1975  size_t a_uInputDataLen)
1976  {
1977  (void)a_pInputData;
1978  SI_ASSERT(a_uInputDataLen != (size_t) -1);
1979 
1980  // ASCII/MBCS/UTF-8 needs no conversion
1981  return a_uInputDataLen;
1982  }
1983 
1984  /** Convert the input string from the storage format to SI_CHAR.
1985  * The storage format is always UTF-8 or MBCS.
1986  *
1987  * @param a_pInputData Data in storage format to be converted to SI_CHAR.
1988  * @param a_uInputDataLen Length of storage format data in bytes. This
1989  * must be the actual length of the data, including
1990  * NULL byte if NULL terminated string is required.
1991  * @param a_pOutputData Pointer to the output buffer to received the
1992  * converted data.
1993  * @param a_uOutputDataSize Size of the output buffer in SI_CHAR.
1994  * @return true if all of the input data was successfully
1995  * converted.
1996  */
1997  virtual bool ConvertFromStore(
1998  const char * a_pInputData,
1999  size_t a_uInputDataLen,
2000  SI_CHAR * a_pOutputData,
2001  size_t a_uOutputDataSize)
2002  {
2003  // ASCII/MBCS/UTF-8 needs no conversion
2004  if (a_uInputDataLen > a_uOutputDataSize) {
2005  return false;
2006  }
2007  memcpy(a_pOutputData, a_pInputData, a_uInputDataLen);
2008  return true;
2009  }
2010 
2011  /** Calculate the number of char required by the storage format of this
2012  * data. The storage format is always UTF-8 or MBCS.
2013  *
2014  * @param a_pInputData NULL terminated string to calculate the number of
2015  * bytes required to be converted to storage format.
2016  * @return Number of bytes required by the string when
2017  * converted to storage format. This size always
2018  * includes space for the terminating NULL character.
2019  * @return -1 cast to size_t on a conversion error.
2020  */
2021  size_t SizeToStore(
2022  const SI_CHAR * a_pInputData)
2023  {
2024  // ASCII/MBCS/UTF-8 needs no conversion
2025  return strlen((const char *)a_pInputData) + 1;
2026  }
2027 
2028  /** Convert the input string to the storage format of this data.
2029  * The storage format is always UTF-8 or MBCS.
2030  *
2031  * @param a_pInputData NULL terminated source string to convert. All of
2032  * the data will be converted including the
2033  * terminating NULL character.
2034  * @param a_pOutputData Pointer to the buffer to receive the converted
2035  * string.
2036  * @param a_uOutputDataSize Size of the output buffer in char.
2037  * @return true if all of the input data, including the
2038  * terminating NULL character was successfully
2039  * converted.
2040  */
2042  const SI_CHAR * a_pInputData,
2043  char * a_pOutputData,
2044  size_t a_uOutputDataSize)
2045  {
2046  // calc input string length (SI_CHAR type and size independent)
2047  size_t uInputLen = strlen((const char *)a_pInputData) + 1;
2048  if (uInputLen > a_uOutputDataSize) {
2049  return false;
2050  }
2051 
2052  // ascii/UTF-8 needs no conversion
2053  memcpy(a_pOutputData, a_pInputData, uInputLen);
2054  return true;
2055  }
2056 };
2057 
2058 /** MRPT custom INI file parser to allow minimal file preprocessing:
2059 * - multiline entries via an end-of-line backslash ('\')
2060 */
2061 struct MRPT_IniFileParser : public SI_ConvertA<char>
2062 {
2064 
2065  /* copy and assignment */
2069  return *this;
2070  }
2071 
2072  virtual size_t SizeFromStore(
2073  const char * a_pInputData,
2074  size_t a_uInputDataLen) MRPT_OVERRIDE
2075  {
2076  SI_ASSERT(a_uInputDataLen != (size_t)-1);
2077  return do_parse(a_pInputData, a_uInputDataLen, nullptr);
2078  }
2079 
2080  virtual bool ConvertFromStore(
2081  const char * a_pInputData,
2082  size_t a_uInputDataLen,
2083  char * a_pOutputData,
2084  size_t a_uOutputDataSize) MRPT_OVERRIDE
2085  {
2086  this->do_parse(a_pInputData, a_uInputDataLen, a_pOutputData);
2087  return true;
2088  }
2089 
2090 private:
2092  {
2093  std::map<std::string, std::string> defined_vars;
2094  std::map<std::string, double> defined_vars_values;
2095  unsigned int line_count = 1;
2096  };
2097 
2098  // Return a string or a number (as string) if expr = "$eval{...}"
2100  {
2101  expr = mrpt::system::trim(expr);
2102  while (expr.size()>6)
2103  {
2104  auto p = expr.find("$eval{");
2105  if (p == std::string::npos) break; // done!
2106  auto pend = expr.find("}",p);
2107  if (pend == std::string::npos)
2108  throw std::runtime_error(mrpt::format("Line %u: Expected closing `}` near: `%s`", pc.line_count, expr.c_str()));
2109 
2110  const auto substr = expr.substr(p + 6, pend - p - 6);
2112  cexpr.compile(substr, pc.defined_vars_values, mrpt::format("Line %u: ", pc.line_count));
2113 
2114  std::string new_expr = expr.substr(0, p);
2115  new_expr += mrpt::format("%e", cexpr.eval());
2116  new_expr += expr.substr(pend+1);
2117 
2118  new_expr.swap(expr);
2119  }
2120  return expr;
2121  }
2122 
2123  void parse_process_var_define(ParseContext &pc, const std::string &var_name, const std::string &var_value)
2124  {
2125  if (!var_name.empty()) {
2126  pc.defined_vars[var_name] = var_value;
2127  if (!var_value.empty()) {
2128  pc.defined_vars_values[var_name] = ::atof(parse_process_var_eval(pc,var_value).c_str());
2129  }
2130  }
2131  }
2132 
2133  /** Shared code for the two virtual methods. If out_str==NULL, just count output bytes */
2134  size_t do_parse(
2135  const char * in_str,
2136  const size_t in_len,
2137  char * out_str)
2138  {
2139  ParseContext pc;
2140  size_t out_len = 0, i = 0;
2141  while (i < in_len)
2142  {
2143  const char c = in_str[i];
2144  if (c == '\n') {
2145  pc.line_count++;
2146  }
2147 
2148  if (c == '\\' && i < in_len - 1 && (in_str[i + 1] == '\r' || in_str[i + 1] == '\n'))
2149  {
2150  // Skip the backslash + one newline: CR "\r", LF "\n", CR+LF "\r\n"
2151  if (i < in_len - 2 && in_str[i + 1] == '\r' && in_str[i + 2] == '\n') {
2152  //out_len += 0;
2153  i += 3;
2154  }
2155  else if (in_str[i + 1] == '\r' || in_str[i + 1] == '\n') {
2156  //out_len += 0;
2157  i += 2;
2158  }
2159  else {
2160  throw std::runtime_error("MRPT_IniFileParser: parse error, shouldn't reach here!");
2161  }
2162  }
2163  else
2164  {
2165  // Handle "@define varname value"
2166  if (in_len>i+7 && !::strncmp(in_str + i, "@define",7))
2167  {
2168  // Extract rest of this line:
2169  i += 7;
2170  std::string var_name, var_value;
2171  bool in_var_name = false, done_var_name = false;
2172  while (i < in_len && in_str[i] != '\r' && in_str[i] != '\n')
2173  {
2174  const char c = in_str[i];
2175  i++;
2176  if (c != ' ' && c != '\t')
2177  {
2178  // not whitespace
2179  if (!in_var_name && !done_var_name) {
2180  in_var_name = true;
2181  }
2182  }
2183  else
2184  {
2185  // whitespace
2186  if (in_var_name) {
2187  in_var_name = false;
2188  done_var_name = true;
2189  }
2190  }
2191  if (in_var_name) {
2192  var_name += c;
2193  }
2194  if (done_var_name) {
2195  var_value += c;
2196  }
2197  }
2198 
2199  parse_process_var_define(pc, var_name, var_value);
2200  continue;
2201  }
2202 
2203  // Handle "${varname}"
2204  if (in_len>i + 4 && in_str[i]=='$' && in_str[i+1] == '{')
2205  {
2206  // extract varname:
2207  i += 2;
2208  std::string varname;
2209  bool end_ok = false;
2210  while (i < in_len && in_str[i]!='\n' && in_str[i]!='\r')
2211  {
2212  const char ch = in_str[i];
2213  i++;
2214  if (ch == '}') {
2215  end_ok = true;
2216  break;
2217  }
2218  varname += ch;
2219  }
2220  if (!end_ok) {
2221  throw std::runtime_error(mrpt::format("Line %u: Expected closing `}` near: `%s`", pc.line_count,varname.c_str()));
2222  }
2223 
2224  const auto it = pc.defined_vars.find(varname);
2225  if (it== pc.defined_vars.end())
2226  throw std::runtime_error(mrpt::format("Line %u: Unknown variable `${%s}`", pc.line_count,varname.c_str()));
2227 
2228  const auto str_out = parse_process_var_eval(pc,it->second);
2229 
2230  for (const char ch : str_out)
2231  {
2232  if (out_str) out_str[out_len] = ch;
2233  out_len++;
2234  }
2235  continue;
2236  }
2237 
2238  // Handle "$eval{expression}"
2239  if (in_len>i + 7 && !strncmp(in_str+i,"$eval{", 6))
2240  {
2241  // extract expression:
2242  std::string expr;
2243  bool end_ok = false;
2244  while (i < in_len && in_str[i] != '\n' && in_str[i] != '\r')
2245  {
2246  const char ch = in_str[i];
2247  i++;
2248  expr += ch;
2249  if (ch == '}') {
2250  end_ok = true;
2251  break;
2252  }
2253  }
2254  if (!end_ok) {
2255  throw std::runtime_error(mrpt::format("Line %u: Expected closing `}` near: `%s`", pc.line_count, expr.c_str()));
2256  }
2257 
2258  const std::string res = parse_process_var_eval(pc,expr);
2259 
2260  for (const char ch : res)
2261  {
2262  if (out_str) out_str[out_len] = ch;
2263  out_len++;
2264  }
2265  continue;
2266  }
2267 
2268  // Normal case:
2269  if (out_str) {
2270  out_str[out_len] = c;
2271  }
2272  out_len++;
2273  i++;
2274  }
2275  }
2276  return out_len;
2277  }
2278 
2279 };
2280 
2281 
2282 
2283 // ---------------------------------------------------------------------------
2284 // TYPE DEFINITIONS
2285 // ---------------------------------------------------------------------------
2286 
2287 typedef CSimpleIniTempl<char,
2289 typedef CSimpleIniTempl<char,
2291 
2293 
2294 #ifdef _UNICODE
2295 # define CSimpleIni CSimpleIniW
2296 # define CSimpleIniCase CSimpleIniCaseW
2297 # define SI_NEWLINE SI_NEWLINE_W
2298 #else // !_UNICODE
2299 # define CSimpleIni CSimpleIniA
2300 # define CSimpleIniCase CSimpleIniCaseA
2301 # define SI_NEWLINE SI_NEWLINE_A
2302 #endif // _UNICODE
2303 
2304 } // end namespace
2305 } // end namespace
2306 } // end namespace
2307 
2308 #endif // INCLUDED_SimpleIni_h
Converter & operator=(const Converter &rhs)
Definition: SimpleIni.h:231
void BASE_IMPEXP memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) MRPT_NO_THROWS
An OS and compiler independent version of "memcpy".
Definition: os.cpp:358
OutputWriter & operator=(const OutputWriter &)
SI_CHAR locase(SI_CHAR ch) const
Definition: SimpleIni.h:1931
FILE BASE_IMPEXP * fopen(const char *fileName, const char *mode) MRPT_NO_THROWS
An OS-independent version of fopen.
Definition: os.cpp:255
FileWriter & operator=(const FileWriter &)
std::map< std::string, std::string > defined_vars
Definition: SimpleIni.h:2093
int m_nOrder
Next order value, used to ensure sections and keys are output in the same order that they are loaded/...
Definition: SimpleIni.h:793
Null conversion class for MBCS/UTF-8 to char (or equivalent).
Definition: SimpleIni.h:1950
int GetSectionSize(const SI_CHAR *a_pSection) const
Query the number of keys in a specific section.
Definition: SimpleIni.h:1533
CSimpleIniTempl< char, SI_GenericNoCase< char >, SI_ConvertA< char > > CSimpleIniA
Definition: SimpleIni.h:2288
bool IsSpace(SI_CHAR ch) const
Is the supplied character a whitespace character?
Definition: SimpleIni.h:715
bool operator()(const Entry &lhs, const Entry &rhs) const
Definition: SimpleIni.h:148
#define MRPT_OVERRIDE
C++11 "override" for virtuals:
int BASE_IMPEXP void BASE_IMPEXP fclose(FILE *f)
An OS-independent version of fclose.
Definition: os.cpp:272
OutputWriter class to write the INI data to an ostream.
Definition: SimpleIni.h:209
const SI_CHAR * pItem
Definition: SimpleIni.h:115
Entry & operator=(const Entry &rhs)
Definition: SimpleIni.h:125
GLenum GLsizei n
Definition: glext.h:4618
Scalar * iterator
Definition: eigen_plugins.h:23
bool m_bAllowMultiKey
Are multiple values permitted for the same key?
Definition: SimpleIni.h:785
std::multimap< Entry, const SI_CHAR *, typename Entry::KeyOrder > TKeyVal
map keys to values
Definition: SimpleIni.h:158
#define SEEK_END
Definition: zconf.h:302
bool IsMultiKey() const
Get the storage format of the INI data.
Definition: SimpleIni.h:298
bool operator()(const Entry &lhs, const Entry &rhs) const
Definition: SimpleIni.h:140
bool IsMultiLineTag(const SI_CHAR *a_pData) const
Definition: SimpleIni.h:1168
std::map< std::string, double > defined_vars_values
Definition: SimpleIni.h:2094
void SetMultiLine(bool a_bAllowMultiLine=true)
Should data values be permitted to span multiple lines in the file.
Definition: SimpleIni.h:307
interface definition for the OutputWriter object to pass to Save() in order to output the INI file da...
Definition: SimpleIni.h:171
SI_Error SetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, const SI_CHAR *a_pComment=NULL)
Add or update a section or value.
Definition: SimpleIni.h:621
void SkipNewLine(SI_CHAR *&a_pData) const
Skip over a newline character (or characters) for either DOS or UNIX.
Definition: SimpleIni.h:726
virtual bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, char *a_pOutputData, size_t a_uOutputDataSize) MRPT_OVERRIDE
Convert the input string from the storage format to SI_CHAR.
Definition: SimpleIni.h:2080
void DeleteString(const SI_CHAR *a_pString)
Delete a string from the copied strings buffer if necessary.
Definition: SimpleIni.h:1881
Simple INI file reader.
Definition: SimpleIni.h:110
const Scalar * const_iterator
Definition: eigen_plugins.h:24
A wrapper of exprtk runtime expression compiler: it takes a string representing an expression (from a...
CSimpleIniTempl< char, SI_GenericCase< char >, SI_ConvertA< char > > CSimpleIniCaseA
Definition: SimpleIni.h:2290
SI_Error LoadFile(const char *a_pszFile)
Load an INI file from disk into memory.
Definition: SimpleIni.h:848
Entry(const SI_CHAR *a_pszItem=NULL, int a_nOrder=0)
Definition: SimpleIni.h:119
bool m_bAllowMultiLine
Are data values permitted to span multiple lines?
Definition: SimpleIni.h:788
void compile(const std::string &expression, const std::map< std::string, double > &variables=std::map< std::string, double >(), const std::string &expr_name_for_error_reporting=std::string())
Initializes the object by compiling an expression.
size_t SizeToStore(const SI_CHAR *a_pInputData)
Calculate the number of char required by the storage format of this data.
Definition: SimpleIni.h:2021
#define SI_ASSERT(x)
Definition: SimpleIni.h:47
void SetMultiKey(bool a_bAllowMultiKey=true)
Should multiple identical keys be permitted in the file.
Definition: SimpleIni.h:293
MRPT custom INI file parser to allow minimal file preprocessing:
Definition: SimpleIni.h:2061
size_t do_parse(const char *in_str, const size_t in_len, char *out_str)
Shared code for the two virtual methods.
Definition: SimpleIni.h:2134
#define SI_WCHAR_T
Definition: SimpleIni.h:79
bool ConvertToStore(const SI_CHAR *a_pszString)
Definition: SimpleIni.h:235
bool GetAllKeys(const SI_CHAR *a_pSection, TNamesDepend &a_names) const
Retrieve all unique key names in a section.
Definition: SimpleIni.h:1595
CSimpleIniTempl< char, SI_GenericNoCase< char >, MRPT_IniFileParser > MRPT_CSimpleIni
Definition: SimpleIni.h:2292
SI_Error AddEntry(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pValue, const SI_CHAR *a_pComment, bool a_bCopyStrings)
Add the section/key/value to our data.
Definition: SimpleIni.h:1377
bool operator<(const CArray< T, N > &x, const CArray< T, N > &y)
Definition: CArray.h:281
void Write(const char *a_pBuf) MRPT_OVERRIDE
Definition: SimpleIni.h:186
double eval() const
Evaluates the current value of the precompiled formula.
const GLubyte * c
Definition: glext.h:5590
TNamesDepend m_strings
This vector stores allocated memory for copies of strings that have been supplied after the file load...
Definition: SimpleIni.h:782
Converter GetConverter() const
Return a conversion object to convert text to the same encoding as is used by the Save()...
Definition: SimpleIni.h:663
virtual bool ConvertFromStore(const char *a_pInputData, size_t a_uInputDataLen, SI_CHAR *a_pOutputData, size_t a_uOutputDataSize)
Convert the input string from the storage format to SI_CHAR.
Definition: SimpleIni.h:1997
int nOrder
Definition: SimpleIni.h:117
Strict less ordering by name of key only.
Definition: SimpleIni.h:139
bool operator>(const CArray< T, N > &x, const CArray< T, N > &y)
Definition: CArray.h:289
void GetAllSections(TNamesDepend &a_names) const
Retrieve all section names.
Definition: SimpleIni.h:1583
SI_Error Load(std::istream &a_istream)
Load INI file data from an istream.
Definition: SimpleIni.h:991
bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const
Definition: SimpleIni.h:1934
bool empty() const
Definition: ts_hash_map.h:120
File error (see errno for detail error)
Definition: SimpleIni.h:66
std::string BASE_IMPEXP format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
GLsizei const GLchar ** string
Definition: glext.h:3919
An existing value was updated.
Definition: SimpleIni.h:60
SI_ConvertA & operator=(const SI_ConvertA &rhs)
Definition: SimpleIni.h:1956
Characterset conversion utility class to convert strings to the same format as is used for the storag...
Definition: SimpleIni.h:225
CSimpleIniTempl(bool a_bMultiKey=false, bool a_bMultiLine=false)
Default constructor.
Definition: SimpleIni.h:805
SI_ConvertA(const SI_ConvertA &rhs)
Definition: SimpleIni.h:1955
bool IsMultiLineData(const SI_CHAR *a_pData) const
Definition: SimpleIni.h:1181
SI_CHAR * m_pData
Copy of the INI file data in our character format.
Definition: SimpleIni.h:764
bool operator()(const SI_CHAR *pLeft, const SI_CHAR *pRight) const
Definition: SimpleIni.h:1911
key entry
Definition: SimpleIni.h:114
TSection m_data
Parsed INI data.
Definition: SimpleIni.h:776
virtual size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen) MRPT_OVERRIDE
Calculate the number of SI_CHAR required for converting the input from the storage format...
Definition: SimpleIni.h:2072
SI_Error SaveFile(const char *a_pszFile) const
Save an INI file from memory to disk.
Definition: SimpleIni.h:1624
bool Delete(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, bool a_bRemoveEmpty=false)
Delete an entire section, or a key from a section.
Definition: SimpleIni.h:1821
const SI_CHAR * GetValue(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, const SI_CHAR *a_pDefault=NULL, bool *a_pHasMultiple=NULL) const
Retrieve the value for a specific key.
Definition: SimpleIni.h:1463
Generic ASCII case-insensitive less than comparison.
Definition: SimpleIni.h:1930
bool IsNewLineChar(SI_CHAR a_c) const
Definition: SimpleIni.h:1218
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
bool ConvertToStore(const SI_CHAR *a_pInputData, char *a_pOutputData, size_t a_uOutputDataSize)
Convert the input string to the storage format of this data.
Definition: SimpleIni.h:2041
MRPT_IniFileParser(const MRPT_IniFileParser &rhs)
Definition: SimpleIni.h:2066
const SI_CHAR * pComment
Definition: SimpleIni.h:116
bool FindEntry(SI_CHAR *&a_pData, const SI_CHAR *&a_pSection, const SI_CHAR *&a_pKey, const SI_CHAR *&a_pVal, const SI_CHAR *&a_pComment) const
Parse the data looking for the next valid entry.
Definition: SimpleIni.h:1035
size_t m_uDataLen
Length of the data that we have stored.
Definition: SimpleIni.h:770
SI_Error FindFileComment(SI_CHAR *&a_pData, bool a_bCopyStrings)
Parse the data looking for a file comment and store it if found.
Definition: SimpleIni.h:1008
virtual void Write(const char *a_pBuf)=0
#define SEEK_SET
Definition: zconf.h:300
bool OutputMultiLineText(OutputWriter &a_oOutput, Converter &a_oConverter, const SI_CHAR *a_pText) const
Definition: SimpleIni.h:1792
A new value was inserted.
Definition: SimpleIni.h:61
const TKeyVal * GetSection(const SI_CHAR *a_pSection) const
Retrieve all key and value pairs for a section.
Definition: SimpleIni.h:1568
OutputWriter class to write the INI data to a file.
Definition: SimpleIni.h:182
Generic case-sensitive less than comparison.
Definition: SimpleIni.h:1910
std::list< Entry > TNamesDepend
set of dependent string pointers.
Definition: SimpleIni.h:166
bool GetAllValues(const SI_CHAR *a_pSection, const SI_CHAR *a_pKey, TNamesDepend &a_values) const
Retrieve all values for a specific key.
Definition: SimpleIni.h:1500
void Write(const char *a_pBuf) MRPT_OVERRIDE
Definition: SimpleIni.h:199
void parse_process_var_define(ParseContext &pc, const std::string &var_name, const std::string &var_value)
Definition: SimpleIni.h:2123
const SI_CHAR * m_pFileComment
File comment for this data, if one exists.
Definition: SimpleIni.h:773
StreamWriter & operator=(const StreamWriter &)
virtual size_t SizeFromStore(const char *a_pInputData, size_t a_uInputDataLen)
Calculate the number of SI_CHAR required for converting the input from the storage format...
Definition: SimpleIni.h:1973
Strict less ordering by order, and then name of key.
Definition: SimpleIni.h:147
std::string BASE_IMPEXP trim(const std::string &str)
Removes leading and trailing spaces.
void Reset()
Deallocate all memory stored by this object.
Definition: SimpleIni.h:825
MRPT_IniFileParser & operator=(const MRPT_IniFileParser &rhs)
Definition: SimpleIni.h:2067
typedef void(APIENTRYP PFNGLBLENDCOLORPROC)(GLclampf red
SI_Error Save(OutputWriter &a_oOutput) const
Save the INI data.
Definition: SimpleIni.h:1673
SI_Error CopyString(const SI_CHAR *&a_pString)
Make a copy of the supplied string, replacing the original pointer.
Definition: SimpleIni.h:1350
GLuint res
Definition: glext.h:6298
bool IsMultiLine() const
Query the status of multi-line data.
Definition: SimpleIni.h:312
std::string parse_process_var_eval(const ParseContext &pc, std::string expr)
Definition: SimpleIni.h:2099
#define SI_NEWLINE_A
Definition: SimpleIni.h:70
GLfloat GLfloat p
Definition: glext.h:5587
bool LoadMultiLineText(SI_CHAR *&a_pData, const SI_CHAR *&a_pVal, const SI_CHAR *a_pTagName, bool a_bAllowBlankLinesInComment=false) const
Definition: SimpleIni.h:1227
Out of memory error.
Definition: SimpleIni.h:65
bool IsComment(SI_CHAR ch) const
Does the supplied character start a comment line?
Definition: SimpleIni.h:720
OutputWriter class to write the INI data to a string.
Definition: SimpleIni.h:195
std::map< Entry, TKeyVal, typename Entry::KeyOrder > TSection
map sections to key/value map
Definition: SimpleIni.h:161
Entry(const Entry &rhs)
Definition: SimpleIni.h:124
bool IsLess(const SI_CHAR *a_pLeft, const SI_CHAR *a_pRight) const
Internal use of our string comparison function.
Definition: SimpleIni.h:737
StringWriter & operator=(const StringWriter &)



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019