SimpleIni.h 127 KB

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