SimpleIni.h 128 KB

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