├── .github └── workflows │ ├── build-and-test.yml │ ├── build-doxygen.yml │ └── codeql.yml ├── .gitignore ├── CMakeLists.txt ├── ConvertUTF.c ├── ConvertUTF.h ├── ConvertUTF_readme.txt ├── Doxyfile ├── LICENCE.txt ├── Makefile ├── README.md ├── SimpleIni.h ├── SimpleIniConfig.cmake.in ├── other ├── package.cmd └── simpleini.doxy ├── release.txt ├── tests ├── .gitignore ├── CMakeLists.txt ├── Makefile ├── example.ini ├── old │ ├── test.cmd │ ├── test1-expected.ini │ ├── test1-input.ini │ ├── test1.cpp │ ├── testsi-EUCJP.ini │ ├── testsi-SJIS.ini │ ├── testsi-UTF8.ini │ └── testsi.cpp ├── packages.config ├── pch.cpp ├── pch.h ├── tests.ini ├── tests.vcxproj ├── tests.vcxproj.filters ├── ts-bugfix.cpp ├── ts-noconvert.cpp ├── ts-quotes.cpp ├── ts-roundtrip.cpp ├── ts-snippets.cpp ├── ts-utf8.cpp └── ts-wchar.cpp └── vcproj ├── SimpleIni.sln ├── SimpleIni.vcxproj └── SimpleIni.vcxproj.filters /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | Windows: 11 | runs-on: windows-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: microsoft/setup-msbuild@v2 15 | - name: Build 16 | run: | 17 | nuget restore vcproj\SimpleIni.sln 18 | msbuild vcproj\SimpleIni.sln /p:Configuration=Release 19 | - name: Run tests 20 | run: | 21 | cd tests 22 | ..\vcproj\x64\Release\tests.exe 23 | 24 | 25 | Ubuntu: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - name: Install requirements 29 | run: sudo apt install libgtest-dev cmake 30 | - uses: actions/checkout@v3 31 | - run: make all && make test 32 | 33 | - name: test with CMake (-DSIMPLEINI_USE_SYSTEM_GTEST=OFF) 34 | run: | 35 | cmake . -B build -DSIMPLEINI_USE_SYSTEM_GTEST=OFF 36 | cmake --build build 37 | ctest --verbose --test-dir build 38 | - name: test with CMake (-DSIMPLEINI_USE_SYSTEM_GTEST=ON) 39 | run: | 40 | cmake . -B build-system-gtest -DSIMPLEINI_USE_SYSTEM_GTEST=ON 41 | cmake --build build-system-gtest 42 | ctest --verbose --test-dir build-system-gtest 43 | 44 | MacOS: 45 | runs-on: macos-latest 46 | steps: 47 | - name: Install requirements 48 | run: brew install googletest cmake 49 | - uses: actions/checkout@v3 50 | - run: make all && make test 51 | 52 | - name: test with CMake (-DSIMPLEINI_USE_SYSTEM_GTEST=OFF) 53 | run: | 54 | cmake . -B build -DSIMPLEINI_USE_SYSTEM_GTEST=OFF 55 | cmake --build build 56 | ctest --verbose --test-dir build 57 | - name: test with CMake (-DSIMPLEINI_USE_SYSTEM_GTEST=ON) 58 | run: | 59 | cmake . -B build-system-gtest -DSIMPLEINI_USE_SYSTEM_GTEST=ON 60 | cmake --build build-system-gtest 61 | ctest --verbose --test-dir build-system-gtest 62 | -------------------------------------------------------------------------------- /.github/workflows/build-doxygen.yml: -------------------------------------------------------------------------------- 1 | name: Build Doxygen Docs 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Build and Test"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | deploy: 11 | if: ${{ github.event.workflow_run.conclusion == 'success' }} 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | - name: Install Doxygen 17 | run: sudo apt-get update && sudo apt-get install -y doxygen graphviz 18 | - name: Generate Doxygen Documentation 19 | run: doxygen Doxyfile 20 | - name: Deploy to GitHub Pages 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./docs/html 25 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: workflow_dispatch 4 | #on: 5 | # push: 6 | # branches: [ "master" ] 7 | # pull_request: 8 | # branches: [ "master" ] 9 | 10 | jobs: 11 | analyze: 12 | name: Analyze 13 | runs-on: ubuntu-latest 14 | permissions: 15 | actions: read 16 | contents: read 17 | security-events: write 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | language: [ 'cpp' ] 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v3 27 | 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v2 30 | with: 31 | languages: ${{ matrix.language }} 32 | 33 | - name: Autobuild 34 | uses: github/codeql-action/autobuild@v2 35 | 36 | - name: Perform CodeQL Analysis 37 | uses: github/codeql-action/analyze@v2 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | docs 7 | 8 | 9 | # User-specific files 10 | *.rsuser 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Mono auto generated files 20 | mono_crash.* 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Ww][Ii][Nn]32/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | build/ 38 | build-system-gtest/ 39 | 40 | # Visual Studio 2015/2017 cache/options directory 41 | .vs/ 42 | # Uncomment if you have tasks that create the project's static files in wwwroot 43 | #wwwroot/ 44 | 45 | # Visual Studio 2017 auto generated files 46 | Generated\ Files/ 47 | 48 | # MSTest test Results 49 | [Tt]est[Rr]esult*/ 50 | [Bb]uild[Ll]og.* 51 | 52 | # NUnit 53 | *.VisualState.xml 54 | TestResult.xml 55 | nunit-*.xml 56 | 57 | # Build Results of an ATL Project 58 | [Dd]ebugPS/ 59 | [Rr]eleasePS/ 60 | dlldata.c 61 | 62 | # Benchmark Results 63 | BenchmarkDotNet.Artifacts/ 64 | 65 | # .NET Core 66 | project.lock.json 67 | project.fragment.lock.json 68 | artifacts/ 69 | 70 | # ASP.NET Scaffolding 71 | ScaffoldingReadMe.txt 72 | 73 | # StyleCop 74 | StyleCopReport.xml 75 | 76 | # Files built by Visual Studio 77 | *_i.c 78 | *_p.c 79 | *_h.h 80 | *.ilk 81 | *.meta 82 | *.obj 83 | *.iobj 84 | *.pch 85 | *.pdb 86 | *.ipdb 87 | *.pgc 88 | *.pgd 89 | *.rsp 90 | *.sbr 91 | *.tlb 92 | *.tli 93 | *.tlh 94 | *.tmp 95 | *.tmp_proj 96 | *_wpftmp.csproj 97 | *.log 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opendb 113 | *.opensdf 114 | *.sdf 115 | *.cachefile 116 | *.VC.db 117 | *.VC.VC.opendb 118 | 119 | # Visual Studio profiler 120 | *.psess 121 | *.vsp 122 | *.vspx 123 | *.sap 124 | 125 | # Visual Studio Trace Files 126 | *.e2e 127 | 128 | # TFS 2012 Local Workspace 129 | $tf/ 130 | 131 | # Guidance Automation Toolkit 132 | *.gpState 133 | 134 | # ReSharper is a .NET coding add-in 135 | _ReSharper*/ 136 | *.[Rr]e[Ss]harper 137 | *.DotSettings.user 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # DotCover is a Code Coverage Tool 143 | *.dotCover 144 | 145 | # AxoCover is a Code Coverage Tool 146 | .axoCover/* 147 | !.axoCover/settings.json 148 | 149 | # Coverlet is a free, cross platform Code Coverage Tool 150 | coverage*[.json, .xml, .info] 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project( 4 | SimpleIni 5 | VERSION 4.22 6 | DESCRIPTION "Cross-platform C++ library providing a simple API to read and write INI-style configuration files" 7 | LANGUAGES CXX 8 | ) 9 | 10 | option(SIMPLEINI_USE_SYSTEM_GTEST "Use system GoogleTest dependency" OFF) 11 | 12 | # disable in-source builds 13 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 14 | message(FATAL_ERROR "In-source builds are not allowed, use cmake -S . -B build.") 15 | endif() 16 | 17 | 18 | # Define library paths and include directories 19 | set(EXPORT_NAMESPACE "${PROJECT_NAME}::") 20 | set(HEADERS SimpleIni.h) 21 | 22 | add_library(${PROJECT_NAME} INTERFACE) 23 | add_library(${EXPORT_NAMESPACE}${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 24 | 25 | include(GNUInstallDirs) 26 | 27 | include(CMakePackageConfigHelpers) 28 | write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 29 | VERSION ${PROJECT_VERSION} 30 | COMPATIBILITY SameMajorVersion 31 | ) 32 | configure_package_config_file(${PROJECT_NAME}Config.cmake.in 33 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 34 | INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME} 35 | ) 36 | 37 | install(FILES SimpleIni.h 38 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 39 | ) 40 | 41 | install(TARGETS ${PROJECT_NAME} 42 | EXPORT ${PROJECT_NAME}Targets 43 | ) 44 | 45 | install(FILES 46 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 47 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 48 | DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME} 49 | ) 50 | install(EXPORT ${PROJECT_NAME}Targets 51 | DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME} 52 | NAMESPACE ${EXPORT_NAMESPACE} 53 | ) 54 | 55 | target_include_directories(${PROJECT_NAME} INTERFACE 56 | $ 57 | $ 58 | ) 59 | 60 | # only build tests when top level and testing enabled 61 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 62 | include(CTest) 63 | if(BUILD_TESTING) 64 | add_subdirectory(tests) 65 | endif() 66 | endif() 67 | -------------------------------------------------------------------------------- /ConvertUTF.c: -------------------------------------------------------------------------------- 1 | /* 2 | * https://web.archive.org/web/20090529064329/http://www.unicode.org:80/Public/PROGRAMS/CVTUTF/ 3 | * 4 | * Copyright 2001-2004 Unicode, Inc. 5 | * 6 | * Disclaimer 7 | * 8 | * This source code is provided as is by Unicode, Inc. No claims are 9 | * made as to fitness for any particular purpose. No warranties of any 10 | * kind are expressed or implied. The recipient agrees to determine 11 | * applicability of information provided. If this file has been 12 | * purchased on magnetic or optical media from Unicode, Inc., the 13 | * sole remedy for any claim will be exchange of defective media 14 | * within 90 days of receipt. 15 | * 16 | * Limitations on Rights to Redistribute This Code 17 | * 18 | * Unicode, Inc. hereby grants the right to freely use the information 19 | * supplied in this file in the creation of products supporting the 20 | * Unicode Standard, and to make copies of this file in any form 21 | * for internal or external distribution as long as this notice 22 | * remains attached. 23 | */ 24 | 25 | /* --------------------------------------------------------------------- 26 | 27 | Conversions between UTF32, UTF-16, and UTF-8. Source code file. 28 | Author: Mark E. Davis, 1994. 29 | Rev History: Rick McGowan, fixes & updates May 2001. 30 | Sept 2001: fixed const & error conditions per 31 | mods suggested by S. Parent & A. Lillich. 32 | June 2002: Tim Dodd added detection and handling of incomplete 33 | source sequences, enhanced error detection, added casts 34 | to eliminate compiler warnings. 35 | July 2003: slight mods to back out aggressive FFFE detection. 36 | Jan 2004: updated switches in from-UTF8 conversions. 37 | Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. 38 | 39 | See the header file "ConvertUTF.h" for complete documentation. 40 | 41 | ------------------------------------------------------------------------ */ 42 | 43 | 44 | #include "ConvertUTF.h" 45 | #ifdef CVTUTF_DEBUG 46 | #include 47 | #endif 48 | 49 | static const int halfShift = 10; /* used for shifting by 10 bits */ 50 | 51 | static const UTF32 halfBase = 0x0010000UL; 52 | static const UTF32 halfMask = 0x3FFUL; 53 | 54 | #define UNI_SUR_HIGH_START (UTF32)0xD800 55 | #define UNI_SUR_HIGH_END (UTF32)0xDBFF 56 | #define UNI_SUR_LOW_START (UTF32)0xDC00 57 | #define UNI_SUR_LOW_END (UTF32)0xDFFF 58 | #define false 0 59 | #define true 1 60 | 61 | /* --------------------------------------------------------------------- */ 62 | 63 | ConversionResult ConvertUTF32toUTF16( 64 | const UTF32** sourceStart, const UTF32* sourceEnd, 65 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { 66 | ConversionResult result = conversionOK; 67 | const UTF32* source = *sourceStart; 68 | UTF16* target = *targetStart; 69 | while (source < sourceEnd) { 70 | UTF32 ch; 71 | if (target >= targetEnd) { 72 | result = targetExhausted; break; 73 | } 74 | ch = *source++; 75 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 76 | /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ 77 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 78 | if (flags == strictConversion) { 79 | --source; /* return to the illegal value itself */ 80 | result = sourceIllegal; 81 | break; 82 | } 83 | else { 84 | *target++ = UNI_REPLACEMENT_CHAR; 85 | } 86 | } 87 | else { 88 | *target++ = (UTF16)ch; /* normal case */ 89 | } 90 | } 91 | else if (ch > UNI_MAX_LEGAL_UTF32) { 92 | if (flags == strictConversion) { 93 | result = sourceIllegal; 94 | } 95 | else { 96 | *target++ = UNI_REPLACEMENT_CHAR; 97 | } 98 | } 99 | else { 100 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 101 | if (target + 1 >= targetEnd) { 102 | --source; /* Back up source pointer! */ 103 | result = targetExhausted; break; 104 | } 105 | ch -= halfBase; 106 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 107 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 108 | } 109 | } 110 | *sourceStart = source; 111 | *targetStart = target; 112 | return result; 113 | } 114 | 115 | /* --------------------------------------------------------------------- */ 116 | 117 | ConversionResult ConvertUTF16toUTF32( 118 | const UTF16** sourceStart, const UTF16* sourceEnd, 119 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 120 | ConversionResult result = conversionOK; 121 | const UTF16* source = *sourceStart; 122 | UTF32* target = *targetStart; 123 | UTF32 ch, ch2; 124 | while (source < sourceEnd) { 125 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 126 | ch = *source++; 127 | /* If we have a surrogate pair, convert to UTF32 first. */ 128 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 129 | /* If the 16 bits following the high surrogate are in the source buffer... */ 130 | if (source < sourceEnd) { 131 | ch2 = *source; 132 | /* If it's a low surrogate, convert to UTF32. */ 133 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 134 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 135 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 136 | ++source; 137 | } 138 | else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 139 | --source; /* return to the illegal value itself */ 140 | result = sourceIllegal; 141 | break; 142 | } 143 | } 144 | else { /* We don't have the 16 bits following the high surrogate. */ 145 | --source; /* return to the high surrogate */ 146 | result = sourceExhausted; 147 | break; 148 | } 149 | } 150 | else if (flags == strictConversion) { 151 | /* UTF-16 surrogate values are illegal in UTF-32 */ 152 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 153 | --source; /* return to the illegal value itself */ 154 | result = sourceIllegal; 155 | break; 156 | } 157 | } 158 | if (target >= targetEnd) { 159 | source = oldSource; /* Back up source pointer! */ 160 | result = targetExhausted; break; 161 | } 162 | *target++ = ch; 163 | } 164 | *sourceStart = source; 165 | *targetStart = target; 166 | #ifdef CVTUTF_DEBUG 167 | if (result == sourceIllegal) { 168 | fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); 169 | fflush(stderr); 170 | } 171 | #endif 172 | return result; 173 | } 174 | 175 | /* --------------------------------------------------------------------- */ 176 | 177 | /* 178 | * Index into the table below with the first byte of a UTF-8 sequence to 179 | * get the number of trailing bytes that are supposed to follow it. 180 | * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is 181 | * left as-is for anyone who may want to do such conversion, which was 182 | * allowed in earlier algorithms. 183 | */ 184 | static const char trailingBytesForUTF8[256] = { 185 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 186 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 187 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 188 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 189 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 190 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 191 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 192 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 193 | }; 194 | 195 | /* 196 | * Magic values subtracted from a buffer value during UTF8 conversion. 197 | * This table contains as many values as there might be trailing bytes 198 | * in a UTF-8 sequence. 199 | */ 200 | static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 201 | 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; 202 | 203 | /* 204 | * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed 205 | * into the first byte, depending on how many bytes follow. There are 206 | * as many entries in this table as there are UTF-8 sequence types. 207 | * (I.e., one byte sequence, two byte... etc.). Remember that sequencs 208 | * for *legal* UTF-8 will be 4 or fewer bytes total. 209 | */ 210 | static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 211 | 212 | /* --------------------------------------------------------------------- */ 213 | 214 | /* The interface converts a whole buffer to avoid function-call overhead. 215 | * Constants have been gathered. Loops & conditionals have been removed as 216 | * much as possible for efficiency, in favor of drop-through switches. 217 | * (See "Note A" at the bottom of the file for equivalent code.) 218 | * If your compiler supports it, the "isLegalUTF8" call can be turned 219 | * into an inline function. 220 | */ 221 | 222 | /* --------------------------------------------------------------------- */ 223 | 224 | ConversionResult ConvertUTF16toUTF8( 225 | const UTF16** sourceStart, const UTF16* sourceEnd, 226 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 227 | ConversionResult result = conversionOK; 228 | const UTF16* source = *sourceStart; 229 | UTF8* target = *targetStart; 230 | while (source < sourceEnd) { 231 | UTF32 ch; 232 | unsigned short bytesToWrite = 0; 233 | const UTF32 byteMask = 0xBF; 234 | const UTF32 byteMark = 0x80; 235 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 236 | ch = *source++; 237 | /* If we have a surrogate pair, convert to UTF32 first. */ 238 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 239 | /* If the 16 bits following the high surrogate are in the source buffer... */ 240 | if (source < sourceEnd) { 241 | UTF32 ch2 = *source; 242 | /* If it's a low surrogate, convert to UTF32. */ 243 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 244 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 245 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 246 | ++source; 247 | } 248 | else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 249 | --source; /* return to the illegal value itself */ 250 | result = sourceIllegal; 251 | break; 252 | } 253 | } 254 | else { /* We don't have the 16 bits following the high surrogate. */ 255 | --source; /* return to the high surrogate */ 256 | result = sourceExhausted; 257 | break; 258 | } 259 | } 260 | else if (flags == strictConversion) { 261 | /* UTF-16 surrogate values are illegal in UTF-32 */ 262 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 263 | --source; /* return to the illegal value itself */ 264 | result = sourceIllegal; 265 | break; 266 | } 267 | } 268 | /* Figure out how many bytes the result will require */ 269 | if (ch < (UTF32)0x80) { 270 | bytesToWrite = 1; 271 | } 272 | else if (ch < (UTF32)0x800) { 273 | bytesToWrite = 2; 274 | } 275 | else if (ch < (UTF32)0x10000) { 276 | bytesToWrite = 3; 277 | } 278 | else if (ch < (UTF32)0x110000) { 279 | bytesToWrite = 4; 280 | } 281 | else { 282 | bytesToWrite = 3; 283 | ch = UNI_REPLACEMENT_CHAR; 284 | } 285 | 286 | target += bytesToWrite; 287 | if (target > targetEnd) { 288 | source = oldSource; /* Back up source pointer! */ 289 | target -= bytesToWrite; result = targetExhausted; break; 290 | } 291 | switch (bytesToWrite) { /* note: everything falls through. */ 292 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 293 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 294 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 295 | case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); 296 | } 297 | target += bytesToWrite; 298 | } 299 | *sourceStart = source; 300 | *targetStart = target; 301 | return result; 302 | } 303 | 304 | /* --------------------------------------------------------------------- */ 305 | 306 | /* 307 | * Utility routine to tell whether a sequence of bytes is legal UTF-8. 308 | * This must be called with the length pre-determined by the first byte. 309 | * If not calling this from ConvertUTF8to*, then the length can be set by: 310 | * length = trailingBytesForUTF8[*source]+1; 311 | * and the sequence is illegal right away if there aren't that many bytes 312 | * available. 313 | * If presented with a length > 4, this returns false. The Unicode 314 | * definition of UTF-8 goes up to 4-byte sequences. 315 | */ 316 | 317 | static Boolean isLegalUTF8(const UTF8* source, int length) { 318 | UTF8 a; 319 | const UTF8* srcptr = source + length; 320 | switch (length) { 321 | default: return false; 322 | /* Everything else falls through when "true"... */ 323 | case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 324 | case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 325 | case 2: if ((a = (*--srcptr)) > 0xBF) return false; 326 | 327 | switch (*source) { 328 | /* no fall-through in this inner switch */ 329 | case 0xE0: if (a < 0xA0) return false; break; 330 | case 0xED: if (a > 0x9F) return false; break; 331 | case 0xF0: if (a < 0x90) return false; break; 332 | case 0xF4: if (a > 0x8F) return false; break; 333 | default: if (a < 0x80) return false; 334 | } 335 | 336 | case 1: if (*source >= 0x80 && *source < 0xC2) return false; 337 | } 338 | if (*source > 0xF4) return false; 339 | return true; 340 | } 341 | 342 | /* --------------------------------------------------------------------- */ 343 | 344 | /* 345 | * Exported function to return whether a UTF-8 sequence is legal or not. 346 | * This is not used here; it's just exported. 347 | */ 348 | Boolean isLegalUTF8Sequence(const UTF8* source, const UTF8* sourceEnd) { 349 | int length = trailingBytesForUTF8[*source] + 1; 350 | if (source + length > sourceEnd) { 351 | return false; 352 | } 353 | return isLegalUTF8(source, length); 354 | } 355 | 356 | /* --------------------------------------------------------------------- */ 357 | 358 | ConversionResult ConvertUTF8toUTF16( 359 | const UTF8** sourceStart, const UTF8* sourceEnd, 360 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { 361 | ConversionResult result = conversionOK; 362 | const UTF8* source = *sourceStart; 363 | UTF16* target = *targetStart; 364 | while (source < sourceEnd) { 365 | UTF32 ch = 0; 366 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 367 | if (source + extraBytesToRead >= sourceEnd) { 368 | result = sourceExhausted; break; 369 | } 370 | /* Do this check whether lenient or strict */ 371 | if (!isLegalUTF8(source, extraBytesToRead + 1)) { 372 | result = sourceIllegal; 373 | break; 374 | } 375 | /* 376 | * The cases all fall through. See "Note A" below. 377 | */ 378 | switch (extraBytesToRead) { 379 | case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 380 | case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 381 | case 3: ch += *source++; ch <<= 6; 382 | case 2: ch += *source++; ch <<= 6; 383 | case 1: ch += *source++; ch <<= 6; 384 | case 0: ch += *source++; 385 | } 386 | ch -= offsetsFromUTF8[extraBytesToRead]; 387 | 388 | if (target >= targetEnd) { 389 | source -= (extraBytesToRead + 1); /* Back up source pointer! */ 390 | result = targetExhausted; break; 391 | } 392 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 393 | /* UTF-16 surrogate values are illegal in UTF-32 */ 394 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 395 | if (flags == strictConversion) { 396 | source -= (extraBytesToRead + 1); /* return to the illegal value itself */ 397 | result = sourceIllegal; 398 | break; 399 | } 400 | else { 401 | *target++ = UNI_REPLACEMENT_CHAR; 402 | } 403 | } 404 | else { 405 | *target++ = (UTF16)ch; /* normal case */ 406 | } 407 | } 408 | else if (ch > UNI_MAX_UTF16) { 409 | if (flags == strictConversion) { 410 | result = sourceIllegal; 411 | source -= (extraBytesToRead + 1); /* return to the start */ 412 | break; /* Bail out; shouldn't continue */ 413 | } 414 | else { 415 | *target++ = UNI_REPLACEMENT_CHAR; 416 | } 417 | } 418 | else { 419 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 420 | if (target + 1 >= targetEnd) { 421 | source -= (extraBytesToRead + 1); /* Back up source pointer! */ 422 | result = targetExhausted; break; 423 | } 424 | ch -= halfBase; 425 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 426 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 427 | } 428 | } 429 | *sourceStart = source; 430 | *targetStart = target; 431 | return result; 432 | } 433 | 434 | /* --------------------------------------------------------------------- */ 435 | 436 | ConversionResult ConvertUTF32toUTF8( 437 | const UTF32** sourceStart, const UTF32* sourceEnd, 438 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 439 | ConversionResult result = conversionOK; 440 | const UTF32* source = *sourceStart; 441 | UTF8* target = *targetStart; 442 | while (source < sourceEnd) { 443 | UTF32 ch; 444 | unsigned short bytesToWrite = 0; 445 | const UTF32 byteMask = 0xBF; 446 | const UTF32 byteMark = 0x80; 447 | ch = *source++; 448 | if (flags == strictConversion) { 449 | /* UTF-16 surrogate values are illegal in UTF-32 */ 450 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 451 | --source; /* return to the illegal value itself */ 452 | result = sourceIllegal; 453 | break; 454 | } 455 | } 456 | /* 457 | * Figure out how many bytes the result will require. Turn any 458 | * illegally large UTF32 things (> Plane 17) into replacement chars. 459 | */ 460 | if (ch < (UTF32)0x80) { 461 | bytesToWrite = 1; 462 | } 463 | else if (ch < (UTF32)0x800) { 464 | bytesToWrite = 2; 465 | } 466 | else if (ch < (UTF32)0x10000) { 467 | bytesToWrite = 3; 468 | } 469 | else if (ch <= UNI_MAX_LEGAL_UTF32) { 470 | bytesToWrite = 4; 471 | } 472 | else { 473 | bytesToWrite = 3; 474 | ch = UNI_REPLACEMENT_CHAR; 475 | result = sourceIllegal; 476 | } 477 | 478 | target += bytesToWrite; 479 | if (target > targetEnd) { 480 | --source; /* Back up source pointer! */ 481 | target -= bytesToWrite; result = targetExhausted; break; 482 | } 483 | switch (bytesToWrite) { /* note: everything falls through. */ 484 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 485 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 486 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 487 | case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); 488 | } 489 | target += bytesToWrite; 490 | } 491 | *sourceStart = source; 492 | *targetStart = target; 493 | return result; 494 | } 495 | 496 | /* --------------------------------------------------------------------- */ 497 | 498 | ConversionResult ConvertUTF8toUTF32( 499 | const UTF8** sourceStart, const UTF8* sourceEnd, 500 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 501 | ConversionResult result = conversionOK; 502 | const UTF8* source = *sourceStart; 503 | UTF32* target = *targetStart; 504 | while (source < sourceEnd) { 505 | UTF32 ch = 0; 506 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 507 | if (source + extraBytesToRead >= sourceEnd) { 508 | result = sourceExhausted; break; 509 | } 510 | /* Do this check whether lenient or strict */ 511 | if (!isLegalUTF8(source, extraBytesToRead + 1)) { 512 | result = sourceIllegal; 513 | break; 514 | } 515 | /* 516 | * The cases all fall through. See "Note A" below. 517 | */ 518 | switch (extraBytesToRead) { 519 | case 5: ch += *source++; ch <<= 6; 520 | case 4: ch += *source++; ch <<= 6; 521 | case 3: ch += *source++; ch <<= 6; 522 | case 2: ch += *source++; ch <<= 6; 523 | case 1: ch += *source++; ch <<= 6; 524 | case 0: ch += *source++; 525 | } 526 | ch -= offsetsFromUTF8[extraBytesToRead]; 527 | 528 | if (target >= targetEnd) { 529 | source -= (extraBytesToRead + 1); /* Back up the source pointer! */ 530 | result = targetExhausted; break; 531 | } 532 | if (ch <= UNI_MAX_LEGAL_UTF32) { 533 | /* 534 | * UTF-16 surrogate values are illegal in UTF-32, and anything 535 | * over Plane 17 (> 0x10FFFF) is illegal. 536 | */ 537 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 538 | if (flags == strictConversion) { 539 | source -= (extraBytesToRead + 1); /* return to the illegal value itself */ 540 | result = sourceIllegal; 541 | break; 542 | } 543 | else { 544 | *target++ = UNI_REPLACEMENT_CHAR; 545 | } 546 | } 547 | else { 548 | *target++ = ch; 549 | } 550 | } 551 | else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ 552 | result = sourceIllegal; 553 | *target++ = UNI_REPLACEMENT_CHAR; 554 | } 555 | } 556 | *sourceStart = source; 557 | *targetStart = target; 558 | return result; 559 | } 560 | 561 | /* --------------------------------------------------------------------- 562 | 563 | Note A. 564 | The fall-through switches in UTF-8 reading code save a 565 | temp variable, some decrements & conditionals. The switches 566 | are equivalent to the following loop: 567 | { 568 | int tmpBytesToRead = extraBytesToRead+1; 569 | do { 570 | ch += *source++; 571 | --tmpBytesToRead; 572 | if (tmpBytesToRead) ch <<= 6; 573 | } while (tmpBytesToRead > 0); 574 | } 575 | In UTF-8 writing code, the switches on "bytesToWrite" are 576 | similarly unrolled loops. 577 | 578 | --------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /ConvertUTF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * https://web.archive.org/web/20090529064329/http://www.unicode.org:80/Public/PROGRAMS/CVTUTF/ 3 | * 4 | * Copyright 2001-2004 Unicode, Inc. 5 | * 6 | * Disclaimer 7 | * 8 | * This source code is provided as is by Unicode, Inc. No claims are 9 | * made as to fitness for any particular purpose. No warranties of any 10 | * kind are expressed or implied. The recipient agrees to determine 11 | * applicability of information provided. If this file has been 12 | * purchased on magnetic or optical media from Unicode, Inc., the 13 | * sole remedy for any claim will be exchange of defective media 14 | * within 90 days of receipt. 15 | * 16 | * Limitations on Rights to Redistribute This Code 17 | * 18 | * Unicode, Inc. hereby grants the right to freely use the information 19 | * supplied in this file in the creation of products supporting the 20 | * Unicode Standard, and to make copies of this file in any form 21 | * for internal or external distribution as long as this notice 22 | * remains attached. 23 | */ 24 | 25 | /* --------------------------------------------------------------------- 26 | 27 | Conversions between UTF32, UTF-16, and UTF-8. Header file. 28 | 29 | Several funtions are included here, forming a complete set of 30 | conversions between the three formats. UTF-7 is not included 31 | here, but is handled in a separate source file. 32 | 33 | Each of these routines takes pointers to input buffers and output 34 | buffers. The input buffers are const. 35 | 36 | Each routine converts the text between *sourceStart and sourceEnd, 37 | putting the result into the buffer between *targetStart and 38 | targetEnd. Note: the end pointers are *after* the last item: e.g. 39 | *(sourceEnd - 1) is the last item. 40 | 41 | The return result indicates whether the conversion was successful, 42 | and if not, whether the problem was in the source or target buffers. 43 | (Only the first encountered problem is indicated.) 44 | 45 | After the conversion, *sourceStart and *targetStart are both 46 | updated to point to the end of last text successfully converted in 47 | the respective buffers. 48 | 49 | Input parameters: 50 | sourceStart - pointer to a pointer to the source buffer. 51 | The contents of this are modified on return so that 52 | it points at the next thing to be converted. 53 | targetStart - similarly, pointer to pointer to the target buffer. 54 | sourceEnd, targetEnd - respectively pointers to the ends of the 55 | two buffers, for overflow checking only. 56 | 57 | These conversion functions take a ConversionFlags argument. When this 58 | flag is set to strict, both irregular sequences and isolated surrogates 59 | will cause an error. When the flag is set to lenient, both irregular 60 | sequences and isolated surrogates are converted. 61 | 62 | Whether the flag is strict or lenient, all illegal sequences will cause 63 | an error return. This includes sequences such as: , , 64 | or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code 65 | must check for illegal sequences. 66 | 67 | When the flag is set to lenient, characters over 0x10FFFF are converted 68 | to the replacement character; otherwise (when the flag is set to strict) 69 | they constitute an error. 70 | 71 | Output parameters: 72 | The value "sourceIllegal" is returned from some routines if the input 73 | sequence is malformed. When "sourceIllegal" is returned, the source 74 | value will point to the illegal value that caused the problem. E.g., 75 | in UTF-8 when a sequence is malformed, it points to the start of the 76 | malformed sequence. 77 | 78 | Author: Mark E. Davis, 1994. 79 | Rev History: Rick McGowan, fixes & updates May 2001. 80 | Fixes & updates, Sept 2001. 81 | 82 | ------------------------------------------------------------------------ */ 83 | 84 | /* --------------------------------------------------------------------- 85 | The following 4 definitions are compiler-specific. 86 | The C standard does not guarantee that wchar_t has at least 87 | 16 bits, so wchar_t is no less portable than unsigned short! 88 | All should be unsigned values to avoid sign extension during 89 | bit mask & shift operations. 90 | ------------------------------------------------------------------------ */ 91 | 92 | typedef unsigned long UTF32; /* at least 32 bits */ 93 | typedef unsigned short UTF16; /* at least 16 bits */ 94 | typedef unsigned char UTF8; /* typically 8 bits */ 95 | typedef unsigned char Boolean; /* 0 or 1 */ 96 | 97 | /* Some fundamental constants */ 98 | #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD 99 | #define UNI_MAX_BMP (UTF32)0x0000FFFF 100 | #define UNI_MAX_UTF16 (UTF32)0x0010FFFF 101 | #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF 102 | #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF 103 | 104 | typedef enum { 105 | conversionOK, /* conversion successful */ 106 | sourceExhausted, /* partial character in source, but hit end */ 107 | targetExhausted, /* insuff. room in target for conversion */ 108 | sourceIllegal /* source sequence is illegal/malformed */ 109 | } ConversionResult; 110 | 111 | typedef enum { 112 | strictConversion = 0, 113 | lenientConversion 114 | } ConversionFlags; 115 | 116 | /* This is for C++ and does no harm in C */ 117 | #ifdef __cplusplus 118 | extern "C" { 119 | #endif 120 | 121 | ConversionResult ConvertUTF8toUTF16( 122 | const UTF8** sourceStart, const UTF8* sourceEnd, 123 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); 124 | 125 | ConversionResult ConvertUTF16toUTF8( 126 | const UTF16** sourceStart, const UTF16* sourceEnd, 127 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 128 | 129 | ConversionResult ConvertUTF8toUTF32( 130 | const UTF8** sourceStart, const UTF8* sourceEnd, 131 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 132 | 133 | ConversionResult ConvertUTF32toUTF8( 134 | const UTF32** sourceStart, const UTF32* sourceEnd, 135 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 136 | 137 | ConversionResult ConvertUTF16toUTF32( 138 | const UTF16** sourceStart, const UTF16* sourceEnd, 139 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 140 | 141 | ConversionResult ConvertUTF32toUTF16( 142 | const UTF32** sourceStart, const UTF32* sourceEnd, 143 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); 144 | 145 | Boolean isLegalUTF8Sequence(const UTF8* source, const UTF8* sourceEnd); 146 | 147 | #ifdef __cplusplus 148 | } 149 | #endif 150 | 151 | /* --------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /ConvertUTF_readme.txt: -------------------------------------------------------------------------------- 1 | https://web.archive.org/web/20090529064329/http://www.unicode.org:80/Public/PROGRAMS/CVTUTF/ 2 | 3 | The accompanying C source code file "ConvertUTF.c" and the associated header 4 | file "ConvertUTF.h" provide for conversion between various transformation 5 | formats of Unicode characters. The following conversions are supported: 6 | 7 | UTF-32 to UTF-16 8 | UTF-32 to UTF-8 9 | UTF-16 to UTF-32 10 | UTF-16 to UTF-8 11 | UTF-8 to UTF-16 12 | UTF-8 to UTF-32 13 | 14 | In addition, there is a test harness which runs various tests. 15 | 16 | The files "CVTUTF7.C" and "CVTUTF7.H" are for archival and historical purposes 17 | only. They have not been updated to Unicode 3.0 or later and should be 18 | considered obsolescent. "CVTUTF7.C" contains two functions that can convert 19 | between UCS2 (i.e., the BMP characters only) and UTF-7. Surrogates are 20 | not supported, the code has not been tested, and should be considered 21 | unsuitable for general purpose use. 22 | 23 | Please submit any bug reports about these programs here: 24 | 25 | http://www.unicode.org/unicode/reporting.html 26 | 27 | Version 1.0: initial version. 28 | 29 | Version 1.1: corrected some minor problems; added stricter checks. 30 | 31 | Version 1.2: corrected switch statements associated with "extraBytesToRead" 32 | in 4 & 5 byte cases, in functions for conversion from UTF8. 33 | Note: formally, the 4 & 5 byte cases are illegal in the latest 34 | UTF8, but the table and this code has always catered for those, 35 | cases since at one time they were legal. 36 | 37 | Version 1.3: Updated UTF-8 legality check; 38 | updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions 39 | Updated UTF-8 legality tests in harness.c 40 | 41 | 42 | Last update: October 19, 2004 43 | 44 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2006-2024 Brodie Thiesfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This makefile is just to build the automatic test harness 2 | # To use SimpleIni, just include SimpleIni.h header file 3 | 4 | PREFIX?= /usr/local 5 | 6 | TOPTARGETS := all clean test 7 | 8 | SUBDIRS := tests 9 | 10 | $(TOPTARGETS): $(SUBDIRS) 11 | $(SUBDIRS): 12 | $(MAKE) -C $@ $(MAKECMDGOALS) 13 | 14 | .PHONY: $(TOPTARGETS) $(SUBDIRS) 15 | 16 | install: 17 | mkdir -p $(DESTDIR)$(PREFIX)/include/ 18 | install -C -m 644 SimpleIni.h $(DESTDIR)$(PREFIX)/include/ 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simpleini 2 | ========= 3 | 4 | ![Latest Test Results](https://github.com/brofield/simpleini/actions/workflows/build-and-test.yml/badge.svg) 5 | 6 | A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS and Unicode. It is designed explicitly to be portable to any platform and has been tested on Windows, WinCE and Linux. Released as open-source and free using the MIT licence. 7 | 8 | [Full documentation](https://brofield.github.io/simpleini/) 9 | 10 | # Feature Summary 11 | 12 | - MIT Licence allows free use in all software (including GPL and commercial) 13 | - multi-platform: Windows (from 95 to 11, CE), Linux, MacOS 14 | - loading and saving of INI-style configuration files 15 | - configuration files can have any newline format on all platforms 16 | - liberal acceptance of file format 17 | * key/values with no section, keys with no value 18 | * removal of whitespace around sections, keys and values 19 | - support for multi-line values (values with embedded newline characters) 20 | - optional support for multiple keys with the same name 21 | - optional case-insensitive sections and keys (for ASCII characters only) 22 | - saves files with sections and keys in the same order as they were loaded 23 | - preserves comments on the file, section and keys where possible 24 | - supports both char or wchar_t programming interfaces 25 | - supports both MBCS (system locale) and UTF-8 file encodings 26 | - supports ICU as conversion library on all platforms 27 | - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file 28 | - support for non-ASCII characters in section, keys, values and comments 29 | - support for non-standard character types or file encodings via user-written converter classes 30 | - support for adding/modifying values programmatically 31 | - should compile with no warnings in most compilers 32 | 33 | # Documentation 34 | 35 | Full documentation of the interface is available in doxygen format. See [latest documentation here](https://brofield.github.io/simpleini/). 36 | 37 | # Examples 38 | 39 | These snippets are included with the distribution in the automatic tests as ts-snippets.cpp. 40 | 41 | ### SIMPLE USAGE 42 | 43 | ```c++ 44 | // simple demonstration 45 | 46 | CSimpleIniA ini; 47 | ini.SetUnicode(); 48 | 49 | SI_Error rc = ini.LoadFile("example.ini"); 50 | if (rc < 0) { /* handle error */ }; 51 | ASSERT_EQ(rc, SI_OK); 52 | 53 | const char* pv; 54 | pv = ini.GetValue("section", "key", "default"); 55 | ASSERT_STREQ(pv, "value"); 56 | 57 | ini.SetValue("section", "key", "newvalue"); 58 | 59 | pv = ini.GetValue("section", "key", "default"); 60 | ASSERT_STREQ(pv, "newvalue"); 61 | ``` 62 | 63 | ### LOADING DATA 64 | 65 | ```c++ 66 | // load from a data file 67 | CSimpleIniA ini; 68 | SI_Error rc = ini.LoadFile("example.ini"); 69 | if (rc < 0) { /* handle error */ }; 70 | ASSERT_EQ(rc, SI_OK); 71 | 72 | // load from a string 73 | const std::string example = "[section]\nkey = value\n"; 74 | CSimpleIniA ini; 75 | SI_Error rc = ini.LoadData(example); 76 | if (rc < 0) { /* handle error */ }; 77 | ASSERT_EQ(rc, SI_OK); 78 | ``` 79 | 80 | ### GETTING SECTIONS AND KEYS 81 | 82 | ```c++ 83 | // get all sections 84 | CSimpleIniA::TNamesDepend sections; 85 | ini.GetAllSections(sections); 86 | 87 | // get all keys in a section 88 | CSimpleIniA::TNamesDepend keys; 89 | ini.GetAllKeys("section1", keys); 90 | ``` 91 | 92 | ### GETTING VALUES 93 | 94 | ```c++ 95 | // get the value of a key that doesn't exist 96 | const char* pv; 97 | pv = ini.GetValue("section1", "key99"); 98 | ASSERT_EQ(pv, nullptr); 99 | 100 | // get the value of a key that does exist 101 | pv = ini.GetValue("section1", "key1"); 102 | ASSERT_STREQ(pv, "value1"); 103 | 104 | // get the value of a key which may have multiple 105 | // values. If hasMultiple is true, then there are 106 | // multiple values and just one value has been returned 107 | bool hasMulti; 108 | pv = ini.GetValue("section1", "key1", nullptr, &hasMulti); 109 | ASSERT_STREQ(pv, "value1"); 110 | ASSERT_EQ(hasMulti, false); 111 | 112 | pv = ini.GetValue("section1", "key2", nullptr, &hasMulti); 113 | ASSERT_STREQ(pv, "value2.1"); 114 | ASSERT_EQ(hasMulti, true); 115 | 116 | // get all values of a key with multiple values 117 | CSimpleIniA::TNamesDepend values; 118 | ini.GetAllValues("section1", "key2", values); 119 | 120 | // sort the values into a known order, in this case we want 121 | // the original load order 122 | values.sort(CSimpleIniA::Entry::LoadOrder()); 123 | 124 | // output all of the items 125 | CSimpleIniA::TNamesDepend::const_iterator it; 126 | for (it = values.begin(); it != values.end(); ++it) { 127 | printf("value = '%s'\n", it->pItem); 128 | } 129 | ``` 130 | 131 | ### MODIFYING DATA 132 | 133 | ```c++ 134 | // add a new section 135 | rc = ini.SetValue("section1", nullptr, nullptr); 136 | if (rc < 0) { /* handle error */ }; 137 | ASSERT_EQ(rc, SI_INSERTED); 138 | 139 | // not an error to add one that already exists 140 | rc = ini.SetValue("section1", nullptr, nullptr); 141 | if (rc < 0) { /* handle error */ }; 142 | ASSERT_EQ(rc, SI_UPDATED); 143 | 144 | // get the value of a key that doesn't exist 145 | const char* pv; 146 | pv = ini.GetValue("section2", "key1", "default-value"); 147 | ASSERT_STREQ(pv, "default-value"); 148 | 149 | // adding a key (the section will be added if needed) 150 | rc = ini.SetValue("section2", "key1", "value1"); 151 | if (rc < 0) { /* handle error */ }; 152 | ASSERT_EQ(rc, SI_INSERTED); 153 | 154 | // ensure it is set to expected value 155 | pv = ini.GetValue("section2", "key1", nullptr); 156 | ASSERT_STREQ(pv, "value1"); 157 | 158 | // change the value of a key 159 | rc = ini.SetValue("section2", "key1", "value2"); 160 | if (rc < 0) { /* handle error */ }; 161 | ASSERT_EQ(rc, SI_UPDATED); 162 | 163 | // ensure it is set to expected value 164 | pv = ini.GetValue("section2", "key1", nullptr); 165 | ASSERT_STREQ(pv, "value2"); 166 | ``` 167 | 168 | ### DELETING DATA 169 | 170 | ```c++ 171 | // deleting a key from a section. Optionally the entire 172 | // section may be deleted if it is now empty. 173 | bool done, deleteSectionIfEmpty = true; 174 | done = ini.Delete("section1", "key1", deleteSectionIfEmpty); 175 | ASSERT_EQ(done, true); 176 | done = ini.Delete("section1", "key1"); 177 | ASSERT_EQ(done, false); 178 | 179 | // deleting an entire section and all keys in it 180 | done = ini.Delete("section2", nullptr); 181 | ASSERT_EQ(done, true); 182 | done = ini.Delete("section2", nullptr); 183 | ASSERT_EQ(done, false); 184 | ``` 185 | 186 | ### SAVING DATA 187 | 188 | ```c++ 189 | // save the data to a string 190 | std::string data; 191 | rc = ini.Save(data); 192 | if (rc < 0) { /* handle error */ }; 193 | ASSERT_EQ(rc, SI_OK); 194 | 195 | // save the data back to the file 196 | rc = ini.SaveFile("example2.ini"); 197 | if (rc < 0) { /* handle error */ }; 198 | ASSERT_EQ(rc, SI_OK); 199 | ``` 200 | -------------------------------------------------------------------------------- /SimpleIniConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # prevent repeatedly including the targets 4 | if(NOT TARGET @PROJECT_NAME@::@PROJECT_NAME@) 5 | include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake) 6 | endif() 7 | 8 | message(STATUS "Found @PROJECT_NAME@, version: ${@PROJECT_NAME@_VERSION}") 9 | -------------------------------------------------------------------------------- /other/package.cmd: -------------------------------------------------------------------------------- 1 | set VERSION=4.15 2 | 3 | set SEVENZIP="C:\Program Files\7-Zip\7z.exe" 4 | 5 | FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Debug*') DO ( 6 | DEL /S /Q "%%G" 7 | RD "%%G" 8 | ) 9 | FOR /F "tokens=*" %%G IN ('DIR /AD /B /S Release*') DO ( 10 | DEL /S /Q "%%G" 11 | RD "%%G" 12 | ) 13 | DEL /Q "SimpleIni.ncb" 14 | ATTRIB -H "SimpleIni.suo" 15 | DEL /Q "SimpleIni.suo" 16 | DEL /Q "SimpleIni.opt" 17 | DEL /Q testsi-out*.ini 18 | DEL /Q test1-blah.ini 19 | DEL /Q test1-output.ini 20 | START "Generate documentation" /WAIT "C:\Program Files (x86)\doxygen\bin\doxygen.exe" SimpleIni.doxy 21 | cd .. 22 | del simpleini-%VERSION%.zip 23 | %SEVENZIP% a -tzip -r- -x!simpleini\.svn simpleini-%VERSION%.zip simpleini\* 24 | del simpleini-doc.zip 25 | %SEVENZIP% a -tzip -r simpleini-doc.zip simpleini-doc\* 26 | cd simpleini 27 | -------------------------------------------------------------------------------- /other/simpleini.doxy: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.5.4 2 | 3 | # This file describes the settings to be used by the documentation system 4 | # doxygen (www.doxygen.org) for a project 5 | # 6 | # All text after a hash (#) is considered a comment and will be ignored 7 | # The format is: 8 | # TAG = value [value, ...] 9 | # For lists items can also be appended using: 10 | # TAG += value [value, ...] 11 | # Values that contain spaces should be placed between quotes (" ") 12 | 13 | #--------------------------------------------------------------------------- 14 | # Project related configuration options 15 | #--------------------------------------------------------------------------- 16 | 17 | # This tag specifies the encoding used for all characters in the config file that 18 | # follow. The default is UTF-8 which is also the encoding used for all text before 19 | # the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into 20 | # libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of 21 | # possible encodings. 22 | 23 | DOXYFILE_ENCODING = UTF-8 24 | 25 | # The PROJECT_NAME tag is a single word (or a sequence of words surrounded 26 | # by quotes) that should identify the project. 27 | 28 | PROJECT_NAME = SimpleIni 29 | 30 | # The PROJECT_NUMBER tag can be used to enter a project or revision number. 31 | # This could be handy for archiving the generated documentation or 32 | # if some version control system is used. 33 | 34 | PROJECT_NUMBER = 35 | 36 | # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 37 | # base path where the generated documentation will be put. 38 | # If a relative path is entered, it will be relative to the location 39 | # where doxygen was started. If left blank the current directory will be used. 40 | 41 | OUTPUT_DIRECTORY = D:/src/simpleini-doc 42 | 43 | # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 44 | # 4096 sub-directories (in 2 levels) under the output directory of each output 45 | # format and will distribute the generated files over these directories. 46 | # Enabling this option can be useful when feeding doxygen a huge amount of 47 | # source files, where putting all generated files in the same directory would 48 | # otherwise cause performance problems for the file system. 49 | 50 | CREATE_SUBDIRS = NO 51 | 52 | # The OUTPUT_LANGUAGE tag is used to specify the language in which all 53 | # documentation generated by doxygen is written. Doxygen will use this 54 | # information to generate all constant output in the proper language. 55 | # The default language is English, other supported languages are: 56 | # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 57 | # Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, 58 | # Italian, Japanese, Japanese-en (Japanese with English messages), Korean, 59 | # Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, 60 | # Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. 61 | 62 | OUTPUT_LANGUAGE = English 63 | 64 | # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 65 | # include brief member descriptions after the members that are listed in 66 | # the file and class documentation (similar to JavaDoc). 67 | # Set to NO to disable this. 68 | 69 | BRIEF_MEMBER_DESC = YES 70 | 71 | # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 72 | # the brief description of a member or function before the detailed description. 73 | # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 74 | # brief descriptions will be completely suppressed. 75 | 76 | REPEAT_BRIEF = YES 77 | 78 | # This tag implements a quasi-intelligent brief description abbreviator 79 | # that is used to form the text in various listings. Each string 80 | # in this list, if found as the leading text of the brief description, will be 81 | # stripped from the text and the result after processing the whole list, is 82 | # used as the annotated text. Otherwise, the brief description is used as-is. 83 | # If left blank, the following values are used ("$name" is automatically 84 | # replaced with the name of the entity): "The $name class" "The $name widget" 85 | # "The $name file" "is" "provides" "specifies" "contains" 86 | # "represents" "a" "an" "the" 87 | 88 | ABBREVIATE_BRIEF = "The $name class " \ 89 | "The $name widget " \ 90 | "The $name file " \ 91 | is \ 92 | provides \ 93 | specifies \ 94 | contains \ 95 | represents \ 96 | a \ 97 | an \ 98 | the 99 | 100 | # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 101 | # Doxygen will generate a detailed section even if there is only a brief 102 | # description. 103 | 104 | ALWAYS_DETAILED_SEC = NO 105 | 106 | # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 107 | # inherited members of a class in the documentation of that class as if those 108 | # members were ordinary class members. Constructors, destructors and assignment 109 | # operators of the base classes will not be shown. 110 | 111 | INLINE_INHERITED_MEMB = NO 112 | 113 | # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 114 | # path before files name in the file list and in the header files. If set 115 | # to NO the shortest path that makes the file name unique will be used. 116 | 117 | FULL_PATH_NAMES = YES 118 | 119 | # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 120 | # can be used to strip a user-defined part of the path. Stripping is 121 | # only done if one of the specified strings matches the left-hand part of 122 | # the path. The tag can be used to show relative paths in the file list. 123 | # If left blank the directory from which doxygen is run is used as the 124 | # path to strip. 125 | 126 | STRIP_FROM_PATH = "D:/src/simpleini/ " 127 | 128 | # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 129 | # the path mentioned in the documentation of a class, which tells 130 | # the reader which header file to include in order to use a class. 131 | # If left blank only the name of the header file containing the class 132 | # definition is used. Otherwise one should specify the include paths that 133 | # are normally passed to the compiler using the -I flag. 134 | 135 | STRIP_FROM_INC_PATH = 136 | 137 | # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 138 | # (but less readable) file names. This can be useful is your file systems 139 | # doesn't support long names like on DOS, Mac, or CD-ROM. 140 | 141 | SHORT_NAMES = NO 142 | 143 | # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 144 | # will interpret the first line (until the first dot) of a JavaDoc-style 145 | # comment as the brief description. If set to NO, the JavaDoc 146 | # comments will behave just like regular Qt-style comments 147 | # (thus requiring an explicit @brief command for a brief description.) 148 | 149 | JAVADOC_AUTOBRIEF = YES 150 | 151 | # If the QT_AUTOBRIEF tag is set to YES then Doxygen will 152 | # interpret the first line (until the first dot) of a Qt-style 153 | # comment as the brief description. If set to NO, the comments 154 | # will behave just like regular Qt-style comments (thus requiring 155 | # an explicit \brief command for a brief description.) 156 | 157 | QT_AUTOBRIEF = NO 158 | 159 | # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 160 | # treat a multi-line C++ special comment block (i.e. a block of //! or /// 161 | # comments) as a brief description. This used to be the default behaviour. 162 | # The new default is to treat a multi-line C++ comment block as a detailed 163 | # description. Set this tag to YES if you prefer the old behaviour instead. 164 | 165 | MULTILINE_CPP_IS_BRIEF = NO 166 | 167 | # If the DETAILS_AT_TOP tag is set to YES then Doxygen 168 | # will output the detailed description near the top, like JavaDoc. 169 | # If set to NO, the detailed description appears after the member 170 | # documentation. 171 | 172 | DETAILS_AT_TOP = NO 173 | 174 | # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 175 | # member inherits the documentation from any documented member that it 176 | # re-implements. 177 | 178 | INHERIT_DOCS = YES 179 | 180 | # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 181 | # a new page for each member. If set to NO, the documentation of a member will 182 | # be part of the file/class/namespace that contains it. 183 | 184 | SEPARATE_MEMBER_PAGES = NO 185 | 186 | # The TAB_SIZE tag can be used to set the number of spaces in a tab. 187 | # Doxygen uses this value to replace tabs by spaces in code fragments. 188 | 189 | TAB_SIZE = 4 190 | 191 | # This tag can be used to specify a number of aliases that acts 192 | # as commands in the documentation. An alias has the form "name=value". 193 | # For example adding "sideeffect=\par Side Effects:\n" will allow you to 194 | # put the command \sideeffect (or @sideeffect) in the documentation, which 195 | # will result in a user-defined paragraph with heading "Side Effects:". 196 | # You can put \n's in the value part of an alias to insert newlines. 197 | 198 | ALIASES = 199 | 200 | # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 201 | # sources only. Doxygen will then generate output that is more tailored for C. 202 | # For instance, some of the names that are used will be different. The list 203 | # of all members will be omitted, etc. 204 | 205 | OPTIMIZE_OUTPUT_FOR_C = NO 206 | 207 | # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 208 | # sources only. Doxygen will then generate output that is more tailored for Java. 209 | # For instance, namespaces will be presented as packages, qualified scopes 210 | # will look different, etc. 211 | 212 | OPTIMIZE_OUTPUT_JAVA = NO 213 | 214 | # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 215 | # include (a tag file for) the STL sources as input, then you should 216 | # set this tag to YES in order to let doxygen match functions declarations and 217 | # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 218 | # func(std::string) {}). This also make the inheritance and collaboration 219 | # diagrams that involve STL classes more complete and accurate. 220 | 221 | BUILTIN_STL_SUPPORT = NO 222 | 223 | # If you use Microsoft's C++/CLI language, you should set this option to YES to 224 | # enable parsing support. 225 | 226 | CPP_CLI_SUPPORT = NO 227 | 228 | # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 229 | # Doxygen will parse them like normal C++ but will assume all classes use public 230 | # instead of private inheritance when no explicit protection keyword is present. 231 | 232 | SIP_SUPPORT = NO 233 | 234 | # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 235 | # tag is set to YES, then doxygen will reuse the documentation of the first 236 | # member in the group (if any) for the other members of the group. By default 237 | # all members of a group must be documented explicitly. 238 | 239 | DISTRIBUTE_GROUP_DOC = NO 240 | 241 | # Set the SUBGROUPING tag to YES (the default) to allow class member groups of 242 | # the same type (for instance a group of public functions) to be put as a 243 | # subgroup of that type (e.g. under the Public Functions section). Set it to 244 | # NO to prevent subgrouping. Alternatively, this can be done per class using 245 | # the \nosubgrouping command. 246 | 247 | SUBGROUPING = YES 248 | 249 | # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is 250 | # documented as struct with the name of the typedef. So 251 | # typedef struct TypeS {} TypeT, will appear in the documentation as a struct 252 | # with name TypeT. When disabled the typedef will appear as a member of a file, 253 | # namespace, or class. And the struct will be named TypeS. This can typically 254 | # be useful for C code where the coding convention is that all structs are 255 | # typedef'ed and only the typedef is referenced never the struct's name. 256 | 257 | TYPEDEF_HIDES_STRUCT = NO 258 | 259 | #--------------------------------------------------------------------------- 260 | # Build related configuration options 261 | #--------------------------------------------------------------------------- 262 | 263 | # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 264 | # documentation are documented, even if no documentation was available. 265 | # Private class members and static file members will be hidden unless 266 | # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES 267 | 268 | EXTRACT_ALL = YES 269 | 270 | # If the EXTRACT_PRIVATE tag is set to YES all private members of a class 271 | # will be included in the documentation. 272 | 273 | EXTRACT_PRIVATE = NO 274 | 275 | # If the EXTRACT_STATIC tag is set to YES all static members of a file 276 | # will be included in the documentation. 277 | 278 | EXTRACT_STATIC = YES 279 | 280 | # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 281 | # defined locally in source files will be included in the documentation. 282 | # If set to NO only classes defined in header files are included. 283 | 284 | EXTRACT_LOCAL_CLASSES = YES 285 | 286 | # This flag is only useful for Objective-C code. When set to YES local 287 | # methods, which are defined in the implementation section but not in 288 | # the interface are included in the documentation. 289 | # If set to NO (the default) only methods in the interface are included. 290 | 291 | EXTRACT_LOCAL_METHODS = NO 292 | 293 | # If this flag is set to YES, the members of anonymous namespaces will be extracted 294 | # and appear in the documentation as a namespace called 'anonymous_namespace{file}', 295 | # where file will be replaced with the base name of the file that contains the anonymous 296 | # namespace. By default anonymous namespace are hidden. 297 | 298 | EXTRACT_ANON_NSPACES = NO 299 | 300 | # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 301 | # undocumented members of documented classes, files or namespaces. 302 | # If set to NO (the default) these members will be included in the 303 | # various overviews, but no documentation section is generated. 304 | # This option has no effect if EXTRACT_ALL is enabled. 305 | 306 | HIDE_UNDOC_MEMBERS = NO 307 | 308 | # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 309 | # undocumented classes that are normally visible in the class hierarchy. 310 | # If set to NO (the default) these classes will be included in the various 311 | # overviews. This option has no effect if EXTRACT_ALL is enabled. 312 | 313 | HIDE_UNDOC_CLASSES = NO 314 | 315 | # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 316 | # friend (class|struct|union) declarations. 317 | # If set to NO (the default) these declarations will be included in the 318 | # documentation. 319 | 320 | HIDE_FRIEND_COMPOUNDS = NO 321 | 322 | # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 323 | # documentation blocks found inside the body of a function. 324 | # If set to NO (the default) these blocks will be appended to the 325 | # function's detailed documentation block. 326 | 327 | HIDE_IN_BODY_DOCS = NO 328 | 329 | # The INTERNAL_DOCS tag determines if documentation 330 | # that is typed after a \internal command is included. If the tag is set 331 | # to NO (the default) then the documentation will be excluded. 332 | # Set it to YES to include the internal documentation. 333 | 334 | INTERNAL_DOCS = NO 335 | 336 | # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 337 | # file names in lower-case letters. If set to YES upper-case letters are also 338 | # allowed. This is useful if you have classes or files whose names only differ 339 | # in case and if your file system supports case sensitive file names. Windows 340 | # and Mac users are advised to set this option to NO. 341 | 342 | CASE_SENSE_NAMES = NO 343 | 344 | # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 345 | # will show members with their full class and namespace scopes in the 346 | # documentation. If set to YES the scope will be hidden. 347 | 348 | HIDE_SCOPE_NAMES = NO 349 | 350 | # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 351 | # will put a list of the files that are included by a file in the documentation 352 | # of that file. 353 | 354 | SHOW_INCLUDE_FILES = YES 355 | 356 | # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 357 | # is inserted in the documentation for inline members. 358 | 359 | INLINE_INFO = YES 360 | 361 | # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 362 | # will sort the (detailed) documentation of file and class members 363 | # alphabetically by member name. If set to NO the members will appear in 364 | # declaration order. 365 | 366 | SORT_MEMBER_DOCS = YES 367 | 368 | # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 369 | # brief documentation of file, namespace and class members alphabetically 370 | # by member name. If set to NO (the default) the members will appear in 371 | # declaration order. 372 | 373 | SORT_BRIEF_DOCS = NO 374 | 375 | # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 376 | # sorted by fully-qualified names, including namespaces. If set to 377 | # NO (the default), the class list will be sorted only by class name, 378 | # not including the namespace part. 379 | # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. 380 | # Note: This option applies only to the class list, not to the 381 | # alphabetical list. 382 | 383 | SORT_BY_SCOPE_NAME = NO 384 | 385 | # The GENERATE_TODOLIST tag can be used to enable (YES) or 386 | # disable (NO) the todo list. This list is created by putting \todo 387 | # commands in the documentation. 388 | 389 | GENERATE_TODOLIST = YES 390 | 391 | # The GENERATE_TESTLIST tag can be used to enable (YES) or 392 | # disable (NO) the test list. This list is created by putting \test 393 | # commands in the documentation. 394 | 395 | GENERATE_TESTLIST = YES 396 | 397 | # The GENERATE_BUGLIST tag can be used to enable (YES) or 398 | # disable (NO) the bug list. This list is created by putting \bug 399 | # commands in the documentation. 400 | 401 | GENERATE_BUGLIST = YES 402 | 403 | # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 404 | # disable (NO) the deprecated list. This list is created by putting 405 | # \deprecated commands in the documentation. 406 | 407 | GENERATE_DEPRECATEDLIST= YES 408 | 409 | # The ENABLED_SECTIONS tag can be used to enable conditional 410 | # documentation sections, marked by \if sectionname ... \endif. 411 | 412 | ENABLED_SECTIONS = 413 | 414 | # The MAX_INITIALIZER_LINES tag determines the maximum number of lines 415 | # the initial value of a variable or define consists of for it to appear in 416 | # the documentation. If the initializer consists of more lines than specified 417 | # here it will be hidden. Use a value of 0 to hide initializers completely. 418 | # The appearance of the initializer of individual variables and defines in the 419 | # documentation can be controlled using \showinitializer or \hideinitializer 420 | # command in the documentation regardless of this setting. 421 | 422 | MAX_INITIALIZER_LINES = 30 423 | 424 | # Set the SHOW_USED_FILES tag to NO to disable the list of files generated 425 | # at the bottom of the documentation of classes and structs. If set to YES the 426 | # list will mention the files that were used to generate the documentation. 427 | 428 | SHOW_USED_FILES = YES 429 | 430 | # If the sources in your project are distributed over multiple directories 431 | # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 432 | # in the documentation. The default is NO. 433 | 434 | SHOW_DIRECTORIES = NO 435 | 436 | # The FILE_VERSION_FILTER tag can be used to specify a program or script that 437 | # doxygen should invoke to get the current version for each file (typically from the 438 | # version control system). Doxygen will invoke the program by executing (via 439 | # popen()) the command , where is the value of 440 | # the FILE_VERSION_FILTER tag, and is the name of an input file 441 | # provided by doxygen. Whatever the program writes to standard output 442 | # is used as the file version. See the manual for examples. 443 | 444 | FILE_VERSION_FILTER = 445 | 446 | #--------------------------------------------------------------------------- 447 | # configuration options related to warning and progress messages 448 | #--------------------------------------------------------------------------- 449 | 450 | # The QUIET tag can be used to turn on/off the messages that are generated 451 | # by doxygen. Possible values are YES and NO. If left blank NO is used. 452 | 453 | QUIET = NO 454 | 455 | # The WARNINGS tag can be used to turn on/off the warning messages that are 456 | # generated by doxygen. Possible values are YES and NO. If left blank 457 | # NO is used. 458 | 459 | WARNINGS = YES 460 | 461 | # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 462 | # for undocumented members. If EXTRACT_ALL is set to YES then this flag will 463 | # automatically be disabled. 464 | 465 | WARN_IF_UNDOCUMENTED = YES 466 | 467 | # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 468 | # potential errors in the documentation, such as not documenting some 469 | # parameters in a documented function, or documenting parameters that 470 | # don't exist or using markup commands wrongly. 471 | 472 | WARN_IF_DOC_ERROR = YES 473 | 474 | # This WARN_NO_PARAMDOC option can be abled to get warnings for 475 | # functions that are documented, but have no documentation for their parameters 476 | # or return value. If set to NO (the default) doxygen will only warn about 477 | # wrong or incomplete parameter documentation, but not about the absence of 478 | # documentation. 479 | 480 | WARN_NO_PARAMDOC = YES 481 | 482 | # The WARN_FORMAT tag determines the format of the warning messages that 483 | # doxygen can produce. The string should contain the $file, $line, and $text 484 | # tags, which will be replaced by the file and line number from which the 485 | # warning originated and the warning text. Optionally the format may contain 486 | # $version, which will be replaced by the version of the file (if it could 487 | # be obtained via FILE_VERSION_FILTER) 488 | 489 | WARN_FORMAT = "$file($line) : $text " 490 | 491 | # The WARN_LOGFILE tag can be used to specify a file to which warning 492 | # and error messages should be written. If left blank the output is written 493 | # to stderr. 494 | 495 | WARN_LOGFILE = 496 | 497 | #--------------------------------------------------------------------------- 498 | # configuration options related to the input files 499 | #--------------------------------------------------------------------------- 500 | 501 | # The INPUT tag can be used to specify the files and/or directories that contain 502 | # documented source files. You may enter file names like "myfile.cpp" or 503 | # directories like "/usr/src/myproject". Separate the files or directories 504 | # with spaces. 505 | 506 | INPUT = D:/src/simpleini/SimpleIni.h 507 | 508 | # This tag can be used to specify the character encoding of the source files that 509 | # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default 510 | # input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. 511 | # See http://www.gnu.org/software/libiconv for the list of possible encodings. 512 | 513 | INPUT_ENCODING = UTF-8 514 | 515 | # If the value of the INPUT tag contains directories, you can use the 516 | # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 517 | # and *.h) to filter out the source-files in the directories. If left 518 | # blank the following patterns are tested: 519 | # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 520 | # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 521 | 522 | FILE_PATTERNS = *.h 523 | 524 | # The RECURSIVE tag can be used to turn specify whether or not subdirectories 525 | # should be searched for input files as well. Possible values are YES and NO. 526 | # If left blank NO is used. 527 | 528 | RECURSIVE = NO 529 | 530 | # The EXCLUDE tag can be used to specify files and/or directories that should 531 | # excluded from the INPUT source files. This way you can easily exclude a 532 | # subdirectory from a directory tree whose root is specified with the INPUT tag. 533 | 534 | EXCLUDE = 535 | 536 | # The EXCLUDE_SYMLINKS tag can be used select whether or not files or 537 | # directories that are symbolic links (a Unix filesystem feature) are excluded 538 | # from the input. 539 | 540 | EXCLUDE_SYMLINKS = NO 541 | 542 | # If the value of the INPUT tag contains directories, you can use the 543 | # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 544 | # certain files from those directories. Note that the wildcards are matched 545 | # against the file with absolute path, so to exclude all test directories 546 | # for example use the pattern */test/* 547 | 548 | EXCLUDE_PATTERNS = 549 | 550 | # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 551 | # (namespaces, classes, functions, etc.) that should be excluded from the output. 552 | # The symbol name can be a fully qualified name, a word, or if the wildcard * is used, 553 | # a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test 554 | 555 | EXCLUDE_SYMBOLS = 556 | 557 | # The EXAMPLE_PATH tag can be used to specify one or more files or 558 | # directories that contain example code fragments that are included (see 559 | # the \include command). 560 | 561 | EXAMPLE_PATH = 562 | 563 | # If the value of the EXAMPLE_PATH tag contains directories, you can use the 564 | # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 565 | # and *.h) to filter out the source-files in the directories. If left 566 | # blank all files are included. 567 | 568 | EXAMPLE_PATTERNS = * 569 | 570 | # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 571 | # searched for input files to be used with the \include or \dontinclude 572 | # commands irrespective of the value of the RECURSIVE tag. 573 | # Possible values are YES and NO. If left blank NO is used. 574 | 575 | EXAMPLE_RECURSIVE = NO 576 | 577 | # The IMAGE_PATH tag can be used to specify one or more files or 578 | # directories that contain image that are included in the documentation (see 579 | # the \image command). 580 | 581 | IMAGE_PATH = 582 | 583 | # The INPUT_FILTER tag can be used to specify a program that doxygen should 584 | # invoke to filter for each input file. Doxygen will invoke the filter program 585 | # by executing (via popen()) the command , where 586 | # is the value of the INPUT_FILTER tag, and is the name of an 587 | # input file. Doxygen will then use the output that the filter program writes 588 | # to standard output. If FILTER_PATTERNS is specified, this tag will be 589 | # ignored. 590 | 591 | INPUT_FILTER = 592 | 593 | # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 594 | # basis. Doxygen will compare the file name with each pattern and apply the 595 | # filter if there is a match. The filters are a list of the form: 596 | # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 597 | # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 598 | # is applied to all files. 599 | 600 | FILTER_PATTERNS = 601 | 602 | # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 603 | # INPUT_FILTER) will be used to filter the input files when producing source 604 | # files to browse (i.e. when SOURCE_BROWSER is set to YES). 605 | 606 | FILTER_SOURCE_FILES = NO 607 | 608 | #--------------------------------------------------------------------------- 609 | # configuration options related to source browsing 610 | #--------------------------------------------------------------------------- 611 | 612 | # If the SOURCE_BROWSER tag is set to YES then a list of source files will 613 | # be generated. Documented entities will be cross-referenced with these sources. 614 | # Note: To get rid of all source code in the generated output, make sure also 615 | # VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH 616 | # then you must also enable this option. If you don't then doxygen will produce 617 | # a warning and turn it on anyway 618 | 619 | SOURCE_BROWSER = YES 620 | 621 | # Setting the INLINE_SOURCES tag to YES will include the body 622 | # of functions and classes directly in the documentation. 623 | 624 | INLINE_SOURCES = NO 625 | 626 | # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 627 | # doxygen to hide any special comment blocks from generated source code 628 | # fragments. Normal C and C++ comments will always remain visible. 629 | 630 | STRIP_CODE_COMMENTS = YES 631 | 632 | # If the REFERENCED_BY_RELATION tag is set to YES (the default) 633 | # then for each documented function all documented 634 | # functions referencing it will be listed. 635 | 636 | REFERENCED_BY_RELATION = YES 637 | 638 | # If the REFERENCES_RELATION tag is set to YES (the default) 639 | # then for each documented function all documented entities 640 | # called/used by that function will be listed. 641 | 642 | REFERENCES_RELATION = YES 643 | 644 | # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) 645 | # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from 646 | # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will 647 | # link to the source code. Otherwise they will link to the documentation. 648 | 649 | REFERENCES_LINK_SOURCE = YES 650 | 651 | # If the USE_HTAGS tag is set to YES then the references to source code 652 | # will point to the HTML generated by the htags(1) tool instead of doxygen 653 | # built-in source browser. The htags tool is part of GNU's global source 654 | # tagging system (see http://www.gnu.org/software/global/global.html). You 655 | # will need version 4.8.6 or higher. 656 | 657 | USE_HTAGS = NO 658 | 659 | # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 660 | # will generate a verbatim copy of the header file for each class for 661 | # which an include is specified. Set to NO to disable this. 662 | 663 | VERBATIM_HEADERS = YES 664 | 665 | #--------------------------------------------------------------------------- 666 | # configuration options related to the alphabetical class index 667 | #--------------------------------------------------------------------------- 668 | 669 | # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 670 | # of all compounds will be generated. Enable this if the project 671 | # contains a lot of classes, structs, unions or interfaces. 672 | 673 | ALPHABETICAL_INDEX = NO 674 | 675 | # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 676 | # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 677 | # in which this list will be split (can be a number in the range [1..20]) 678 | 679 | COLS_IN_ALPHA_INDEX = 5 680 | 681 | # In case all classes in a project start with a common prefix, all 682 | # classes will be put under the same header in the alphabetical index. 683 | # The IGNORE_PREFIX tag can be used to specify one or more prefixes that 684 | # should be ignored while generating the index headers. 685 | 686 | IGNORE_PREFIX = 687 | 688 | #--------------------------------------------------------------------------- 689 | # configuration options related to the HTML output 690 | #--------------------------------------------------------------------------- 691 | 692 | # If the GENERATE_HTML tag is set to YES (the default) Doxygen will 693 | # generate HTML output. 694 | 695 | GENERATE_HTML = YES 696 | 697 | # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 698 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 699 | # put in front of it. If left blank `html' will be used as the default path. 700 | 701 | HTML_OUTPUT = html 702 | 703 | # The HTML_FILE_EXTENSION tag can be used to specify the file extension for 704 | # each generated HTML page (for example: .htm,.php,.asp). If it is left blank 705 | # doxygen will generate files with .html extension. 706 | 707 | HTML_FILE_EXTENSION = .html 708 | 709 | # The HTML_HEADER tag can be used to specify a personal HTML header for 710 | # each generated HTML page. If it is left blank doxygen will generate a 711 | # standard header. 712 | 713 | HTML_HEADER = 714 | 715 | # The HTML_FOOTER tag can be used to specify a personal HTML footer for 716 | # each generated HTML page. If it is left blank doxygen will generate a 717 | # standard footer. 718 | 719 | HTML_FOOTER = 720 | 721 | # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 722 | # style sheet that is used by each HTML page. It can be used to 723 | # fine-tune the look of the HTML output. If the tag is left blank doxygen 724 | # will generate a default style sheet. Note that doxygen will try to copy 725 | # the style sheet file to the HTML output directory, so don't put your own 726 | # stylesheet in the HTML output directory as well, or it will be erased! 727 | 728 | HTML_STYLESHEET = 729 | 730 | # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 731 | # files or namespaces will be aligned in HTML using tables. If set to 732 | # NO a bullet list will be used. 733 | 734 | HTML_ALIGN_MEMBERS = YES 735 | 736 | # If the GENERATE_HTMLHELP tag is set to YES, additional index files 737 | # will be generated that can be used as input for tools like the 738 | # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 739 | # of the generated HTML documentation. 740 | 741 | GENERATE_HTMLHELP = NO 742 | 743 | # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 744 | # documentation will contain sections that can be hidden and shown after the 745 | # page has loaded. For this to work a browser that supports 746 | # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 747 | # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). 748 | 749 | HTML_DYNAMIC_SECTIONS = NO 750 | 751 | # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 752 | # be used to specify the file name of the resulting .chm file. You 753 | # can add a path in front of the file if the result should not be 754 | # written to the html output directory. 755 | 756 | CHM_FILE = 757 | 758 | # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 759 | # be used to specify the location (absolute path including file name) of 760 | # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 761 | # the HTML help compiler on the generated index.hhp. 762 | 763 | HHC_LOCATION = 764 | 765 | # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 766 | # controls if a separate .chi index file is generated (YES) or that 767 | # it should be included in the master .chm file (NO). 768 | 769 | GENERATE_CHI = NO 770 | 771 | # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 772 | # controls whether a binary table of contents is generated (YES) or a 773 | # normal table of contents (NO) in the .chm file. 774 | 775 | BINARY_TOC = NO 776 | 777 | # The TOC_EXPAND flag can be set to YES to add extra items for group members 778 | # to the contents of the HTML help documentation and to the tree view. 779 | 780 | TOC_EXPAND = NO 781 | 782 | # The DISABLE_INDEX tag can be used to turn on/off the condensed index at 783 | # top of each HTML page. The value NO (the default) enables the index and 784 | # the value YES disables it. 785 | 786 | DISABLE_INDEX = NO 787 | 788 | # This tag can be used to set the number of enum values (range [1..20]) 789 | # that doxygen will group on one line in the generated HTML documentation. 790 | 791 | ENUM_VALUES_PER_LINE = 4 792 | 793 | # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be 794 | # generated containing a tree-like index structure (just like the one that 795 | # is generated for HTML Help). For this to work a browser that supports 796 | # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 797 | # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 798 | # probably better off using the HTML help feature. 799 | 800 | GENERATE_TREEVIEW = NO 801 | 802 | # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 803 | # used to set the initial width (in pixels) of the frame in which the tree 804 | # is shown. 805 | 806 | TREEVIEW_WIDTH = 250 807 | 808 | #--------------------------------------------------------------------------- 809 | # configuration options related to the LaTeX output 810 | #--------------------------------------------------------------------------- 811 | 812 | # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 813 | # generate Latex output. 814 | 815 | GENERATE_LATEX = NO 816 | 817 | # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 818 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 819 | # put in front of it. If left blank `latex' will be used as the default path. 820 | 821 | LATEX_OUTPUT = latex 822 | 823 | # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 824 | # invoked. If left blank `latex' will be used as the default command name. 825 | 826 | LATEX_CMD_NAME = latex 827 | 828 | # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 829 | # generate index for LaTeX. If left blank `makeindex' will be used as the 830 | # default command name. 831 | 832 | MAKEINDEX_CMD_NAME = makeindex 833 | 834 | # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 835 | # LaTeX documents. This may be useful for small projects and may help to 836 | # save some trees in general. 837 | 838 | COMPACT_LATEX = NO 839 | 840 | # The PAPER_TYPE tag can be used to set the paper type that is used 841 | # by the printer. Possible values are: a4, a4wide, letter, legal and 842 | # executive. If left blank a4wide will be used. 843 | 844 | PAPER_TYPE = a4wide 845 | 846 | # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 847 | # packages that should be included in the LaTeX output. 848 | 849 | EXTRA_PACKAGES = 850 | 851 | # The LATEX_HEADER tag can be used to specify a personal LaTeX header for 852 | # the generated latex document. The header should contain everything until 853 | # the first chapter. If it is left blank doxygen will generate a 854 | # standard header. Notice: only use this tag if you know what you are doing! 855 | 856 | LATEX_HEADER = 857 | 858 | # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 859 | # is prepared for conversion to pdf (using ps2pdf). The pdf file will 860 | # contain links (just like the HTML output) instead of page references 861 | # This makes the output suitable for online browsing using a pdf viewer. 862 | 863 | PDF_HYPERLINKS = NO 864 | 865 | # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 866 | # plain latex in the generated Makefile. Set this option to YES to get a 867 | # higher quality PDF documentation. 868 | 869 | USE_PDFLATEX = NO 870 | 871 | # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 872 | # command to the generated LaTeX files. This will instruct LaTeX to keep 873 | # running if errors occur, instead of asking the user for help. 874 | # This option is also used when generating formulas in HTML. 875 | 876 | LATEX_BATCHMODE = NO 877 | 878 | # If LATEX_HIDE_INDICES is set to YES then doxygen will not 879 | # include the index chapters (such as File Index, Compound Index, etc.) 880 | # in the output. 881 | 882 | LATEX_HIDE_INDICES = NO 883 | 884 | #--------------------------------------------------------------------------- 885 | # configuration options related to the RTF output 886 | #--------------------------------------------------------------------------- 887 | 888 | # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 889 | # The RTF output is optimized for Word 97 and may not look very pretty with 890 | # other RTF readers or editors. 891 | 892 | GENERATE_RTF = NO 893 | 894 | # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 895 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 896 | # put in front of it. If left blank `rtf' will be used as the default path. 897 | 898 | RTF_OUTPUT = rtf 899 | 900 | # If the COMPACT_RTF tag is set to YES Doxygen generates more compact 901 | # RTF documents. This may be useful for small projects and may help to 902 | # save some trees in general. 903 | 904 | COMPACT_RTF = NO 905 | 906 | # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 907 | # will contain hyperlink fields. The RTF file will 908 | # contain links (just like the HTML output) instead of page references. 909 | # This makes the output suitable for online browsing using WORD or other 910 | # programs which support those fields. 911 | # Note: wordpad (write) and others do not support links. 912 | 913 | RTF_HYPERLINKS = NO 914 | 915 | # Load stylesheet definitions from file. Syntax is similar to doxygen's 916 | # config file, i.e. a series of assignments. You only have to provide 917 | # replacements, missing definitions are set to their default value. 918 | 919 | RTF_STYLESHEET_FILE = 920 | 921 | # Set optional variables used in the generation of an rtf document. 922 | # Syntax is similar to doxygen's config file. 923 | 924 | RTF_EXTENSIONS_FILE = 925 | 926 | #--------------------------------------------------------------------------- 927 | # configuration options related to the man page output 928 | #--------------------------------------------------------------------------- 929 | 930 | # If the GENERATE_MAN tag is set to YES (the default) Doxygen will 931 | # generate man pages 932 | 933 | GENERATE_MAN = NO 934 | 935 | # The MAN_OUTPUT tag is used to specify where the man pages will be put. 936 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 937 | # put in front of it. If left blank `man' will be used as the default path. 938 | 939 | MAN_OUTPUT = man 940 | 941 | # The MAN_EXTENSION tag determines the extension that is added to 942 | # the generated man pages (default is the subroutine's section .3) 943 | 944 | MAN_EXTENSION = .3 945 | 946 | # If the MAN_LINKS tag is set to YES and Doxygen generates man output, 947 | # then it will generate one additional man file for each entity 948 | # documented in the real man page(s). These additional files 949 | # only source the real man page, but without them the man command 950 | # would be unable to find the correct page. The default is NO. 951 | 952 | MAN_LINKS = NO 953 | 954 | #--------------------------------------------------------------------------- 955 | # configuration options related to the XML output 956 | #--------------------------------------------------------------------------- 957 | 958 | # If the GENERATE_XML tag is set to YES Doxygen will 959 | # generate an XML file that captures the structure of 960 | # the code including all documentation. 961 | 962 | GENERATE_XML = NO 963 | 964 | # The XML_OUTPUT tag is used to specify where the XML pages will be put. 965 | # If a relative path is entered the value of OUTPUT_DIRECTORY will be 966 | # put in front of it. If left blank `xml' will be used as the default path. 967 | 968 | XML_OUTPUT = xml 969 | 970 | # The XML_SCHEMA tag can be used to specify an XML schema, 971 | # which can be used by a validating XML parser to check the 972 | # syntax of the XML files. 973 | 974 | XML_SCHEMA = 975 | 976 | # The XML_DTD tag can be used to specify an XML DTD, 977 | # which can be used by a validating XML parser to check the 978 | # syntax of the XML files. 979 | 980 | XML_DTD = 981 | 982 | # If the XML_PROGRAMLISTING tag is set to YES Doxygen will 983 | # dump the program listings (including syntax highlighting 984 | # and cross-referencing information) to the XML output. Note that 985 | # enabling this will significantly increase the size of the XML output. 986 | 987 | XML_PROGRAMLISTING = YES 988 | 989 | #--------------------------------------------------------------------------- 990 | # configuration options for the AutoGen Definitions output 991 | #--------------------------------------------------------------------------- 992 | 993 | # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 994 | # generate an AutoGen Definitions (see autogen.sf.net) file 995 | # that captures the structure of the code including all 996 | # documentation. Note that this feature is still experimental 997 | # and incomplete at the moment. 998 | 999 | GENERATE_AUTOGEN_DEF = NO 1000 | 1001 | #--------------------------------------------------------------------------- 1002 | # configuration options related to the Perl module output 1003 | #--------------------------------------------------------------------------- 1004 | 1005 | # If the GENERATE_PERLMOD tag is set to YES Doxygen will 1006 | # generate a Perl module file that captures the structure of 1007 | # the code including all documentation. Note that this 1008 | # feature is still experimental and incomplete at the 1009 | # moment. 1010 | 1011 | GENERATE_PERLMOD = NO 1012 | 1013 | # If the PERLMOD_LATEX tag is set to YES Doxygen will generate 1014 | # the necessary Makefile rules, Perl scripts and LaTeX code to be able 1015 | # to generate PDF and DVI output from the Perl module output. 1016 | 1017 | PERLMOD_LATEX = NO 1018 | 1019 | # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 1020 | # nicely formatted so it can be parsed by a human reader. This is useful 1021 | # if you want to understand what is going on. On the other hand, if this 1022 | # tag is set to NO the size of the Perl module output will be much smaller 1023 | # and Perl will parse it just the same. 1024 | 1025 | PERLMOD_PRETTY = YES 1026 | 1027 | # The names of the make variables in the generated doxyrules.make file 1028 | # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 1029 | # This is useful so different doxyrules.make files included by the same 1030 | # Makefile don't overwrite each other's variables. 1031 | 1032 | PERLMOD_MAKEVAR_PREFIX = 1033 | 1034 | #--------------------------------------------------------------------------- 1035 | # Configuration options related to the preprocessor 1036 | #--------------------------------------------------------------------------- 1037 | 1038 | # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 1039 | # evaluate all C-preprocessor directives found in the sources and include 1040 | # files. 1041 | 1042 | ENABLE_PREPROCESSING = YES 1043 | 1044 | # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 1045 | # names in the source code. If set to NO (the default) only conditional 1046 | # compilation will be performed. Macro expansion can be done in a controlled 1047 | # way by setting EXPAND_ONLY_PREDEF to YES. 1048 | 1049 | MACRO_EXPANSION = NO 1050 | 1051 | # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 1052 | # then the macro expansion is limited to the macros specified with the 1053 | # PREDEFINED and EXPAND_AS_DEFINED tags. 1054 | 1055 | EXPAND_ONLY_PREDEF = NO 1056 | 1057 | # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 1058 | # in the INCLUDE_PATH (see below) will be search if a #include is found. 1059 | 1060 | SEARCH_INCLUDES = YES 1061 | 1062 | # The INCLUDE_PATH tag can be used to specify one or more directories that 1063 | # contain include files that are not input files but should be processed by 1064 | # the preprocessor. 1065 | 1066 | INCLUDE_PATH = 1067 | 1068 | # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 1069 | # patterns (like *.h and *.hpp) to filter out the header-files in the 1070 | # directories. If left blank, the patterns specified with FILE_PATTERNS will 1071 | # be used. 1072 | 1073 | INCLUDE_FILE_PATTERNS = 1074 | 1075 | # The PREDEFINED tag can be used to specify one or more macro names that 1076 | # are defined before the preprocessor is started (similar to the -D option of 1077 | # gcc). The argument of the tag is a list of macros of the form: name 1078 | # or name=definition (no spaces). If the definition and the = are 1079 | # omitted =1 is assumed. To prevent a macro definition from being 1080 | # undefined via #undef or recursively expanded use the := operator 1081 | # instead of the = operator. 1082 | 1083 | PREDEFINED = SI_HAS_WIDE_FILE \ 1084 | SI_SUPPORT_IOSTREAMS 1085 | 1086 | # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 1087 | # this tag can be used to specify a list of macro names that should be expanded. 1088 | # The macro definition that is found in the sources will be used. 1089 | # Use the PREDEFINED tag if you want to use a different macro definition. 1090 | 1091 | EXPAND_AS_DEFINED = 1092 | 1093 | # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 1094 | # doxygen's preprocessor will remove all function-like macros that are alone 1095 | # on a line, have an all uppercase name, and do not end with a semicolon. Such 1096 | # function macros are typically used for boiler-plate code, and will confuse 1097 | # the parser if not removed. 1098 | 1099 | SKIP_FUNCTION_MACROS = YES 1100 | 1101 | #--------------------------------------------------------------------------- 1102 | # Configuration::additions related to external references 1103 | #--------------------------------------------------------------------------- 1104 | 1105 | # The TAGFILES option can be used to specify one or more tagfiles. 1106 | # Optionally an initial location of the external documentation 1107 | # can be added for each tagfile. The format of a tag file without 1108 | # this location is as follows: 1109 | # TAGFILES = file1 file2 ... 1110 | # Adding location for the tag files is done as follows: 1111 | # TAGFILES = file1=loc1 "file2 = loc2" ... 1112 | # where "loc1" and "loc2" can be relative or absolute paths or 1113 | # URLs. If a location is present for each tag, the installdox tool 1114 | # does not have to be run to correct the links. 1115 | # Note that each tag file must have a unique name 1116 | # (where the name does NOT include the path) 1117 | # If a tag file is not located in the directory in which doxygen 1118 | # is run, you must also specify the path to the tagfile here. 1119 | 1120 | TAGFILES = 1121 | 1122 | # When a file name is specified after GENERATE_TAGFILE, doxygen will create 1123 | # a tag file that is based on the input files it reads. 1124 | 1125 | GENERATE_TAGFILE = 1126 | 1127 | # If the ALLEXTERNALS tag is set to YES all external classes will be listed 1128 | # in the class index. If set to NO only the inherited external classes 1129 | # will be listed. 1130 | 1131 | ALLEXTERNALS = NO 1132 | 1133 | # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 1134 | # in the modules index. If set to NO, only the current project's groups will 1135 | # be listed. 1136 | 1137 | EXTERNAL_GROUPS = YES 1138 | 1139 | # The PERL_PATH should be the absolute path and name of the perl script 1140 | # interpreter (i.e. the result of `which perl'). 1141 | 1142 | PERL_PATH = /usr/bin/perl 1143 | 1144 | #--------------------------------------------------------------------------- 1145 | # Configuration options related to the dot tool 1146 | #--------------------------------------------------------------------------- 1147 | 1148 | # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 1149 | # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 1150 | # or super classes. Setting the tag to NO turns the diagrams off. Note that 1151 | # this option is superseded by the HAVE_DOT option below. This is only a 1152 | # fallback. It is recommended to install and use dot, since it yields more 1153 | # powerful graphs. 1154 | 1155 | CLASS_DIAGRAMS = YES 1156 | 1157 | # You can define message sequence charts within doxygen comments using the \msc 1158 | # command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to 1159 | # produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to 1160 | # specify the directory where the mscgen tool resides. If left empty the tool is assumed to 1161 | # be found in the default search path. 1162 | 1163 | MSCGEN_PATH = 1164 | 1165 | # If set to YES, the inheritance and collaboration graphs will hide 1166 | # inheritance and usage relations if the target is undocumented 1167 | # or is not a class. 1168 | 1169 | HIDE_UNDOC_RELATIONS = YES 1170 | 1171 | # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 1172 | # available from the path. This tool is part of Graphviz, a graph visualization 1173 | # toolkit from AT&T and Lucent Bell Labs. The other options in this section 1174 | # have no effect if this option is set to NO (the default) 1175 | 1176 | HAVE_DOT = NO 1177 | 1178 | # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 1179 | # will generate a graph for each documented class showing the direct and 1180 | # indirect inheritance relations. Setting this tag to YES will force the 1181 | # the CLASS_DIAGRAMS tag to NO. 1182 | 1183 | CLASS_GRAPH = YES 1184 | 1185 | # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 1186 | # will generate a graph for each documented class showing the direct and 1187 | # indirect implementation dependencies (inheritance, containment, and 1188 | # class references variables) of the class with other documented classes. 1189 | 1190 | COLLABORATION_GRAPH = YES 1191 | 1192 | # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 1193 | # will generate a graph for groups, showing the direct groups dependencies 1194 | 1195 | GROUP_GRAPHS = YES 1196 | 1197 | # If the UML_LOOK tag is set to YES doxygen will generate inheritance and 1198 | # collaboration diagrams in a style similar to the OMG's Unified Modeling 1199 | # Language. 1200 | 1201 | UML_LOOK = NO 1202 | 1203 | # If set to YES, the inheritance and collaboration graphs will show the 1204 | # relations between templates and their instances. 1205 | 1206 | TEMPLATE_RELATIONS = NO 1207 | 1208 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 1209 | # tags are set to YES then doxygen will generate a graph for each documented 1210 | # file showing the direct and indirect include dependencies of the file with 1211 | # other documented files. 1212 | 1213 | INCLUDE_GRAPH = YES 1214 | 1215 | # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 1216 | # HAVE_DOT tags are set to YES then doxygen will generate a graph for each 1217 | # documented header file showing the documented files that directly or 1218 | # indirectly include this file. 1219 | 1220 | INCLUDED_BY_GRAPH = YES 1221 | 1222 | # If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 1223 | # generate a call dependency graph for every global function or class method. 1224 | # Note that enabling this option will significantly increase the time of a run. 1225 | # So in most cases it will be better to enable call graphs for selected 1226 | # functions only using the \callgraph command. 1227 | 1228 | CALL_GRAPH = NO 1229 | 1230 | # If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 1231 | # generate a caller dependency graph for every global function or class method. 1232 | # Note that enabling this option will significantly increase the time of a run. 1233 | # So in most cases it will be better to enable caller graphs for selected 1234 | # functions only using the \callergraph command. 1235 | 1236 | CALLER_GRAPH = NO 1237 | 1238 | # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 1239 | # will graphical hierarchy of all classes instead of a textual one. 1240 | 1241 | GRAPHICAL_HIERARCHY = YES 1242 | 1243 | # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 1244 | # then doxygen will show the dependencies a directory has on other directories 1245 | # in a graphical way. The dependency relations are determined by the #include 1246 | # relations between the files in the directories. 1247 | 1248 | DIRECTORY_GRAPH = YES 1249 | 1250 | # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 1251 | # generated by dot. Possible values are png, jpg, or gif 1252 | # If left blank png will be used. 1253 | 1254 | DOT_IMAGE_FORMAT = png 1255 | 1256 | # The tag DOT_PATH can be used to specify the path where the dot tool can be 1257 | # found. If left blank, it is assumed the dot tool can be found in the path. 1258 | 1259 | DOT_PATH = 1260 | 1261 | # The DOTFILE_DIRS tag can be used to specify one or more directories that 1262 | # contain dot files that are included in the documentation (see the 1263 | # \dotfile command). 1264 | 1265 | DOTFILE_DIRS = 1266 | 1267 | # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 1268 | # nodes that will be shown in the graph. If the number of nodes in a graph 1269 | # becomes larger than this value, doxygen will truncate the graph, which is 1270 | # visualized by representing a node as a red box. Note that doxygen if the number 1271 | # of direct children of the root node in a graph is already larger than 1272 | # MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note 1273 | # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. 1274 | 1275 | DOT_GRAPH_MAX_NODES = 50 1276 | 1277 | # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 1278 | # graphs generated by dot. A depth value of 3 means that only nodes reachable 1279 | # from the root by following a path via at most 3 edges will be shown. Nodes 1280 | # that lay further from the root node will be omitted. Note that setting this 1281 | # option to 1 or 2 may greatly reduce the computation time needed for large 1282 | # code bases. Also note that the size of a graph can be further restricted by 1283 | # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. 1284 | 1285 | MAX_DOT_GRAPH_DEPTH = 1000 1286 | 1287 | # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 1288 | # background. This is disabled by default, which results in a white background. 1289 | # Warning: Depending on the platform used, enabling this option may lead to 1290 | # badly anti-aliased labels on the edges of a graph (i.e. they become hard to 1291 | # read). 1292 | 1293 | DOT_TRANSPARENT = NO 1294 | 1295 | # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 1296 | # files in one run (i.e. multiple -o and -T options on the command line). This 1297 | # makes dot run faster, but since only newer versions of dot (>1.8.10) 1298 | # support this, this feature is disabled by default. 1299 | 1300 | DOT_MULTI_TARGETS = NO 1301 | 1302 | # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 1303 | # generate a legend page explaining the meaning of the various boxes and 1304 | # arrows in the dot generated graphs. 1305 | 1306 | GENERATE_LEGEND = YES 1307 | 1308 | # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 1309 | # remove the intermediate dot files that are used to generate 1310 | # the various graphs. 1311 | 1312 | DOT_CLEANUP = YES 1313 | 1314 | #--------------------------------------------------------------------------- 1315 | # Configuration::additions related to the search engine 1316 | #--------------------------------------------------------------------------- 1317 | 1318 | # The SEARCHENGINE tag specifies whether or not a search engine should be 1319 | # used. If set to NO the values of all tags below this one will be ignored. 1320 | 1321 | SEARCHENGINE = NO 1322 | -------------------------------------------------------------------------------- /release.txt: -------------------------------------------------------------------------------- 1 | Making a release: 2 | * update version number in SimpleIni.h 3 | * update version number in CMakeLists.txt 4 | * check-in all files 5 | * make a release "v4.22" etc creating a matching tag 6 | 7 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | example2.ini 2 | 3 | tests 4 | *.o 5 | 6 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(SIMPLEINI_USE_SYSTEM_GTEST) 2 | find_package(GTest REQUIRED) 3 | else() 4 | include(FetchContent) 5 | FetchContent_Declare( 6 | googletest 7 | DOWNLOAD_EXTRACT_TIMESTAMP ON 8 | URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip 9 | URL_HASH SHA1=0ac421f2ec11af38b0fff0f1992184032731a8bc 10 | ) 11 | FetchContent_MakeAvailable(googletest) 12 | endif() 13 | 14 | add_executable(tests 15 | ts-bugfix.cpp 16 | ts-noconvert.cpp 17 | ts-quotes.cpp 18 | ts-roundtrip.cpp 19 | ts-snippets.cpp 20 | ts-utf8.cpp) 21 | 22 | add_test(NAME tests COMMAND tests) 23 | target_link_libraries(tests PRIVATE ${PROJECT_NAME} GTest::gtest_main) 24 | 25 | add_custom_command( 26 | TARGET tests POST_BUILD 27 | COMMAND ${CMAKE_COMMAND} -E copy 28 | ${CMAKE_CURRENT_SOURCE_DIR}/example.ini 29 | ${CMAKE_CURRENT_SOURCE_DIR}/tests.ini 30 | ${CMAKE_CURRENT_BINARY_DIR}) 31 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CXX?=g++ 2 | CXXFLAGS+=-Wall -std=c++14 `pkg-config --cflags gtest_main` 3 | LDFLAGS+=`pkg-config --libs gtest_main` 4 | 5 | OBJS=ts-roundtrip.o ts-snippets.o ts-utf8.o ts-bugfix.o ts-quotes.o ts-noconvert.o 6 | 7 | BIN=./tests 8 | 9 | all: $(BIN) 10 | 11 | $(BIN): $(OBJS) 12 | $(CXX) -o $(BIN) $(OBJS) $(LDFLAGS) 13 | 14 | clean: 15 | rm -f core $(OBJS) $(BIN) 16 | 17 | test: $(BIN) 18 | $(BIN) 19 | 20 | $(OBJS): ../SimpleIni.h 21 | 22 | -------------------------------------------------------------------------------- /tests/example.ini: -------------------------------------------------------------------------------- 1 | [section] 2 | key = value 3 | -------------------------------------------------------------------------------- /tests/old/test.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | Debug\testsi.exe -u -m -l test1-input.ini > test1-blah.ini 4 | fc test1-expected.ini test1-output.ini 5 | if errorlevel 1 goto error 6 | 7 | "Debug Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini 8 | fc test1-expected.ini test1-output.ini 9 | if errorlevel 1 goto error 10 | 11 | Release\testsi.exe -u -m -l test1-input.ini > test1-blah.ini 12 | fc test1-expected.ini test1-output.ini 13 | if errorlevel 1 goto error 14 | 15 | "Release Unicode\testsi.exe" -u -m -l test1-input.ini > test1-blah.ini 16 | fc test1-expected.ini test1-output.ini 17 | if errorlevel 1 goto error 18 | 19 | exit /b 0 20 | 21 | :error 22 | echo Failed during test run. Output file doesn't match expected file. 23 | pause 24 | exit /b 1 25 | -------------------------------------------------------------------------------- /tests/old/test1-expected.ini: -------------------------------------------------------------------------------- 1 | ; testsi-UTF8-std.ini : standard UTF-8 test file for SimpleIni automated testing 2 | ; 3 | ; The number after a section or key is the order that it is defined in this file 4 | ; to make it easier to see if it has been written out correctly. This file should 5 | ; be loaded with Unicode / MultiKey / MultiLine turned on. 6 | 7 | 8 | 9 | ; This comment should be joined on to the one below it about the key 10 | ; with no section. 11 | 12 | ; Key with no section 13 | lonely-key = nosection 14 | another = nosection either 15 | 16 | ; This key has no value 17 | empty = 18 | 19 | 20 | ; This should be joined with the comment below about japanese. 21 | ; Another line which will be un-indented. 22 | 23 | ; This is a section of keys showing the word Japanese in different syllabies. 24 | [ordered-1] 25 | a-1 = blah 26 | 27 | ; this is in kanji 28 | japanese-2 = 日本語 29 | 30 | ; this is in hiragana 31 | japanese-3 = にほんご 32 | 33 | ; this is in katakana 34 | japanese-4 = ニホンゴ 35 | 36 | ; this is in romaji 37 | japanese-5 = nihongo 38 | 39 | ; kanji as the key 40 | 日本語-6 = japanese 41 | 42 | 43 | [multi-2] 44 | 45 | ; value a 46 | test = a 47 | 48 | ; value b 49 | test = b 50 | 51 | ; value c 52 | test = c 53 | 54 | ; value d 55 | test = d 56 | 57 | 58 | [multiline-3] 59 | 60 | ; This is obviously a multi-line entry 61 | multiline-1 = << 4 | // Source: http://code.jellycan.com/simpleini/ 5 | // 6 | // Automated testing for SimpleIni streams 7 | 8 | #ifdef _WIN32 9 | # pragma warning(disable: 4786) 10 | #endif 11 | 12 | #ifdef _WIN32 13 | # include 14 | # define DELETE_FILE DeleteFileA 15 | #else 16 | # include 17 | # define DELETE_FILE unlink 18 | #endif 19 | #include 20 | 21 | #define SI_SUPPORT_IOSTREAMS 22 | #include "SimpleIni.h" 23 | 24 | class Test 25 | { 26 | std::string m_strTest; 27 | 28 | public: 29 | Test(const char * a_pszName) 30 | : m_strTest(a_pszName) 31 | { 32 | printf("%s: test starting\n", m_strTest.c_str()); 33 | } 34 | 35 | bool Success() 36 | { 37 | printf("%s: test succeeded\n", m_strTest.c_str()); 38 | return false; 39 | } 40 | 41 | bool Failure(const char * pszReason) 42 | { 43 | printf("%s: test FAILED (%s)\n", m_strTest.c_str(), pszReason); 44 | return false; 45 | } 46 | }; 47 | 48 | bool FileComparisonTest(const char * a_pszFile1, const char * a_pszFile2) { 49 | // ensure that the two files are the same 50 | try { 51 | std::string strFile1, strFile2; 52 | 53 | char szBuf[1024]; 54 | FILE * fp = NULL; 55 | 56 | #if __STDC_WANT_SECURE_LIB__ 57 | fopen_s(&fp, a_pszFile1, "rb"); 58 | #else 59 | fp = fopen(a_pszFile1, "rb"); 60 | #endif 61 | if (!fp) throw false; 62 | while (!feof(fp)) { 63 | size_t n = fread(szBuf, 1, sizeof(szBuf), fp); 64 | strFile1.append(szBuf, n); 65 | } 66 | fclose(fp); 67 | 68 | fp = NULL; 69 | #if __STDC_WANT_SECURE_LIB__ 70 | fopen_s(&fp, a_pszFile2, "rb"); 71 | #else 72 | fp = fopen(a_pszFile2, "rb"); 73 | #endif 74 | if (!fp) throw false; 75 | while (!feof(fp)) { 76 | size_t n = fread(szBuf, 1, sizeof(szBuf), fp); 77 | strFile2.append(szBuf, n); 78 | } 79 | fclose(fp); 80 | 81 | if (strFile1 != strFile2) throw false; 82 | } 83 | catch (...) { 84 | return false; 85 | } 86 | 87 | return true; 88 | } 89 | 90 | bool FileLoadTest(const char * a_pszFile1, const char * a_pszFile2) { 91 | // ensure that the two files load into simpleini the same 92 | CSimpleIniA ini(true, true, true); 93 | bool b; 94 | try { 95 | ini.Reset(); 96 | if (ini.LoadFile(a_pszFile1) < 0) throw "Load failed for file 1"; 97 | if (ini.SaveFile("test1.ini") < 0) throw "Save failed for file 1"; 98 | 99 | ini.Reset(); 100 | if (ini.LoadFile(a_pszFile2) < 0) throw "Load failed for file 2"; 101 | if (ini.SaveFile("test2.ini") < 0) throw "Save failed for file 2"; 102 | 103 | b = FileComparisonTest("test1.ini", "test2.ini"); 104 | DELETE_FILE("test1.ini"); 105 | DELETE_FILE("test2.ini"); 106 | if (!b) throw "File comparison failed in FileLoadTest"; 107 | } 108 | catch (...) { 109 | return false; 110 | } 111 | 112 | return true; 113 | } 114 | 115 | bool TestStreams() 116 | { 117 | const char * rgszTestFile[3] = { 118 | "test1-input.ini", 119 | "test1-output.ini", 120 | "test1-expected.ini" 121 | }; 122 | 123 | Test oTest("TestStreams"); 124 | 125 | CSimpleIniW ini; 126 | ini.SetUnicode(true); 127 | ini.SetMultiKey(true); 128 | ini.SetMultiLine(true); 129 | 130 | // load the file 131 | try { 132 | std::ifstream instream; 133 | instream.open(rgszTestFile[0], std::ifstream::in | std::ifstream::binary); 134 | if (ini.LoadData(instream) < 0) throw false; 135 | instream.close(); 136 | } 137 | catch (...) { 138 | return oTest.Failure("Failed to load file"); 139 | } 140 | 141 | // standard contents test 142 | //if (!StandardContentsTest(ini, oTest)) { 143 | // return false; 144 | //} 145 | 146 | // save the file 147 | try { 148 | std::ofstream outfile; 149 | outfile.open(rgszTestFile[1], std::ofstream::out | std::ofstream::binary); 150 | if (ini.Save(outfile, true) < 0) throw false; 151 | outfile.close(); 152 | } 153 | catch (...) { 154 | return oTest.Failure("Failed to save file"); 155 | } 156 | 157 | // file comparison test 158 | if (!FileComparisonTest(rgszTestFile[1], rgszTestFile[2])) { 159 | return oTest.Failure("Failed file comparison"); 160 | } 161 | if (!FileLoadTest(rgszTestFile[1], rgszTestFile[2])) { 162 | return oTest.Failure("Failed file load comparison"); 163 | } 164 | 165 | return oTest.Success(); 166 | } 167 | -------------------------------------------------------------------------------- /tests/old/testsi-EUCJP.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brofield/simpleini/6048871ea9ee0ec24be5bd099d161a10567d7dc2/tests/old/testsi-EUCJP.ini -------------------------------------------------------------------------------- /tests/old/testsi-SJIS.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brofield/simpleini/6048871ea9ee0ec24be5bd099d161a10567d7dc2/tests/old/testsi-SJIS.ini -------------------------------------------------------------------------------- /tests/old/testsi-UTF8.ini: -------------------------------------------------------------------------------- 1 | ; test file for SimpleIni 2 | 3 | whitespace = ok 4 | nosection=ok 5 | NOSECTION=still ok 6 | 7 | [standard] 8 | foo=foo1 9 | standard-1=foo 10 | 日本語=ok1 11 | 12 | [Standard] 13 | Foo=foo2 14 | standard-2=foo 15 | 日本語=ok2 16 | 17 | [ Whitespace ] 18 | 19 | a= 20 | 21 | [ whitespace in section name ] 22 | whitespace in key name = whitespace in value name 23 | 24 | ; comments 25 | ; more comments 26 | 27 | invalid 28 | =invalid 29 | ====invalid 30 | 31 | [Japanese] 32 | nihongo = 日本語 33 | 日本語 = 日本語 34 | 35 | [日本語] 36 | nihongo = 日本語 37 | 日本語 = 日本語 38 | 39 | [] 40 | more=no section name 41 | 42 | [MultiLine] 43 | single = This is a single line. 44 | multi = << 4 | // Source: http://code.jellycan.com/simpleini/ 5 | // 6 | // Demo of usage 7 | 8 | #ifdef _WIN32 9 | # pragma warning(disable: 4786) 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define SI_SUPPORT_IOSTREAMS 17 | #if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) 18 | # include 19 | #endif 20 | 21 | //#define SI_CONVERT_GENERIC 22 | //#define SI_CONVERT_ICU 23 | //#define SI_CONVERT_WIN32 24 | #include "../SimpleIni.h" 25 | 26 | #ifdef SI_CONVERT_ICU 27 | // if converting using ICU then we need the ICU library 28 | # pragma comment(lib, "icuuc.lib") 29 | #endif 30 | 31 | #ifdef _WIN32 32 | # include 33 | #else // !_WIN32 34 | # define TCHAR char 35 | # define _T(x) x 36 | # define _tprintf printf 37 | # define _tmain main 38 | #endif // _WIN32 39 | 40 | static void 41 | Test( 42 | CSimpleIni & ini 43 | ) 44 | { 45 | const TCHAR *pszSection = 0; 46 | const TCHAR *pItem = 0; 47 | const TCHAR *pszVal = 0; 48 | 49 | // get the value of the key "foo" in section "standard" 50 | bool bHasMulti; 51 | pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti); 52 | _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), 53 | pszVal ? pszVal : _T("(null)"), bHasMulti); 54 | 55 | // set the value of the key "foo" in section "standard" 56 | ini.SetValue(_T("standard"), _T("foo"), _T("wibble")); 57 | pszVal = ini.GetValue(_T("standard"), _T("foo"), 0, &bHasMulti); 58 | _tprintf(_T("\n-- Value of standard::foo is '%s' (hasMulti = %d)\n"), 59 | pszVal ? pszVal : _T("(null)"), bHasMulti); 60 | 61 | // get all values of the key "foo" in section "standard" 62 | CSimpleIni::TNamesDepend values; 63 | if (ini.GetAllValues(_T("standard"), _T("foo"), values)) { 64 | _tprintf(_T("\n-- Values of standard::foo are:\n")); 65 | CSimpleIni::TNamesDepend::const_iterator i = values.begin(); 66 | for (; i != values.end(); ++i) { 67 | pszVal = i->pItem; 68 | _tprintf(_T(" -> '%s'\n"), pszVal); 69 | } 70 | } 71 | 72 | // get the size of the section [standard] 73 | _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), 74 | ini.GetSectionSize(_T("standard"))); 75 | 76 | // delete the key "foo" in section "standard", if it has value "bar" 77 | ini.DeleteValue(_T("standard"), _T("foo"), _T("bar")); 78 | pszVal = ini.GetValue(_T("standard"), _T("foo"), 0); 79 | _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), 80 | pszVal ? pszVal : _T("(null)")); 81 | 82 | // delete the key "foo" in section "standard" 83 | ini.Delete(_T("standard"), _T("foo")); 84 | pszVal = ini.GetValue(_T("standard"), _T("foo"), 0); 85 | _tprintf(_T("\n-- Value of standard::foo is now '%s'\n"), 86 | pszVal ? pszVal : _T("(null)")); 87 | 88 | // get the size of the section [standard] 89 | _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), 90 | ini.GetSectionSize(_T("standard"))); 91 | 92 | // get the list of all key names for the section "standard" 93 | _tprintf(_T("\n-- Dumping keys of section: [standard]\n")); 94 | CSimpleIni::TNamesDepend keys; 95 | ini.GetAllKeys(_T("standard"), keys); 96 | 97 | // dump all of the key names 98 | CSimpleIni::TNamesDepend::const_iterator iKey = keys.begin(); 99 | for ( ; iKey != keys.end(); ++iKey ) { 100 | pItem = iKey->pItem; 101 | _tprintf(_T("Key: %s\n"), pItem); 102 | } 103 | 104 | // add a decimal value 105 | ini.SetLongValue(_T("integer"), _T("dec"), 42, NULL, false); 106 | ini.SetLongValue(_T("integer"), _T("hex"), 42, NULL, true); 107 | 108 | // add some bool values 109 | ini.SetBoolValue(_T("bool"), _T("t"), true); 110 | ini.SetBoolValue(_T("bool"), _T("f"), false); 111 | 112 | // get the values back 113 | assert(42 == ini.GetLongValue(_T("integer"), _T("dec"))); 114 | assert(42 == ini.GetLongValue(_T("integer"), _T("hex"))); 115 | assert(true == ini.GetBoolValue(_T("bool"), _T("t"))); 116 | assert(false == ini.GetBoolValue(_T("bool"), _T("f"))); 117 | 118 | // delete the section "standard" 119 | ini.Delete(_T("standard"), NULL); 120 | _tprintf(_T("\n-- Number of keys in section [standard] = %d\n"), 121 | ini.GetSectionSize(_T("standard"))); 122 | 123 | // iterate through every section in the file 124 | _tprintf(_T("\n-- Dumping all sections\n")); 125 | CSimpleIni::TNamesDepend sections; 126 | ini.GetAllSections(sections); 127 | CSimpleIni::TNamesDepend::const_iterator iSection = sections.begin(); 128 | for ( ; iSection != sections.end(); ++iSection ) { 129 | pszSection = iSection->pItem; 130 | 131 | // print the section name 132 | printf("\n"); 133 | if (*pszSection) { 134 | _tprintf(_T("[%s]\n"), pszSection); 135 | } 136 | 137 | // if there are keys and values... 138 | const CSimpleIni::TKeyVal * pSectionData = ini.GetSection(pszSection); 139 | if (pSectionData) { 140 | // iterate over all keys and dump the key name and value 141 | CSimpleIni::TKeyVal::const_iterator iKeyVal = pSectionData->begin(); 142 | for ( ;iKeyVal != pSectionData->end(); ++iKeyVal) { 143 | pItem = iKeyVal->first.pItem; 144 | pszVal = iKeyVal->second; 145 | _tprintf(_T("%s=%s\n"), pItem, pszVal); 146 | } 147 | } 148 | } 149 | } 150 | 151 | #if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) 152 | static bool 153 | TestStreams( 154 | const TCHAR * a_pszFile, 155 | bool a_bIsUtf8, 156 | bool a_bUseMultiKey, 157 | bool a_bUseMultiLine 158 | ) 159 | { 160 | // load the file 161 | CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); 162 | _tprintf(_T("Loading file: %s\n"), a_pszFile); 163 | std::ifstream instream; 164 | instream.open(a_pszFile, std::ifstream::in | std::ifstream::binary); 165 | SI_Error rc = ini.LoadData(instream); 166 | instream.close(); 167 | if (rc < 0) { 168 | printf("Failed to open file.\n"); 169 | return false; 170 | } 171 | 172 | Test(ini); 173 | 174 | // save the file (simple) 175 | _tprintf(_T("\n-- Saving file to: testsi-out-streams.ini\n")); 176 | std::ofstream outstream; 177 | outstream.open("testsi-out-streams.ini", std::ofstream::out | std::ofstream::binary); 178 | ini.Save(outstream); 179 | outstream.close(); 180 | 181 | return true; 182 | } 183 | #endif // SI_SUPPORT_IOSTREAMS 184 | 185 | static bool 186 | TestFile( 187 | const TCHAR * a_pszFile, 188 | bool a_bIsUtf8, 189 | bool a_bUseMultiKey, 190 | bool a_bUseMultiLine 191 | ) 192 | { 193 | // load the file 194 | CSimpleIni ini(a_bIsUtf8, a_bUseMultiKey, a_bUseMultiLine); 195 | _tprintf(_T("Loading file: %s\n"), a_pszFile); 196 | SI_Error rc = ini.LoadFile(a_pszFile); 197 | if (rc < 0) { 198 | printf("Failed to open file.\n"); 199 | return false; 200 | } 201 | 202 | // run the tests 203 | Test(ini); 204 | 205 | // save the file (simple) 206 | _tprintf(_T("\n-- Saving file to: testsi-out.ini\n")); 207 | ini.SaveFile("testsi-out.ini"); 208 | 209 | // save the file (with comments) 210 | // Note: to save the file and add a comment to the beginning, use 211 | // code such as the following. 212 | _tprintf(_T("\n-- Saving file to: testsi-out-comment.ini\n")); 213 | FILE * fp = NULL; 214 | #if __STDC_WANT_SECURE_LIB__ 215 | fopen_s(&fp, "testsi-out-comment.ini", "wb"); 216 | #else 217 | fp = fopen("testsi-out-comment.ini", "wb"); 218 | #endif 219 | if (fp) { 220 | CSimpleIni::FileWriter writer(fp); 221 | if (a_bIsUtf8) { 222 | writer.Write(SI_UTF8_SIGNATURE); 223 | } 224 | 225 | // add a string to the file in the correct text format 226 | CSimpleIni::Converter convert = ini.GetConverter(); 227 | convert.ConvertToStore(_T("; output from testsi.cpp test program") 228 | SI_NEWLINE SI_NEWLINE); 229 | writer.Write(convert.Data()); 230 | 231 | ini.Save(writer, false); 232 | fclose(fp); 233 | } 234 | 235 | return true; 236 | } 237 | 238 | static bool 239 | ParseCommandLine( 240 | int argc, 241 | TCHAR * argv[], 242 | const TCHAR * & a_pszFile, 243 | bool & a_bIsUtf8, 244 | bool & a_bUseMultiKey, 245 | bool & a_bUseMultiLine 246 | ) 247 | { 248 | a_pszFile = 0; 249 | a_bIsUtf8 = false; 250 | a_bUseMultiKey = false; 251 | a_bUseMultiLine = false; 252 | for (--argc; argc > 0; --argc) { 253 | if (argv[argc][0] == '-') { 254 | switch (argv[argc][1]) { 255 | case TCHAR('u'): 256 | a_bIsUtf8 = true; 257 | break; 258 | case TCHAR('m'): 259 | a_bUseMultiKey = true; 260 | break; 261 | case TCHAR('l'): 262 | a_bUseMultiLine = true; 263 | break; 264 | } 265 | } 266 | else { 267 | a_pszFile = argv[argc]; 268 | } 269 | } 270 | if (!a_pszFile) { 271 | _tprintf( 272 | _T("Usage: testsi [-u] [-m] [-l] iniFile\n") 273 | _T(" -u Load file as UTF-8 (Default is to use system locale)\n") 274 | _T(" -m Enable multiple keys\n") 275 | _T(" -l Enable multiple line values\n") 276 | ); 277 | return false; 278 | } 279 | 280 | return true; 281 | } 282 | 283 | extern bool TestStreams(); 284 | 285 | int 286 | _tmain( 287 | int argc, 288 | TCHAR * argv[] 289 | ) 290 | { 291 | setlocale(LC_ALL, ""); 292 | 293 | // start of automated testing... 294 | TestStreams(); 295 | 296 | // parse the command line 297 | const TCHAR * pszFile; 298 | bool bIsUtf8, bUseMultiKey, bUseMultiLine; 299 | if (!ParseCommandLine(argc, argv, pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { 300 | return 1; 301 | } 302 | 303 | // run the test 304 | if (!TestFile(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { 305 | return 1; 306 | } 307 | #if defined(SI_SUPPORT_IOSTREAMS) && !defined(_UNICODE) 308 | if (!TestStreams(pszFile, bIsUtf8, bUseMultiKey, bUseMultiLine)) { 309 | return 1; 310 | } 311 | #endif 312 | 313 | return 0; 314 | } 315 | 316 | -------------------------------------------------------------------------------- /tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/pch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // pch.cpp 3 | // Include the standard header and generate the precompiled header. 4 | // 5 | 6 | #include "pch.h" 7 | -------------------------------------------------------------------------------- /tests/pch.h: -------------------------------------------------------------------------------- 1 | // 2 | // pch.h 3 | // Header for standard system include files. 4 | // 5 | 6 | #pragma once 7 | 8 | #include "gtest/gtest.h" 9 | -------------------------------------------------------------------------------- /tests/tests.ini: -------------------------------------------------------------------------------- 1 | [section1] 2 | key1=value1 3 | 4 | [section2] 5 | test2=テスト2 6 | テスト=test 7 | テスト2=テスト二 8 | 9 | [検査] 10 | key2=value2 11 | test2=テスト2 12 | テスト=test 13 | テスト2=テスト二 14 | -------------------------------------------------------------------------------- /tests/tests.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {8f30a5dc-b942-4c9a-ba75-91c906ff85fa} 15 | Win32Proj 16 | 10.0 17 | Application 18 | v143 19 | Unicode 20 | tests 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Create 40 | Create 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Use 58 | pch.h 59 | Disabled 60 | X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 61 | EnableFastChecks 62 | MultiThreadedDebugDLL 63 | Level4 64 | 65 | 66 | true 67 | Console 68 | 69 | 70 | 71 | 72 | Use 73 | pch.h 74 | X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | MultiThreadedDLL 76 | Level3 77 | ProgramDatabase 78 | 79 | 80 | true 81 | Console 82 | true 83 | true 84 | 85 | 86 | 87 | 88 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tests/tests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Test Cases 6 | 7 | 8 | Test Cases 9 | 10 | 11 | Test Cases 12 | 13 | 14 | Test Cases 15 | 16 | 17 | Test Cases 18 | 19 | 20 | Test Cases 21 | 22 | 23 | Test Cases 24 | 25 | 26 | Other Files 27 | 28 | 29 | 30 | 31 | Other Files 32 | 33 | 34 | Test Cases 35 | 36 | 37 | Other Files 38 | 39 | 40 | 41 | 42 | 43 | {8b76c482-decd-405d-abd8-98726f5c0b03} 44 | ts-*.cpp 45 | 46 | 47 | {8f23fd84-666d-4e92-83de-ed431f244af0} 48 | 49 | 50 | 51 | 52 | Other Files 53 | 54 | 55 | -------------------------------------------------------------------------------- /tests/ts-bugfix.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../SimpleIni.h" 3 | 4 | TEST(TestBugFix, TestEmptySection) { 5 | CSimpleIniA ini; 6 | ini.SetValue("foo", "skey", "sval"); 7 | ini.SetValue("", "rkey", "rval"); 8 | ini.SetValue("bar", "skey", "sval"); 9 | 10 | std::string output; 11 | ini.Save(output); 12 | 13 | std::string expected = 14 | "rkey = rval\n" 15 | "\n" 16 | "\n" 17 | "[foo]\n" 18 | "skey = sval\n" 19 | "\n" 20 | "\n" 21 | "[bar]\n" 22 | "skey = sval\n"; 23 | 24 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 25 | ASSERT_STREQ(expected.c_str(), output.c_str()); 26 | } 27 | 28 | TEST(TestBugFix, TestMultiLineIgnoreTrailSpace0) { 29 | std::string input = 30 | "; multiline values\n" 31 | "key = << 3 | #include "../SimpleIni.h" 4 | 5 | class TestQuotes : public ::testing::Test { 6 | protected: 7 | void SetUp() override; 8 | 9 | protected: 10 | CSimpleIniA ini; 11 | std::string input; 12 | std::string expect; 13 | std::string output; 14 | }; 15 | 16 | void TestQuotes::SetUp() { 17 | ini.SetUnicode(); 18 | } 19 | 20 | TEST_F(TestQuotes, TestEmpty) { 21 | ini.SetQuotes(true); 22 | 23 | input = 24 | "[section]\n" 25 | "key1 = \"\"\n" 26 | "key2 = \n" 27 | ; 28 | 29 | // no need to preserve quotes for empty data 30 | expect = 31 | "[section]\n" 32 | "key1 = \n" 33 | "key2 = \n" 34 | ; 35 | 36 | const char* result; 37 | SI_Error rc = ini.LoadData(input); 38 | ASSERT_EQ(rc, SI_OK); 39 | 40 | result = ini.GetValue("section", "key1"); 41 | ASSERT_STREQ(result, ""); 42 | 43 | result = ini.GetValue("section", "key2"); 44 | ASSERT_STREQ(result, ""); 45 | 46 | rc = ini.Save(output); 47 | ASSERT_EQ(rc, SI_OK); 48 | 49 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 50 | ASSERT_STREQ(expect.c_str(), output.c_str()); 51 | } 52 | 53 | TEST_F(TestQuotes, TestEmptyDisabled) { 54 | ini.SetQuotes(false); 55 | 56 | input = 57 | "[section]\n" 58 | "key1 = \"\"\n" 59 | "key2 = \n" 60 | ; 61 | 62 | const char* result; 63 | SI_Error rc = ini.LoadData(input); 64 | ASSERT_EQ(rc, SI_OK); 65 | 66 | result = ini.GetValue("section", "key1"); 67 | ASSERT_STREQ(result, "\"\""); 68 | 69 | result = ini.GetValue("section", "key2"); 70 | ASSERT_STREQ(result, ""); 71 | 72 | rc = ini.Save(output); 73 | ASSERT_EQ(rc, SI_OK); 74 | 75 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 76 | ASSERT_STREQ(input.c_str(), output.c_str()); 77 | } 78 | 79 | TEST_F(TestQuotes, TestGeneral) { 80 | ini.SetQuotes(true); 81 | 82 | input = 83 | "[section]\n" 84 | "key1 = foo\n" 85 | "key2 = \"foo\"\n" 86 | "key3 = foo \n" 87 | "key4 = \" foo \"\n" 88 | "key5 = \"foo\n" 89 | "key6 = foo\"\n" 90 | "key7 = foo \" foo \n" 91 | "key8 = \" foo \" foo \" \n" 92 | ; 93 | 94 | expect = 95 | "[section]\n" 96 | "key1 = foo\n" 97 | "key2 = foo\n" 98 | "key3 = foo\n" 99 | "key4 = \" foo \"\n" 100 | "key5 = \"foo\n" 101 | "key6 = foo\"\n" 102 | "key7 = foo \" foo\n" 103 | "key8 = \" foo \" foo \"\n" 104 | ; 105 | 106 | const char* result; 107 | SI_Error rc = ini.LoadData(input); 108 | ASSERT_EQ(rc, SI_OK); 109 | 110 | result = ini.GetValue("section", "key1"); 111 | ASSERT_STREQ(result, "foo"); 112 | 113 | result = ini.GetValue("section", "key2"); 114 | ASSERT_STREQ(result, "foo"); 115 | 116 | result = ini.GetValue("section", "key3"); 117 | ASSERT_STREQ(result, "foo"); 118 | 119 | result = ini.GetValue("section", "key4"); 120 | ASSERT_STREQ(result, " foo "); 121 | 122 | result = ini.GetValue("section", "key5"); 123 | ASSERT_STREQ(result, "\"foo"); 124 | 125 | result = ini.GetValue("section", "key6"); 126 | ASSERT_STREQ(result, "foo\""); 127 | 128 | result = ini.GetValue("section", "key7"); 129 | ASSERT_STREQ(result, "foo \" foo"); 130 | 131 | result = ini.GetValue("section", "key8"); 132 | ASSERT_STREQ(result, " foo \" foo "); 133 | 134 | rc = ini.Save(output); 135 | ASSERT_EQ(rc, SI_OK); 136 | 137 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 138 | ASSERT_STREQ(expect.c_str(), output.c_str()); 139 | } 140 | 141 | TEST_F(TestQuotes, TestGeneralDisabled) { 142 | ini.SetQuotes(false); 143 | 144 | input = 145 | "[section]\n" 146 | "key1 = foo\n" 147 | "key2 = \"foo\"\n" 148 | "key3 = foo \n" 149 | "key4 = \" foo \"\n" 150 | "key5 = \"foo\n" 151 | "key6 = foo\"\n" 152 | "key7 = foo \" foo \n" 153 | "key8 = \" foo \" foo \" \n" 154 | ; 155 | 156 | expect = 157 | "[section]\n" 158 | "key1 = foo\n" 159 | "key2 = \"foo\"\n" 160 | "key3 = foo\n" 161 | "key4 = \" foo \"\n" 162 | "key5 = \"foo\n" 163 | "key6 = foo\"\n" 164 | "key7 = foo \" foo\n" 165 | "key8 = \" foo \" foo \"\n" 166 | ; 167 | 168 | const char* result; 169 | SI_Error rc = ini.LoadData(input); 170 | ASSERT_EQ(rc, SI_OK); 171 | 172 | rc = ini.Save(output); 173 | ASSERT_EQ(rc, SI_OK); 174 | 175 | result = ini.GetValue("section", "key1"); 176 | ASSERT_STREQ(result, "foo"); 177 | 178 | result = ini.GetValue("section", "key2"); 179 | ASSERT_STREQ(result, "\"foo\""); 180 | 181 | result = ini.GetValue("section", "key3"); 182 | ASSERT_STREQ(result, "foo"); 183 | 184 | result = ini.GetValue("section", "key4"); 185 | ASSERT_STREQ(result, "\" foo \""); 186 | 187 | result = ini.GetValue("section", "key5"); 188 | ASSERT_STREQ(result, "\"foo"); 189 | 190 | result = ini.GetValue("section", "key6"); 191 | ASSERT_STREQ(result, "foo\""); 192 | 193 | result = ini.GetValue("section", "key7"); 194 | ASSERT_STREQ(result, "foo \" foo"); 195 | 196 | result = ini.GetValue("section", "key8"); 197 | ASSERT_STREQ(result, "\" foo \" foo \""); 198 | 199 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 200 | ASSERT_STREQ(expect.c_str(), output.c_str()); 201 | } 202 | 203 | -------------------------------------------------------------------------------- /tests/ts-roundtrip.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include 3 | #include "../SimpleIni.h" 4 | 5 | class TestRoundTrip : public ::testing::Test { 6 | protected: 7 | void SetUp() override; 8 | void TestMulti(); 9 | void TestBOM(bool useBOM); 10 | 11 | protected: 12 | CSimpleIniA ini; 13 | std::string input; 14 | std::string output; 15 | }; 16 | 17 | void TestRoundTrip::SetUp() { 18 | ini.SetUnicode(); 19 | } 20 | 21 | TEST_F(TestRoundTrip, TestStandard) { 22 | input = 23 | "; File comment\n" 24 | "\n" 25 | "\n" 26 | "; Section 1 comment\n" 27 | "[section1]\n" 28 | "\n" 29 | "\n" 30 | "; Section 2 comment\n" 31 | "[section2]\n" 32 | "\n" 33 | "; key1 comment\n" 34 | "key1 = string\n" 35 | "\n" 36 | "; key 2 comment\n" 37 | "key2 = true\n" 38 | "key3 = 3.1415\n" 39 | ; 40 | 41 | SI_Error rc = ini.LoadData(input); 42 | ASSERT_EQ(rc, SI_OK); 43 | 44 | const char* result = ini.GetValue("section2", "key1"); 45 | ASSERT_STREQ(result, "string"); 46 | 47 | rc = ini.Save(output); 48 | ASSERT_EQ(rc, SI_OK); 49 | 50 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 51 | ASSERT_STREQ(input.c_str(), output.c_str()); 52 | } 53 | 54 | void TestRoundTrip::TestMulti() { 55 | input = 56 | "[section]\n" 57 | "key = string1\n" 58 | "key = string2\n" 59 | ; 60 | 61 | SI_Error rc = ini.LoadData(input); 62 | ASSERT_EQ(rc, SI_OK); 63 | 64 | rc = ini.Save(output); 65 | ASSERT_EQ(rc, SI_OK); 66 | 67 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 68 | } 69 | 70 | TEST_F(TestRoundTrip, TestMultiGood) { 71 | ini.SetMultiKey(true); 72 | TestMulti(); 73 | ASSERT_STREQ(input.c_str(), output.c_str()); 74 | } 75 | 76 | TEST_F(TestRoundTrip, TestMultiBad) { 77 | std::string expected = 78 | "[section]\n" 79 | "key = string2\n"; 80 | 81 | ini.SetMultiKey(false); 82 | TestMulti(); 83 | ASSERT_STRNE(input.c_str(), output.c_str()); 84 | ASSERT_STREQ(expected.c_str(), output.c_str()); 85 | } 86 | 87 | TEST_F(TestRoundTrip, TestSpacesTrue) { 88 | input = 89 | "[section]\n" 90 | "key = string1\n"; 91 | 92 | SI_Error rc = ini.LoadData(input); 93 | ASSERT_EQ(rc, SI_OK); 94 | 95 | ini.SetSpaces(true); 96 | rc = ini.Save(output); 97 | ASSERT_EQ(rc, SI_OK); 98 | 99 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 100 | 101 | ASSERT_STREQ(input.c_str(), output.c_str()); 102 | } 103 | 104 | TEST_F(TestRoundTrip, TestSpacesFalse) { 105 | input = 106 | "[section]\n" 107 | "key = string1\n"; 108 | 109 | SI_Error rc = ini.LoadData(input); 110 | ASSERT_EQ(rc, SI_OK); 111 | 112 | ini.SetSpaces(false); 113 | rc = ini.Save(output); 114 | ASSERT_EQ(rc, SI_OK); 115 | 116 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 117 | 118 | ASSERT_STRNE(input.c_str(), output.c_str()); 119 | 120 | std::string expected = 121 | "[section]\n" 122 | "key=string1\n"; 123 | 124 | ASSERT_STREQ(expected.c_str(), output.c_str()); 125 | } 126 | 127 | void TestRoundTrip::TestBOM(bool useBOM) { 128 | const char bom[] = "\xEF\xBB\xBF"; 129 | const char input8[] = 130 | u8"[テスト1]\n" 131 | u8"テスト2 = テスト3\n"; 132 | 133 | input = bom; 134 | input += input8; 135 | 136 | ini.Reset(); 137 | ini.SetUnicode(false); 138 | SI_Error rc = ini.LoadData(input); 139 | ASSERT_EQ(rc, SI_OK); 140 | 141 | const char tesuto1[] = u8"テスト1"; 142 | const char tesuto2[] = u8"テスト2"; 143 | const char tesuto3[] = u8"テスト3"; 144 | 145 | const char* result = ini.GetValue(tesuto1, tesuto2); 146 | ASSERT_STREQ(result, tesuto3); 147 | 148 | rc = ini.Save(output, useBOM); 149 | ASSERT_EQ(rc, SI_OK); 150 | 151 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 152 | } 153 | 154 | TEST_F(TestRoundTrip, TestWithBOM) { 155 | TestBOM(true); 156 | 157 | ASSERT_STREQ(input.c_str(), output.c_str()); 158 | } 159 | 160 | TEST_F(TestRoundTrip, TestWithoutBOM) { 161 | TestBOM(false); 162 | 163 | ASSERT_STRNE(input.c_str(), output.c_str()); 164 | 165 | std::string expected(input, 3); 166 | ASSERT_STREQ(expected.c_str(), output.c_str()); 167 | } 168 | 169 | TEST_F(TestRoundTrip, TestAllowKeyOnly1) { 170 | ini.SetAllowKeyOnly(false); 171 | 172 | input = 173 | "[section1]\n" 174 | "key1 = string\n" 175 | "key2 = \n" 176 | "key3= \n" 177 | "key4=\n" 178 | "key5\n" 179 | "\n" 180 | "Never going to give you up\n" 181 | "Never going to let you down\n" 182 | ; 183 | 184 | std::string expect = 185 | "[section1]\n" 186 | "key1 = string\n" 187 | "key2 = \n" 188 | "key3 = \n" 189 | "key4 = \n" 190 | ; 191 | 192 | SI_Error rc = ini.LoadData(input); 193 | ASSERT_EQ(rc, SI_OK); 194 | 195 | rc = ini.Save(output); 196 | ASSERT_EQ(rc, SI_OK); 197 | 198 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 199 | ASSERT_STREQ(expect.c_str(), output.c_str()); 200 | } 201 | 202 | TEST_F(TestRoundTrip, TestAllowKeyOnly2) { 203 | ini.SetAllowKeyOnly(true); 204 | 205 | input = 206 | "[section1]\n" 207 | "key1\n" 208 | "key2\n" 209 | "[section2]\n" 210 | "key1 = string\n" 211 | "key2 = \n" 212 | "key3= \n" 213 | "key4=\n" 214 | "\n" 215 | "key5\n" 216 | "\n" 217 | "Never going to give you up\n" 218 | "\n" 219 | "Never going to let you down\n" 220 | ; 221 | 222 | std::string expect = 223 | "[section1]\n" 224 | "key1\n" 225 | "key2\n" 226 | "\n\n" 227 | "[section2]\n" 228 | "key1 = string\n" 229 | "key2\n" 230 | "key3\n" 231 | "key4\n" 232 | "key5\n" 233 | "Never going to give you up\n" 234 | "Never going to let you down\n" 235 | ; 236 | 237 | SI_Error rc = ini.LoadData(input); 238 | ASSERT_EQ(rc, SI_OK); 239 | 240 | rc = ini.Save(output); 241 | ASSERT_EQ(rc, SI_OK); 242 | 243 | output.erase(std::remove(output.begin(), output.end(), '\r'), output.end()); 244 | ASSERT_STREQ(expect.c_str(), output.c_str()); 245 | } 246 | 247 | -------------------------------------------------------------------------------- /tests/ts-snippets.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../SimpleIni.h" 3 | 4 | 5 | // ### SIMPLE USAGE 6 | 7 | TEST(TestSnippets, TestSimple) { 8 | // simple demonstration 9 | 10 | CSimpleIniA ini; 11 | ini.SetUnicode(); 12 | 13 | SI_Error rc = ini.LoadFile("example.ini"); 14 | if (rc < 0) { /* handle error */ }; 15 | ASSERT_EQ(rc, SI_OK); 16 | 17 | const char* pv; 18 | pv = ini.GetValue("section", "key", "default"); 19 | ASSERT_STREQ(pv, "value"); 20 | 21 | ini.SetValue("section", "key", "newvalue"); 22 | 23 | pv = ini.GetValue("section", "key", "default"); 24 | ASSERT_STREQ(pv, "newvalue"); 25 | } 26 | 27 | 28 | // ### LOADING DATA 29 | 30 | TEST(TestSnippets, TestLoadFile) { 31 | // load from a data file 32 | CSimpleIniA ini; 33 | SI_Error rc = ini.LoadFile("example.ini"); 34 | if (rc < 0) { /* handle error */ }; 35 | ASSERT_EQ(rc, SI_OK); 36 | } 37 | 38 | TEST(TestSnippets, TestLoadString) { 39 | // load from a string 40 | const std::string example = "[section]\nkey = value\n"; 41 | CSimpleIniA ini; 42 | SI_Error rc = ini.LoadData(example); 43 | if (rc < 0) { /* handle error */ }; 44 | ASSERT_EQ(rc, SI_OK); 45 | } 46 | 47 | 48 | // ### GETTING SECTIONS AND KEYS 49 | 50 | TEST(TestSnippets, TestSectionsAndKeys) { 51 | const std::string example = 52 | "[section1]\n" 53 | "key1 = value1\n" 54 | "key2 = value2\n" 55 | "\n" 56 | "[section2]\n" 57 | "[section3]\n"; 58 | 59 | CSimpleIniA ini; 60 | SI_Error rc = ini.LoadData(example); 61 | ASSERT_EQ(rc, SI_OK); 62 | 63 | 64 | 65 | // get all sections 66 | CSimpleIniA::TNamesDepend sections; 67 | ini.GetAllSections(sections); 68 | 69 | // get all keys in a section 70 | CSimpleIniA::TNamesDepend keys; 71 | ini.GetAllKeys("section1", keys); 72 | 73 | 74 | 75 | const char* expectedSections[] = { "section1", "section2", "section3", nullptr }; 76 | const char* expectedKeys[] = { "key1", "key2", nullptr }; 77 | 78 | CSimpleIniA::TNamesDepend::const_iterator it; 79 | int i; 80 | 81 | for (i = 0, it = sections.begin(); it != sections.end(); ++i, ++it) { 82 | ASSERT_NE(expectedSections[i], nullptr); 83 | ASSERT_STREQ(expectedSections[i], it->pItem); 84 | } 85 | ASSERT_EQ(expectedSections[i], nullptr); 86 | 87 | for (i = 0, it = keys.begin(); it != keys.end(); ++i, ++it) { 88 | ASSERT_NE(expectedKeys[i], nullptr); 89 | ASSERT_STREQ(expectedKeys[i], it->pItem); 90 | } 91 | ASSERT_EQ(expectedKeys[i], nullptr); 92 | } 93 | 94 | 95 | // ### GETTING VALUES 96 | 97 | TEST(TestSnippets, TestGettingValues) { 98 | const std::string example = 99 | "[section1]\n" 100 | "key1 = value1\n" 101 | "key2 = value2.1\n" 102 | "key2 = value2.2\n" 103 | "\n" 104 | "[section2]\n" 105 | "[section3]\n"; 106 | 107 | bool utf8 = true; 108 | bool multiKey = true; 109 | CSimpleIniA ini(utf8, multiKey); 110 | SI_Error rc = ini.LoadData(example); 111 | ASSERT_EQ(rc, SI_OK); 112 | 113 | 114 | // get the value of a key that doesn't exist 115 | const char* pv; 116 | pv = ini.GetValue("section1", "key99"); 117 | ASSERT_EQ(pv, nullptr); 118 | 119 | // get the value of a key that does exist 120 | pv = ini.GetValue("section1", "key1"); 121 | ASSERT_STREQ(pv, "value1"); 122 | 123 | // get the value of a key which may have multiple 124 | // values. If hasMultiple is true, then there are 125 | // multiple values and just one value has been returned 126 | bool hasMulti; 127 | pv = ini.GetValue("section1", "key1", nullptr, &hasMulti); 128 | ASSERT_STREQ(pv, "value1"); 129 | ASSERT_EQ(hasMulti, false); 130 | 131 | pv = ini.GetValue("section1", "key2", nullptr, &hasMulti); 132 | ASSERT_STREQ(pv, "value2.1"); 133 | ASSERT_EQ(hasMulti, true); 134 | 135 | // get all values of a key with multiple values 136 | CSimpleIniA::TNamesDepend values; 137 | ini.GetAllValues("section1", "key2", values); 138 | 139 | // sort the values into a known order, in this case we want 140 | // the original load order 141 | values.sort(CSimpleIniA::Entry::LoadOrder()); 142 | 143 | // output all of the items 144 | CSimpleIniA::TNamesDepend::const_iterator it; 145 | for (it = values.begin(); it != values.end(); ++it) { 146 | //printf("value = '%s'\n", it->pItem); 147 | } 148 | 149 | 150 | int i; 151 | const char* expectedValues[] = { "value2.1", "value2.2", nullptr }; 152 | for (i = 0, it = values.begin(); it != values.end(); ++it, ++i) { 153 | ASSERT_NE(expectedValues[i], nullptr); 154 | ASSERT_STREQ(expectedValues[i], it->pItem); 155 | } 156 | ASSERT_EQ(expectedValues[i], nullptr); 157 | } 158 | 159 | // ### VALUE EXISTS 160 | 161 | TEST(TestSnippets, TestExists) 162 | { 163 | const std::string example = 164 | "[section1]\n" 165 | "key1 = value1\n" 166 | "key2 = value2.1\n" 167 | "key2 = value2.2\n" 168 | "\n" 169 | "[section2]\n" 170 | "key1\n" 171 | "key2\n" 172 | "[section3]\n"; 173 | 174 | CSimpleIniA ini; 175 | ini.SetUnicode(); 176 | ini.SetMultiKey(); 177 | ini.SetAllowKeyOnly(); 178 | 179 | SI_Error rc = ini.LoadData(example); 180 | ASSERT_EQ(rc, SI_OK); 181 | 182 | 183 | // check for section doesn't exist 184 | EXPECT_FALSE(ini.SectionExists("")); 185 | EXPECT_FALSE(ini.SectionExists("section4")); 186 | 187 | // check for section does exist 188 | EXPECT_TRUE(ini.SectionExists("section1")); 189 | EXPECT_TRUE(ini.SectionExists("section2")); 190 | EXPECT_TRUE(ini.SectionExists("section3")); 191 | 192 | // check for key doesn't exist 193 | EXPECT_FALSE(ini.KeyExists("", "key")); 194 | EXPECT_FALSE(ini.KeyExists("section1", "key")); 195 | EXPECT_FALSE(ini.KeyExists("section2", "key")); 196 | 197 | // check for key does exist 198 | EXPECT_TRUE(ini.KeyExists("section1", "key1")); 199 | EXPECT_TRUE(ini.KeyExists("section1", "key2")); 200 | EXPECT_TRUE(ini.KeyExists("section2", "key1")); 201 | EXPECT_TRUE(ini.KeyExists("section2", "key2")); 202 | } 203 | 204 | // ### MODIFYING DATA 205 | 206 | TEST(TestSnippets, TestModifyingData) { 207 | bool utf8 = true; 208 | bool multiKey = false; 209 | CSimpleIniA ini(utf8, multiKey); 210 | SI_Error rc; 211 | 212 | 213 | // add a new section 214 | rc = ini.SetValue("section1", nullptr, nullptr); 215 | if (rc < 0) { /* handle error */ }; 216 | ASSERT_EQ(rc, SI_INSERTED); 217 | 218 | // not an error to add one that already exists 219 | rc = ini.SetValue("section1", nullptr, nullptr); 220 | if (rc < 0) { /* handle error */ }; 221 | ASSERT_EQ(rc, SI_UPDATED); 222 | 223 | // get the value of a key that doesn't exist 224 | const char* pv; 225 | pv = ini.GetValue("section2", "key1", "default-value"); 226 | ASSERT_STREQ(pv, "default-value"); 227 | 228 | // adding a key (the section will be added if needed) 229 | rc = ini.SetValue("section2", "key1", "value1"); 230 | if (rc < 0) { /* handle error */ }; 231 | ASSERT_EQ(rc, SI_INSERTED); 232 | 233 | // ensure it is set to expected value 234 | pv = ini.GetValue("section2", "key1", nullptr); 235 | ASSERT_STREQ(pv, "value1"); 236 | 237 | // change the value of a key 238 | rc = ini.SetValue("section2", "key1", "value2"); 239 | if (rc < 0) { /* handle error */ }; 240 | ASSERT_EQ(rc, SI_UPDATED); 241 | 242 | // ensure it is set to expected value 243 | pv = ini.GetValue("section2", "key1", nullptr); 244 | ASSERT_STREQ(pv, "value2"); 245 | } 246 | 247 | 248 | // ### DELETING DATA 249 | 250 | TEST(TestSnippets, TestDeletingData) { 251 | const std::string example = 252 | "[section1]\n" 253 | "key1 = value1\n" 254 | "key2 = value2\n" 255 | "\n" 256 | "[section2]\n" 257 | "key1 = value1\n" 258 | "key2 = value2\n" 259 | "\n" 260 | "[section3]\n"; 261 | 262 | bool utf8 = true; 263 | CSimpleIniA ini(utf8); 264 | SI_Error rc = ini.LoadData(example); 265 | ASSERT_EQ(rc, SI_OK); 266 | 267 | 268 | // deleting a key from a section. Optionally the entire 269 | // section may be deleted if it is now empty. 270 | bool done, deleteSectionIfEmpty = true; 271 | done = ini.Delete("section1", "key1", deleteSectionIfEmpty); 272 | ASSERT_EQ(done, true); 273 | done = ini.Delete("section1", "key1"); 274 | ASSERT_EQ(done, false); 275 | 276 | // deleting an entire section and all keys in it 277 | done = ini.Delete("section2", nullptr); 278 | ASSERT_EQ(done, true); 279 | done = ini.Delete("section2", nullptr); 280 | ASSERT_EQ(done, false); 281 | } 282 | 283 | 284 | // ### SAVING DATA 285 | 286 | TEST(TestSnippets, TestSavingData) { 287 | bool utf8 = true; 288 | CSimpleIniA ini(utf8); 289 | SI_Error rc; 290 | 291 | 292 | // save the data to a string 293 | std::string data; 294 | rc = ini.Save(data); 295 | if (rc < 0) { /* handle error */ }; 296 | ASSERT_EQ(rc, SI_OK); 297 | 298 | // save the data back to the file 299 | rc = ini.SaveFile("example2.ini"); 300 | if (rc < 0) { /* handle error */ }; 301 | ASSERT_EQ(rc, SI_OK); 302 | } 303 | 304 | -------------------------------------------------------------------------------- /tests/ts-utf8.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../SimpleIni.h" 3 | 4 | class TestUTF8 : public ::testing::Test { 5 | protected: 6 | void SetUp() override; 7 | protected: 8 | CSimpleIniA ini; 9 | }; 10 | 11 | void TestUTF8::SetUp() { 12 | ini.SetUnicode(); 13 | SI_Error err = ini.LoadFile("tests.ini"); 14 | ASSERT_EQ(err, SI_OK); 15 | } 16 | 17 | TEST_F(TestUTF8, TestSectionAKeyAValA) { 18 | const char* result = ini.GetValue("section1", "key1"); 19 | ASSERT_STREQ(result, "value1"); 20 | } 21 | 22 | TEST_F(TestUTF8, TestSectionAKeyAValU) { 23 | const char tesuto2[] = u8"テスト2"; 24 | const char* result = ini.GetValue("section2", "test2"); 25 | ASSERT_STREQ(result, tesuto2); 26 | } 27 | 28 | TEST_F(TestUTF8, TestSectionAKeyUValA) { 29 | const char tesuto[] = u8"テスト"; 30 | const char* result = ini.GetValue("section2", tesuto); 31 | ASSERT_STREQ(result, "test"); 32 | } 33 | 34 | TEST_F(TestUTF8, TestSectionAKeyUValU) { 35 | const char tesuto2[] = u8"テスト2"; 36 | const char tesutoni[] = u8"テスト二"; 37 | const char* result = ini.GetValue("section2", tesuto2); 38 | ASSERT_STREQ(result, tesutoni); 39 | } 40 | 41 | TEST_F(TestUTF8, TestSectionUKeyAValA) { 42 | const char kensa[] = u8"検査"; 43 | const char* result = ini.GetValue(kensa, "key2"); 44 | ASSERT_STREQ(result, "value2"); 45 | } 46 | 47 | TEST_F(TestUTF8, TestSectionUKeyAValU) { 48 | const char kensa[] = u8"検査"; 49 | const char tesuto2[] = u8"テスト2"; 50 | const char* result = ini.GetValue(kensa, "test2"); 51 | ASSERT_STREQ(result, tesuto2); 52 | } 53 | 54 | TEST_F(TestUTF8, TestSectionUKeyUValA) { 55 | const char kensa[] = u8"検査"; 56 | const char tesuto[] = u8"テスト"; 57 | const char* result = ini.GetValue(kensa, tesuto); 58 | ASSERT_STREQ(result, "test"); 59 | } 60 | 61 | TEST_F(TestUTF8, TestSectionUKeyUValU) { 62 | const char kensa[] = u8"検査"; 63 | const char tesuto2[] = u8"テスト2"; 64 | const char tesutoni[] = u8"テスト二"; 65 | const char* result = ini.GetValue(kensa, tesuto2); 66 | ASSERT_STREQ(result, tesutoni); 67 | } 68 | -------------------------------------------------------------------------------- /tests/ts-wchar.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "../SimpleIni.h" 3 | 4 | class TestWide : public ::testing::Test { 5 | protected: 6 | void TestWide::SetUp() override; 7 | protected: 8 | CSimpleIniW ini; 9 | }; 10 | 11 | void TestWide::SetUp() { 12 | ini.SetUnicode(); 13 | SI_Error err = ini.LoadFile(L"tests.ini"); 14 | ASSERT_EQ(err, SI_OK); 15 | } 16 | 17 | TEST_F(TestWide, TestSectionAKeyAValA) { 18 | const wchar_t* result = ini.GetValue(L"section1", L"key1"); 19 | ASSERT_STREQ(result, L"value1"); 20 | } 21 | 22 | TEST_F(TestWide, TestSectionAKeyAValU) { 23 | const wchar_t tesuto2[] = L"テスト2"; 24 | const wchar_t* result = ini.GetValue(L"section2", L"test2"); 25 | ASSERT_STREQ(result, tesuto2); 26 | } 27 | 28 | TEST_F(TestWide, TestSectionAKeyUValA) { 29 | const wchar_t tesuto[] = L"テスト"; 30 | const wchar_t* result = ini.GetValue(L"section2", tesuto); 31 | ASSERT_STREQ(result, L"test"); 32 | } 33 | 34 | TEST_F(TestWide, TestSectionAKeyUValU) { 35 | const wchar_t tesuto2[] = L"テスト2"; 36 | const wchar_t tesutoni[] = L"テスト二"; 37 | const wchar_t* result = ini.GetValue(L"section2", tesuto2); 38 | ASSERT_STREQ(result, tesutoni); 39 | } 40 | 41 | TEST_F(TestWide, TestSectionUKeyAValA) { 42 | const wchar_t kensa[] = L"検査"; 43 | const wchar_t* result = ini.GetValue(kensa, L"key2"); 44 | ASSERT_STREQ(result, L"value2"); 45 | } 46 | 47 | TEST_F(TestWide, TestSectionUKeyAValU) { 48 | const wchar_t kensa[] = L"検査"; 49 | const wchar_t tesuto2[] = L"テスト2"; 50 | const wchar_t* result = ini.GetValue(kensa, L"test2"); 51 | ASSERT_STREQ(result, tesuto2); 52 | } 53 | 54 | TEST_F(TestWide, TestSectionUKeyUValA) { 55 | const wchar_t kensa[] = L"検査"; 56 | const wchar_t tesuto[] = L"テスト"; 57 | const wchar_t* result = ini.GetValue(kensa, tesuto); 58 | ASSERT_STREQ(result, L"test"); 59 | } 60 | 61 | TEST_F(TestWide, TestSectionUKeyUValU) { 62 | const wchar_t kensa[] = L"検査"; 63 | const wchar_t tesuto2[] = L"テスト2"; 64 | const wchar_t tesutoni[] = L"テスト二"; 65 | const wchar_t* result = ini.GetValue(kensa, tesuto2); 66 | ASSERT_STREQ(result, tesutoni); 67 | } 68 | -------------------------------------------------------------------------------- /vcproj/SimpleIni.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32804.467 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library Files", "Library Files", "{C1F8A145-78E7-42C6-95D5-23C746C2BC56}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\ConvertUTF.c = ..\ConvertUTF.c 9 | ..\ConvertUTF.h = ..\ConvertUTF.h 10 | ..\ConvertUTF_readme.txt = ..\ConvertUTF_readme.txt 11 | ..\SimpleIni.h = ..\SimpleIni.h 12 | EndProjectSection 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Information Files", "Information Files", "{E40DD170-6D17-49D2-9BB2-8546658F0A37}" 15 | ProjectSection(SolutionItems) = preProject 16 | ..\LICENCE.txt = ..\LICENCE.txt 17 | ..\README.md = ..\README.md 18 | EndProjectSection 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Other Files", "Other Files", "{560B512C-6D1C-4E65-83C1-049110E5DEF6}" 21 | ProjectSection(SolutionItems) = preProject 22 | ..\Makefile = ..\Makefile 23 | ..\other\package.cmd = ..\other\package.cmd 24 | ..\other\simpleini.doxy = ..\other\simpleini.doxy 25 | EndProjectSection 26 | EndProject 27 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "..\tests\tests.vcxproj", "{8F30A5DC-B942-4C9A-BA75-91C906FF85FA}" 28 | EndProject 29 | Global 30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 31 | Debug|x64 = Debug|x64 32 | Release|x64 = Release|x64 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {8F30A5DC-B942-4C9A-BA75-91C906FF85FA}.Debug|x64.ActiveCfg = Debug|x64 36 | {8F30A5DC-B942-4C9A-BA75-91C906FF85FA}.Debug|x64.Build.0 = Debug|x64 37 | {8F30A5DC-B942-4C9A-BA75-91C906FF85FA}.Release|x64.ActiveCfg = Release|x64 38 | {8F30A5DC-B942-4C9A-BA75-91C906FF85FA}.Release|x64.Build.0 = Release|x64 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {ABD2CECE-EA8B-455B-8AE7-00E499634EC2} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /vcproj/SimpleIni.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {9a59fc4f-ad32-4bd3-b6b5-9bb0ddc6138d} 25 | SimpleIni 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /vcproj/SimpleIni.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {353054bc-f2a3-46d2-a9cb-df767fe52289} 6 | 7 | 8 | 9 | 10 | Tests 11 | 12 | 13 | Tests 14 | 15 | 16 | --------------------------------------------------------------------------------