react-native-nitro-xlsx 0.1.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/NitroXlsx.podspec +27 -27
  2. package/Package.swift +35 -10
  3. package/README.md +220 -58
  4. package/android/CMakeLists.txt +24 -27
  5. package/android/cpp-adapter.cpp +12 -0
  6. package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
  7. package/cpp/HybridNitroXlsx.cpp +25 -179
  8. package/cpp/HybridNitroXlsx.hpp +2 -2
  9. package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
  10. package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
  11. package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
  12. package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
  13. package/cpp/HybridXlsxWorkbook.cpp +159 -196
  14. package/cpp/HybridXlsxWorkbook.hpp +7 -2
  15. package/cpp/HybridXlsxWorksheet.cpp +433 -62
  16. package/cpp/HybridXlsxWorksheet.hpp +73 -26
  17. package/cpp/XlsxCacheDir.cpp +76 -0
  18. package/cpp/XlsxCacheDir.hpp +27 -0
  19. package/cpp/XlsxError.hpp +68 -0
  20. package/cpp/XlsxJson.cpp +185 -0
  21. package/cpp/XlsxJson.hpp +36 -0
  22. package/ios/XlsxCacheDir.mm +60 -0
  23. package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
  24. package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -3
  25. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
  26. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
  27. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
  28. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
  29. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +3 -0
  30. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +6 -1
  31. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
  32. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
  33. package/package.json +3 -3
  34. package/scripts/run-cpp-tests.sh +14 -0
  35. package/src/__mocks__/react-native-nitro-modules.ts +1 -0
  36. package/src/__tests__/index.test.ts +76 -7
  37. package/src/index.ts +107 -18
  38. package/src/specs/NitroXlsx.nitro.ts +141 -44
  39. package/thirdparty/OpenXLSX/.clang-format +118 -0
  40. package/thirdparty/OpenXLSX/.clang-tidy +28 -0
  41. package/thirdparty/OpenXLSX/.gitignore +130 -0
  42. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_before_XMLNode_wrappers +46 -0
  43. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_and_stripping +49 -0
  44. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_char_support +49 -0
  45. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_support +49 -0
  46. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespaced_name_disabled +49 -0
  47. package/thirdparty/OpenXLSX/Benchmarks/2024-08-11-benchmarks_post-XLCellIterator-Rework +46 -0
  48. package/thirdparty/OpenXLSX/Benchmarks/2024-08-14-benchmarks_post-default-row-column-style-support +46 -0
  49. package/thirdparty/OpenXLSX/Benchmarks/2024-09-02-benchmarks +46 -0
  50. package/thirdparty/OpenXLSX/Benchmarks/2024-12-19-benchmarks +46 -0
  51. package/thirdparty/OpenXLSX/Benchmarks/2024-12-30-benchmarks-post-XLSharedStrings-by-ref +46 -0
  52. package/thirdparty/OpenXLSX/Benchmarks/2025-01-09-benchmarks-post-conditionalFormatsAndSheetProtection +46 -0
  53. package/thirdparty/OpenXLSX/Benchmarks/2025-01-10-benchmarks-post-XLCellValue-bugfix +46 -0
  54. package/thirdparty/OpenXLSX/Benchmarks/2025-01-31-benchmarks-post-XLComments-implementation +46 -0
  55. package/thirdparty/OpenXLSX/Benchmarks/2025-02-02-benchmarks-post-XLRowIterator-Rework +46 -0
  56. package/thirdparty/OpenXLSX/Benchmarks/2025-07-20-benchmarks-after-hiding-pugixml-internals +46 -0
  57. package/thirdparty/OpenXLSX/Benchmarks/2025-08-29-benchmarks-after-code-review-with-libzip +46 -0
  58. package/thirdparty/OpenXLSX/Benchmarks/Benchmark.cpp +249 -0
  59. package/thirdparty/OpenXLSX/Benchmarks/CMakeLists.txt +19 -0
  60. package/thirdparty/OpenXLSX/CMakeLists.txt +466 -0
  61. package/thirdparty/OpenXLSX/Documentation/CMakeLists.txt +31 -0
  62. package/thirdparty/OpenXLSX/Documentation/Doxyfile.in +2471 -0
  63. package/thirdparty/OpenXLSX/Documentation/DoxygenLayout.xml +194 -0
  64. package/thirdparty/OpenXLSX/Documentation/customdoxygen.css +1355 -0
  65. package/thirdparty/OpenXLSX/Examples/CMakeLists.txt +103 -0
  66. package/thirdparty/OpenXLSX/Examples/Demo1.cpp +279 -0
  67. package/thirdparty/OpenXLSX/Examples/Demo10.cpp +545 -0
  68. package/thirdparty/OpenXLSX/Examples/Demo1A.cpp +137 -0
  69. package/thirdparty/OpenXLSX/Examples/Demo2.cpp +65 -0
  70. package/thirdparty/OpenXLSX/Examples/Demo3.cpp +89 -0
  71. package/thirdparty/OpenXLSX/Examples/Demo4.cpp +86 -0
  72. package/thirdparty/OpenXLSX/Examples/Demo5.cpp +77 -0
  73. package/thirdparty/OpenXLSX/Examples/Demo6.cpp +103 -0
  74. package/thirdparty/OpenXLSX/Examples/Demo7.cpp +79 -0
  75. package/thirdparty/OpenXLSX/Examples/Demo8.cpp +117 -0
  76. package/thirdparty/OpenXLSX/Examples/Demo9.cpp +363 -0
  77. package/thirdparty/OpenXLSX/Examples/cmake/FindLIBZIP.cmake +61 -0
  78. package/thirdparty/OpenXLSX/Examples/external/CustomZip/CustomZip.hpp +256 -0
  79. package/thirdparty/OpenXLSX/LICENSE.md +29 -0
  80. package/thirdparty/OpenXLSX/Makefile.GNU +530 -0
  81. package/thirdparty/OpenXLSX/Notes/TBDs.txt +16 -0
  82. package/thirdparty/OpenXLSX/Notes/confirmed-TBCs.txt +8 -0
  83. package/thirdparty/OpenXLSX/Notes/styles-missing-support.xml +12 -0
  84. package/thirdparty/OpenXLSX/Notes/todo-list.txt +108 -0
  85. package/thirdparty/OpenXLSX/OpenXLSX/CMakeLists.txt +631 -0
  86. package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSX.hpp +63 -0
  87. package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSXConfig.cmake +11 -0
  88. package/thirdparty/OpenXLSX/OpenXLSX/cmake/bundle_static_library.cmake +198 -0
  89. package/thirdparty/OpenXLSX/OpenXLSX/cmake/patch_pkg_config.cmake +69 -0
  90. package/thirdparty/OpenXLSX/OpenXLSX/headers/IZipArchive.hpp +361 -0
  91. package/thirdparty/OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp +12 -0
  92. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCell.hpp +367 -0
  93. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellIterator.hpp +252 -0
  94. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellRange.hpp +223 -0
  95. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellReference.hpp +317 -0
  96. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellValue.hpp +793 -0
  97. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColor.hpp +232 -0
  98. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColumn.hpp +159 -0
  99. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCommandQuery.hpp +228 -0
  100. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLComments.hpp +270 -0
  101. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLConstants.hpp +18 -0
  102. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLContentTypes.hpp +264 -0
  103. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDateTime.hpp +175 -0
  104. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDocument.hpp +505 -0
  105. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDrawing.hpp +452 -0
  106. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLException.hpp +161 -0
  107. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLFormula.hpp +375 -0
  108. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLIterator.hpp +40 -0
  109. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLMergeCells.hpp +216 -0
  110. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLProperties.hpp +348 -0
  111. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRelationships.hpp +348 -0
  112. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRow.hpp +721 -0
  113. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRowData.hpp +488 -0
  114. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSharedStrings.hpp +204 -0
  115. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSheet.hpp +1640 -0
  116. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLStyles.hpp +2390 -0
  117. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLTables.hpp +132 -0
  118. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLWorkbook.hpp +404 -0
  119. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlData.hpp +283 -0
  120. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlFile.hpp +194 -0
  121. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParser.hpp +282 -0
  122. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParserForwardDeclarations.hpp +85 -0
  123. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLZipArchive.hpp +232 -0
  124. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/LibZip.hpp +447 -0
  125. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/OpenXLSXFileSystemTools.hpp +207 -0
  126. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/Zippy.hpp +1375 -0
  127. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCell.cpp +340 -0
  128. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellIterator.cpp +427 -0
  129. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellRange.cpp +221 -0
  130. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellReference.cpp +408 -0
  131. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellValue.cpp +519 -0
  132. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColor.cpp +202 -0
  133. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColumn.cpp +141 -0
  134. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLComments.cpp +637 -0
  135. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLContentTypes.cpp +329 -0
  136. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDateTime.cpp +269 -0
  137. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDocument.cpp +1678 -0
  138. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDrawing.cpp +784 -0
  139. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLFormula.cpp +201 -0
  140. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLMergeCells.cpp +357 -0
  141. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLProperties.cpp +810 -0
  142. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRelationships.cpp +529 -0
  143. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRow.cpp +918 -0
  144. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRowData.cpp +508 -0
  145. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSharedStrings.cpp +220 -0
  146. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSheet.cpp +1970 -0
  147. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLStyles.cpp +3014 -0
  148. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLTables.cpp +125 -0
  149. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLWorkbook.cpp +784 -0
  150. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlData.cpp +247 -0
  151. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlFile.cpp +160 -0
  152. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlParser.cpp +261 -0
  153. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLZipArchive.cpp +266 -0
  154. package/thirdparty/OpenXLSX/OpenXLSX/sources/utilities/XLUtilities.hpp +478 -0
  155. package/thirdparty/OpenXLSX/README.md +1279 -0
  156. package/thirdparty/OpenXLSX/Scripts/cmake-cleanup.sh +102 -0
  157. package/thirdparty/OpenXLSX/Scripts/compile.sh +23 -0
  158. package/thirdparty/OpenXLSX/Scripts/demos-cleanup.sh +25 -0
  159. package/thirdparty/OpenXLSX/Scripts/demos-run-all.sh +60 -0
  160. package/thirdparty/OpenXLSX/Scripts/guarded-xml-format.sh +47 -0
  161. package/thirdparty/OpenXLSX/Scripts/make-gnu.sh +11 -0
  162. package/thirdparty/OpenXLSX/Scripts/uninstall-manifest.sh +48 -0
  163. package/thirdparty/OpenXLSX/Scripts/xml-format-all.sh +19 -0
  164. package/thirdparty/OpenXLSX/Scripts/xml-format.sh +38 -0
  165. package/thirdparty/OpenXLSX/Tests/CMakeLists.txt +37 -0
  166. package/thirdparty/OpenXLSX/Tests/catch/catch.hpp +17799 -0
  167. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_automake.hpp +68 -0
  168. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_sonarqube.hpp +182 -0
  169. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_tap.hpp +278 -0
  170. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_teamcity.hpp +212 -0
  171. package/thirdparty/OpenXLSX/Tests/main.cpp +55 -0
  172. package/thirdparty/OpenXLSX/Tests/testXLCell.cpp +176 -0
  173. package/thirdparty/OpenXLSX/Tests/testXLCellRange.cpp +180 -0
  174. package/thirdparty/OpenXLSX/Tests/testXLCellReference.cpp +160 -0
  175. package/thirdparty/OpenXLSX/Tests/testXLCellValue.cpp +366 -0
  176. package/thirdparty/OpenXLSX/Tests/testXLCellValueProxy.cpp +255 -0
  177. package/thirdparty/OpenXLSX/Tests/testXLColor.cpp +75 -0
  178. package/thirdparty/OpenXLSX/Tests/testXLDateTime.cpp +229 -0
  179. package/thirdparty/OpenXLSX/Tests/testXLDocument.cpp +197 -0
  180. package/thirdparty/OpenXLSX/Tests/testXLFormula.cpp +131 -0
  181. package/thirdparty/OpenXLSX/Tests/testXLRow.cpp +597 -0
  182. package/thirdparty/OpenXLSX/Tests/testXLSheet.cpp +77 -0
  183. package/thirdparty/OpenXLSX/Tests/ziptest-with-libs.cpp +88 -0
  184. package/thirdparty/OpenXLSX/Tests/ziptest.cpp +735 -0
  185. package/thirdparty/OpenXLSX/cmake/get-git-HEAD-ID.cmake +45 -0
  186. package/thirdparty/OpenXLSX/cmake/libopenxlsxCPackOptions.cmake.in +20 -0
  187. package/thirdparty/OpenXLSX/cmake/manage_dependency.cmake +813 -0
  188. package/thirdparty/OpenXLSX/cmake/recursive_library_type_test.cmake +52 -0
  189. package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-shared.hpp +42 -0
  190. package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-static.hpp +42 -0
  191. package/thirdparty/OpenXLSX/vcpkg.json +24 -0
  192. package/thirdparty/README.md +42 -0
  193. package/thirdparty/miniz/.clang-format +47 -0
  194. package/thirdparty/miniz/.travis.yml +2 -0
  195. package/thirdparty/miniz/CMakeLists.txt +333 -0
  196. package/thirdparty/miniz/CMakePresets.json +31 -0
  197. package/thirdparty/miniz/ChangeLog.md +272 -0
  198. package/thirdparty/miniz/Config.cmake.in +1 -0
  199. package/thirdparty/miniz/LICENSE +22 -0
  200. package/thirdparty/miniz/amalgamate.sh +57 -0
  201. package/thirdparty/miniz/examples/example1.c +105 -0
  202. package/thirdparty/miniz/examples/example2.c +164 -0
  203. package/thirdparty/miniz/examples/example3.c +269 -0
  204. package/thirdparty/miniz/examples/example4.c +102 -0
  205. package/thirdparty/miniz/examples/example5.c +327 -0
  206. package/thirdparty/miniz/examples/example6.c +189 -0
  207. package/thirdparty/miniz/meson.build +21 -0
  208. package/thirdparty/miniz/miniz.c +646 -0
  209. package/thirdparty/miniz/miniz.h +615 -0
  210. package/thirdparty/miniz/miniz.pc.in +13 -0
  211. package/thirdparty/miniz/miniz_common.h +89 -0
  212. package/thirdparty/miniz/miniz_export.h +20 -0
  213. package/thirdparty/miniz/miniz_tdef.c +1602 -0
  214. package/thirdparty/miniz/miniz_tdef.h +199 -0
  215. package/thirdparty/miniz/miniz_tinfl.c +778 -0
  216. package/thirdparty/miniz/miniz_tinfl.h +150 -0
  217. package/thirdparty/miniz/miniz_zip.c +4895 -0
  218. package/thirdparty/miniz/miniz_zip.h +454 -0
  219. package/thirdparty/miniz/readme.md +46 -0
  220. package/thirdparty/miniz/test.sh +36 -0
  221. package/thirdparty/miniz/tests/add_in_place_fuzzer.c +87 -0
  222. package/thirdparty/miniz/tests/catch_amalgamated.cpp +10795 -0
  223. package/thirdparty/miniz/tests/catch_amalgamated.hpp +13143 -0
  224. package/thirdparty/miniz/tests/checksum_fuzzer.c +26 -0
  225. package/thirdparty/miniz/tests/compress_fuzzer.c +91 -0
  226. package/thirdparty/miniz/tests/flush_fuzzer.c +88 -0
  227. package/thirdparty/miniz/tests/fuzz_main.c +55 -0
  228. package/thirdparty/miniz/tests/large_fuzzer.c +133 -0
  229. package/thirdparty/miniz/tests/main.cpp +185 -0
  230. package/thirdparty/miniz/tests/miniz_tdefl_fuzzer.cc +115 -0
  231. package/thirdparty/miniz/tests/miniz_tester.cpp +1881 -0
  232. package/thirdparty/miniz/tests/miniz_zip_validate_fuzzer.cc +71 -0
  233. package/thirdparty/miniz/tests/miniz_zip_writer_fuzzer.cc +96 -0
  234. package/thirdparty/miniz/tests/ossfuzz.sh +48 -0
  235. package/thirdparty/miniz/tests/small_fuzzer.c +126 -0
  236. package/thirdparty/miniz/tests/timer.cpp +152 -0
  237. package/thirdparty/miniz/tests/timer.h +40 -0
  238. package/thirdparty/miniz/tests/uncompress2_fuzzer.c +21 -0
  239. package/thirdparty/miniz/tests/uncompress_fuzzer.c +30 -0
  240. package/thirdparty/miniz/tests/zip.dict +9 -0
  241. package/thirdparty/miniz/tests/zip_fuzzer.c +63 -0
  242. package/thirdparty/pugixml/.codecov.yml +7 -0
  243. package/thirdparty/pugixml/.gitattributes +1 -0
  244. package/thirdparty/pugixml/CMakeLists.txt +330 -0
  245. package/thirdparty/pugixml/LICENSE.md +24 -0
  246. package/thirdparty/pugixml/Makefile +98 -0
  247. package/thirdparty/pugixml/README.md +81 -0
  248. package/thirdparty/pugixml/SECURITY.md +9 -0
  249. package/thirdparty/pugixml/appveyor.yml +50 -0
  250. package/thirdparty/pugixml/docs/config.adoc +7 -0
  251. package/thirdparty/pugixml/docs/images/dom_tree.png +0 -0
  252. package/thirdparty/pugixml/docs/images/vs2005_link1.png +0 -0
  253. package/thirdparty/pugixml/docs/images/vs2005_link2.png +0 -0
  254. package/thirdparty/pugixml/docs/images/vs2005_pch1.png +0 -0
  255. package/thirdparty/pugixml/docs/images/vs2005_pch2.png +0 -0
  256. package/thirdparty/pugixml/docs/images/vs2005_pch3.png +0 -0
  257. package/thirdparty/pugixml/docs/images/vs2005_pch4.png +0 -0
  258. package/thirdparty/pugixml/docs/images/vs2010_link1.png +0 -0
  259. package/thirdparty/pugixml/docs/images/vs2010_link2.png +0 -0
  260. package/thirdparty/pugixml/docs/manual.adoc +3326 -0
  261. package/thirdparty/pugixml/docs/manual.html +6395 -0
  262. package/thirdparty/pugixml/docs/quickstart.adoc +287 -0
  263. package/thirdparty/pugixml/docs/quickstart.html +1107 -0
  264. package/thirdparty/pugixml/docs/samples/character.xml +8 -0
  265. package/thirdparty/pugixml/docs/samples/custom_memory_management.cpp +27 -0
  266. package/thirdparty/pugixml/docs/samples/include.cpp +64 -0
  267. package/thirdparty/pugixml/docs/samples/load_error_handling.cpp +33 -0
  268. package/thirdparty/pugixml/docs/samples/load_file.cpp +16 -0
  269. package/thirdparty/pugixml/docs/samples/load_memory.cpp +66 -0
  270. package/thirdparty/pugixml/docs/samples/load_options.cpp +30 -0
  271. package/thirdparty/pugixml/docs/samples/load_stream.cpp +97 -0
  272. package/thirdparty/pugixml/docs/samples/modify_add.cpp +29 -0
  273. package/thirdparty/pugixml/docs/samples/modify_base.cpp +43 -0
  274. package/thirdparty/pugixml/docs/samples/modify_remove.cpp +27 -0
  275. package/thirdparty/pugixml/docs/samples/save_custom_writer.cpp +116 -0
  276. package/thirdparty/pugixml/docs/samples/save_declaration.cpp +27 -0
  277. package/thirdparty/pugixml/docs/samples/save_file.cpp +17 -0
  278. package/thirdparty/pugixml/docs/samples/save_options.cpp +48 -0
  279. package/thirdparty/pugixml/docs/samples/save_stream.cpp +18 -0
  280. package/thirdparty/pugixml/docs/samples/save_subtree.cpp +26 -0
  281. package/thirdparty/pugixml/docs/samples/text.cpp +35 -0
  282. package/thirdparty/pugixml/docs/samples/transitions.xml +7 -0
  283. package/thirdparty/pugixml/docs/samples/traverse_base.cpp +51 -0
  284. package/thirdparty/pugixml/docs/samples/traverse_iter.cpp +27 -0
  285. package/thirdparty/pugixml/docs/samples/traverse_predicate.cpp +48 -0
  286. package/thirdparty/pugixml/docs/samples/traverse_rangefor.cpp +32 -0
  287. package/thirdparty/pugixml/docs/samples/traverse_walker.cpp +35 -0
  288. package/thirdparty/pugixml/docs/samples/tree.xml +12 -0
  289. package/thirdparty/pugixml/docs/samples/weekly-shift_jis.xml +78 -0
  290. package/thirdparty/pugixml/docs/samples/weekly-utf-16.xml +0 -0
  291. package/thirdparty/pugixml/docs/samples/weekly-utf-8.xml +78 -0
  292. package/thirdparty/pugixml/docs/samples/xgconsole.xml +12 -0
  293. package/thirdparty/pugixml/docs/samples/xpath_error.cpp +43 -0
  294. package/thirdparty/pugixml/docs/samples/xpath_query.cpp +36 -0
  295. package/thirdparty/pugixml/docs/samples/xpath_select.cpp +28 -0
  296. package/thirdparty/pugixml/docs/samples/xpath_variables.cpp +38 -0
  297. package/thirdparty/pugixml/readme.txt +50 -0
  298. package/thirdparty/pugixml/scripts/archive.py +54 -0
  299. package/thirdparty/pugixml/scripts/cocoapods_push.sh +4 -0
  300. package/thirdparty/pugixml/scripts/natvis/pugixml.natvis +77 -0
  301. package/thirdparty/pugixml/scripts/natvis/pugixml_compact.natvis +506 -0
  302. package/thirdparty/pugixml/scripts/nuget/build/native/pugixml-propertiesui.xml +17 -0
  303. package/thirdparty/pugixml/scripts/nuget/build/native/pugixml.targets +28 -0
  304. package/thirdparty/pugixml/scripts/nuget/pugixml.nuspec +20 -0
  305. package/thirdparty/pugixml/scripts/nuget_build.ps1 +60 -0
  306. package/thirdparty/pugixml/scripts/premake4.lua +92 -0
  307. package/thirdparty/pugixml/scripts/pugixml-config.cmake.in +12 -0
  308. package/thirdparty/pugixml/scripts/pugixml.pc.in +11 -0
  309. package/thirdparty/pugixml/scripts/pugixml.podspec +14 -0
  310. package/thirdparty/pugixml/scripts/pugixml.xcodeproj/project.pbxproj +212 -0
  311. package/thirdparty/pugixml/scripts/pugixml_airplay.mkf +13 -0
  312. package/thirdparty/pugixml/scripts/pugixml_codeblocks.cbp +44 -0
  313. package/thirdparty/pugixml/scripts/pugixml_codelite.project +56 -0
  314. package/thirdparty/pugixml/scripts/pugixml_dll.rc +45 -0
  315. package/thirdparty/pugixml/scripts/pugixml_vs2005.vcproj +343 -0
  316. package/thirdparty/pugixml/scripts/pugixml_vs2005_static.vcproj +343 -0
  317. package/thirdparty/pugixml/scripts/pugixml_vs2008.vcproj +339 -0
  318. package/thirdparty/pugixml/scripts/pugixml_vs2008_static.vcproj +339 -0
  319. package/thirdparty/pugixml/scripts/pugixml_vs2010.vcxproj +191 -0
  320. package/thirdparty/pugixml/scripts/pugixml_vs2010_static.vcxproj +191 -0
  321. package/thirdparty/pugixml/scripts/pugixml_vs2013.vcxproj +199 -0
  322. package/thirdparty/pugixml/scripts/pugixml_vs2013_static.vcxproj +199 -0
  323. package/thirdparty/pugixml/scripts/pugixml_vs2015.vcxproj +172 -0
  324. package/thirdparty/pugixml/scripts/pugixml_vs2015_static.vcxproj +176 -0
  325. package/thirdparty/pugixml/scripts/pugixml_vs2017.vcxproj +172 -0
  326. package/thirdparty/pugixml/scripts/pugixml_vs2017_static.vcxproj +176 -0
  327. package/thirdparty/pugixml/scripts/pugixml_vs2019.vcxproj +176 -0
  328. package/thirdparty/pugixml/scripts/pugixml_vs2019_static.vcxproj +180 -0
  329. package/thirdparty/pugixml/scripts/pugixml_vs2022.vcxproj +176 -0
  330. package/thirdparty/pugixml/scripts/pugixml_vs2022_static.vcxproj +180 -0
  331. package/thirdparty/pugixml/scripts/pugixml_vs2026.vcxproj +176 -0
  332. package/thirdparty/pugixml/scripts/pugixml_vs2026_static.vcxproj +180 -0
  333. package/thirdparty/pugixml/scripts/sbom.cdx.json +47 -0
  334. package/thirdparty/pugixml/src/pugiconfig.hpp +81 -0
  335. package/thirdparty/pugixml/src/pugixml.cpp +13776 -0
  336. package/thirdparty/pugixml/src/pugixml.hpp +1604 -0
  337. package/thirdparty/pugixml/tests/allocator.cpp +175 -0
  338. package/thirdparty/pugixml/tests/allocator.hpp +10 -0
  339. package/thirdparty/pugixml/tests/autotest-appveyor.ps1 +93 -0
  340. package/thirdparty/pugixml/tests/cmake-appveyor.ps1 +57 -0
  341. package/thirdparty/pugixml/tests/data/empty.xml +0 -0
  342. package/thirdparty/pugixml/tests/data/large.xml +1 -0
  343. package/thirdparty/pugixml/tests/data/latintest_latin1.xml +1 -0
  344. package/thirdparty/pugixml/tests/data/latintest_utf8.xml +1 -0
  345. package/thirdparty/pugixml/tests/data/multiline.xml +3 -0
  346. package/thirdparty/pugixml/tests/data/small.xml +1 -0
  347. package/thirdparty/pugixml/tests/data/truncation.xml +19 -0
  348. package/thirdparty/pugixml/tests/data/utftest_utf16_be.xml +0 -0
  349. package/thirdparty/pugixml/tests/data/utftest_utf16_be_bom.xml +0 -0
  350. package/thirdparty/pugixml/tests/data/utftest_utf16_be_clean.xml +0 -0
  351. package/thirdparty/pugixml/tests/data/utftest_utf16_be_nodecl.xml +0 -0
  352. package/thirdparty/pugixml/tests/data/utftest_utf16_le.xml +0 -0
  353. package/thirdparty/pugixml/tests/data/utftest_utf16_le_bom.xml +0 -0
  354. package/thirdparty/pugixml/tests/data/utftest_utf16_le_clean.xml +0 -0
  355. package/thirdparty/pugixml/tests/data/utftest_utf16_le_nodecl.xml +0 -0
  356. package/thirdparty/pugixml/tests/data/utftest_utf32_be.xml +0 -0
  357. package/thirdparty/pugixml/tests/data/utftest_utf32_be_bom.xml +0 -0
  358. package/thirdparty/pugixml/tests/data/utftest_utf32_be_clean.xml +0 -0
  359. package/thirdparty/pugixml/tests/data/utftest_utf32_be_nodecl.xml +0 -0
  360. package/thirdparty/pugixml/tests/data/utftest_utf32_le.xml +0 -0
  361. package/thirdparty/pugixml/tests/data/utftest_utf32_le_bom.xml +0 -0
  362. package/thirdparty/pugixml/tests/data/utftest_utf32_le_clean.xml +0 -0
  363. package/thirdparty/pugixml/tests/data/utftest_utf32_le_nodecl.xml +0 -0
  364. package/thirdparty/pugixml/tests/data/utftest_utf8.xml +87 -0
  365. package/thirdparty/pugixml/tests/data/utftest_utf8_bom.xml +87 -0
  366. package/thirdparty/pugixml/tests/data/utftest_utf8_clean.xml +84 -0
  367. package/thirdparty/pugixml/tests/data/utftest_utf8_nodecl.xml +86 -0
  368. package/thirdparty/pugixml/tests/data//321/202/320/265/321/201/321/202.xml +1 -0
  369. package/thirdparty/pugixml/tests/data_fuzz_parse/basic.xml +1 -0
  370. package/thirdparty/pugixml/tests/data_fuzz_parse/doctype.xml +1 -0
  371. package/thirdparty/pugixml/tests/data_fuzz_parse/refs.xml +1 -0
  372. package/thirdparty/pugixml/tests/data_fuzz_parse/types.xml +1 -0
  373. package/thirdparty/pugixml/tests/data_fuzz_parse/utf16.xml +0 -0
  374. package/thirdparty/pugixml/tests/data_fuzz_parse/utf32.xml +0 -0
  375. package/thirdparty/pugixml/tests/data_fuzz_xpath/basic.xpath +1 -0
  376. package/thirdparty/pugixml/tests/data_fuzz_xpath/functions.xpath +1 -0
  377. package/thirdparty/pugixml/tests/data_fuzz_xpath/math.xpath +1 -0
  378. package/thirdparty/pugixml/tests/data_fuzz_xpath/path.xpath +1 -0
  379. package/thirdparty/pugixml/tests/data_fuzz_xpath/predicate.xpath +1 -0
  380. package/thirdparty/pugixml/tests/fuzz_parse.cpp +14 -0
  381. package/thirdparty/pugixml/tests/fuzz_parse.dict +72 -0
  382. package/thirdparty/pugixml/tests/fuzz_setup.sh +11 -0
  383. package/thirdparty/pugixml/tests/fuzz_xpath.cpp +46 -0
  384. package/thirdparty/pugixml/tests/fuzz_xpath.dict +72 -0
  385. package/thirdparty/pugixml/tests/helpers.hpp +114 -0
  386. package/thirdparty/pugixml/tests/main.cpp +218 -0
  387. package/thirdparty/pugixml/tests/test.cpp +220 -0
  388. package/thirdparty/pugixml/tests/test.hpp +175 -0
  389. package/thirdparty/pugixml/tests/test_compact.cpp +150 -0
  390. package/thirdparty/pugixml/tests/test_deprecated.cpp +25 -0
  391. package/thirdparty/pugixml/tests/test_document.cpp +1910 -0
  392. package/thirdparty/pugixml/tests/test_dom_modify.cpp +2122 -0
  393. package/thirdparty/pugixml/tests/test_dom_text.cpp +601 -0
  394. package/thirdparty/pugixml/tests/test_dom_traverse.cpp +1481 -0
  395. package/thirdparty/pugixml/tests/test_header_guard.cpp +3 -0
  396. package/thirdparty/pugixml/tests/test_header_iosfwd_1.cpp +3 -0
  397. package/thirdparty/pugixml/tests/test_header_iosfwd_2.cpp +3 -0
  398. package/thirdparty/pugixml/tests/test_header_iostream_1.cpp +3 -0
  399. package/thirdparty/pugixml/tests/test_header_iostream_2.cpp +3 -0
  400. package/thirdparty/pugixml/tests/test_header_only_1.cpp +21 -0
  401. package/thirdparty/pugixml/tests/test_header_only_2.cpp +21 -0
  402. package/thirdparty/pugixml/tests/test_header_string_1.cpp +3 -0
  403. package/thirdparty/pugixml/tests/test_header_string_2.cpp +3 -0
  404. package/thirdparty/pugixml/tests/test_header_string_iostream.cpp +5 -0
  405. package/thirdparty/pugixml/tests/test_memory.cpp +303 -0
  406. package/thirdparty/pugixml/tests/test_parse.cpp +1463 -0
  407. package/thirdparty/pugixml/tests/test_parse_doctype.cpp +366 -0
  408. package/thirdparty/pugixml/tests/test_unicode.cpp +152 -0
  409. package/thirdparty/pugixml/tests/test_version.cpp +5 -0
  410. package/thirdparty/pugixml/tests/test_write.cpp +757 -0
  411. package/thirdparty/pugixml/tests/test_xpath.cpp +833 -0
  412. package/thirdparty/pugixml/tests/test_xpath_api.cpp +646 -0
  413. package/thirdparty/pugixml/tests/test_xpath_functions.cpp +844 -0
  414. package/thirdparty/pugixml/tests/test_xpath_operators.cpp +566 -0
  415. package/thirdparty/pugixml/tests/test_xpath_parse.cpp +428 -0
  416. package/thirdparty/pugixml/tests/test_xpath_paths.cpp +787 -0
  417. package/thirdparty/pugixml/tests/test_xpath_paths_abbrev_w3c.cpp +219 -0
  418. package/thirdparty/pugixml/tests/test_xpath_paths_w3c.cpp +312 -0
  419. package/thirdparty/pugixml/tests/test_xpath_variables.cpp +683 -0
  420. package/thirdparty/pugixml/tests/test_xpath_xalan_1.cpp +411 -0
  421. package/thirdparty/pugixml/tests/test_xpath_xalan_2.cpp +417 -0
  422. package/thirdparty/pugixml/tests/test_xpath_xalan_3.cpp +321 -0
  423. package/thirdparty/pugixml/tests/test_xpath_xalan_4.cpp +300 -0
  424. package/thirdparty/pugixml/tests/test_xpath_xalan_5.cpp +295 -0
  425. package/thirdparty/pugixml/tests/writer_string.cpp +79 -0
  426. package/thirdparty/pugixml/tests/writer_string.hpp +27 -0
  427. package/ios/dummy.swift +0 -2
  428. package/scripts/prepare-openxlsx.js +0 -109
@@ -0,0 +1,1375 @@
1
+ #ifndef ZIPPY2_LIBRARY_H
2
+ #define ZIPPY2_LIBRARY_H
3
+
4
+ #include <algorithm>
5
+ #include <ctime>
6
+ #include <fstream>
7
+ #include <miniz.h>
8
+ #include <miniz_zip.h>
9
+ #include <random>
10
+ #include <stdexcept>
11
+ #include <sys/stat.h>
12
+
13
+ #ifdef _WIN32
14
+ #include <direct.h>
15
+ #endif
16
+
17
+ #include "detail/OpenXLSXFileSystemTools.hpp" // OpenXLSX::GenerateRandomNameInSamePath, OpenXLSX::remove, OpenXLSX::rename
18
+
19
+ // 2024-09-15: moved Zippy exceptions to top of module to be able to use them earlier - forward declaration didn't seem
20
+ // to work
21
+ namespace Zippy
22
+ {
23
+ /**
24
+ * @brief provide a (const) library name to the caller
25
+ * @details
26
+ */
27
+ constexpr const char *ZipLibraryName() { return "miniz"; };
28
+
29
+ /**
30
+ * @brief get the library version from miniz
31
+ * @details provide a pass-through function to mz_version
32
+ */
33
+ const char *ZipLibraryVersion() { return mz_version(); }
34
+
35
+ /**
36
+ * @brief The ZipRuntimeError class is a custom exception class derived from the std::runtime_error class.
37
+ * @details In case of an error in the Zippy library, an ZipRuntimeError object will be thrown, with a message
38
+ * describing the details of the error.
39
+ */
40
+ class ZipRuntimeError : public std::runtime_error
41
+ {
42
+ public:
43
+ /**
44
+ * @brief Constructor.
45
+ * @param err A string with a description of the error.
46
+ */
47
+ inline explicit ZipRuntimeError(const std::string& err) : runtime_error(err) {}
48
+
49
+ /**
50
+ * @brief Destructor.
51
+ */
52
+ inline ~ZipRuntimeError() override = default;
53
+ };
54
+
55
+ /**
56
+ * @brief
57
+ */
58
+ class ZipLogicError : public std::logic_error
59
+ {
60
+ public:
61
+ /**
62
+ * @brief
63
+ * @param err
64
+ */
65
+ inline explicit ZipLogicError(const std::string& err) : logic_error(err) {}
66
+
67
+ /**
68
+ * @brief
69
+ */
70
+ inline ~ZipLogicError() override = default;
71
+ };
72
+
73
+ } // namespace Zippy
74
+
75
+
76
+ namespace Zippy
77
+ {
78
+ // using namespace ns_miniz;
79
+
80
+ class ZipArchive;
81
+
82
+ class ZipEntry;
83
+
84
+ // ===== Alias Declarations
85
+
86
+ /**
87
+ * @brief The ZipEntryInfo entity is an alias of the mz_zip_archive_file_stat from the miniz library.
88
+ * @details The ZipEntryInfo/mz_zip_archive_file_stat struct holds various meta data related to a particular
89
+ * entry (or item) in a zip archive, such as: comments, file size, date stamp etc.
90
+ * @note A new ZipEntryInfo should not be created manually.
91
+ */
92
+ using ZipEntryInfo = mz_zip_archive_file_stat;
93
+
94
+ /**
95
+ * @brief The ZipEntryData entity is an alias for a std::vector of std::bytes.
96
+ * @details This is used as a generic container of file data of any kind, both character strings and binary.
97
+ * A vector of char or an array of char can also be used, but a vector of bytes makes it clearer that it can
98
+ * also be used for non-text data.
99
+ */
100
+ using ZipEntryData = std::vector<unsigned char>;
101
+
102
+ /**
103
+ * @brief The ZipEntryMetaData is essentially a wrapper around the ZipEntryInfo scruct, which is an alias for a
104
+ * miniz struct.
105
+ */
106
+ struct ZipEntryMetaData
107
+ {
108
+ /**
109
+ * @brief Constructor.
110
+ * @param info A reference to a ZipEntryInfo object.
111
+ */
112
+ explicit ZipEntryMetaData(const ZipEntryInfo& info)
113
+ : Index(info.m_file_index),
114
+ CompressedSize(info.m_comp_size),
115
+ UncompressedSize(info.m_uncomp_size),
116
+ IsDirectory(info.m_is_directory),
117
+ IsEncrypted(info.m_is_encrypted),
118
+ IsSupported(info.m_is_supported),
119
+ Filename(info.m_filename),
120
+ Comment(info.m_comment),
121
+ Time(info.m_time)
122
+ {}
123
+
124
+ uint32_t Index; /**< */
125
+ uint64_t CompressedSize; /**< */
126
+ uint64_t UncompressedSize; /**< */
127
+ bool IsDirectory; /**< */
128
+ bool IsEncrypted; /**< */
129
+ bool IsSupported; /**< */
130
+ std::string Filename; /**< */
131
+ std::string Comment; /**< */
132
+ const time_t Time; /**< */
133
+ };
134
+
135
+ namespace Impl
136
+ {
137
+ /**
138
+ * @brief The Impl::ZipEntry class implements the functionality required for manipulating entries in a zip archive.
139
+ * @details This is the implementation class. The ZipEntry class in the Zippy namespace implements the public interface.
140
+ * The reason for having a separate implementation class, is that it enables easy copy and move operations of
141
+ * ZipEntry objects, without duplicating the underlying implementation objects.
142
+ */
143
+ class ZipEntry
144
+ {
145
+ friend class Zippy::ZipArchive;
146
+
147
+ friend class Zippy::ZipEntry;
148
+
149
+ public:
150
+ // ===== Constructors, Destructor and Operators
151
+
152
+ /**
153
+ * @brief Constructor. Creates a new ZipEntry with the given ZipEntryInfo parameter. This is only used for creating
154
+ * a ZipEntry for an entry already present in the ZipArchive, when opening an existing archive.
155
+ * @param info A reference to a ZipEntryInfo object with the entry info.
156
+ */
157
+ explicit ZipEntry(const ZipEntryInfo& info) : m_EntryInfo(info)
158
+ {
159
+ // ===== Call GetNewIndex to update the index counter with the getValue in the ZipEntryInfo object.
160
+ GetNewIndex(info.m_file_index); // The return getValue is deliberately not used.
161
+ }
162
+
163
+ /**
164
+ * @brief Constructor. Creates a new ZipEntry with the given name and binary data. This should only be used for creating
165
+ * new entries, not already present in the ZipArchive
166
+ * @param name The name of the new entry to add to the zip archive.
167
+ * @param data A reference to a ZipEntryData object with the file data to add.
168
+ */
169
+ ZipEntry(const std::string& name, const ZipEntryData& data)
170
+ {
171
+ // ===== Create new ZipEntryData object
172
+ m_EntryInfo = CreateInfo(name);
173
+
174
+ // ===== Assign the input data to the m_EntryData variable and set the m_IsModified flag to true.
175
+ m_EntryData = data;
176
+ m_IsModified = true;
177
+ }
178
+
179
+ /**
180
+ * @brief Constructor. Creates a new ZipEntry with the given name and string data. This should only be used for creating
181
+ * new entries, not already present in the ZipArchive
182
+ * @param name The name of the new entry to add to the zip archive.
183
+ * @param data A string with the text data to add to the zip archive.
184
+ */
185
+ ZipEntry(const std::string& name, const std::string& data)
186
+ {
187
+ // ===== Create new ZipEntryData object
188
+ m_EntryInfo = CreateInfo(name);
189
+
190
+ // ===== Copy the string data to the m_EntryData member and set the m_IsModified flag to true.
191
+ m_EntryData.reserve(data.size());
192
+ for (const auto& ch : data) {
193
+ m_EntryData.emplace_back(ch);
194
+ }
195
+ m_IsModified = true;
196
+ }
197
+
198
+ /**
199
+ * @brief Copy Constructor (Deleted)
200
+ * @param other The object to copy.
201
+ * @note The copy constructor has been deleted, because it is not obvious what should happen to the
202
+ * underlying file when copying the entry object.
203
+ */
204
+ ZipEntry(const ZipEntry& other) = delete;
205
+
206
+ /**
207
+ * @brief Move Constructor.
208
+ * @param other The object to be moved.
209
+ */
210
+ ZipEntry(ZipEntry&& other) noexcept = default;
211
+
212
+ /**
213
+ * @brief Destructor.
214
+ */
215
+ virtual ~ZipEntry() = default;
216
+
217
+ /**
218
+ * @brief Copy Assignment Operator (deleted)
219
+ * @param other The object to be copied.
220
+ * @return A reference to the copied-to object.
221
+ * @note The copy assignment operator has been deleted, because it is not obvious what should happen to the
222
+ * underlying file when copying the entry object.
223
+ */
224
+ ZipEntry& operator=(const ZipEntry& other) = delete;
225
+
226
+ /**
227
+ * @brief Move Assignment Operator.
228
+ * @param other The object to be moved.
229
+ * @return A reference to the moved-to object.
230
+ */
231
+ ZipEntry& operator=(ZipEntry&& other) noexcept = default;
232
+
233
+ // ===== Data Access and Manipulation
234
+
235
+ /**
236
+ * @brief Get data from entry.
237
+ * @return Returns a ZipEntryData (alias for std::vector<std::byte>) object with the fil data.
238
+ */
239
+ ZipEntryData GetData() const
240
+ {
241
+ return m_EntryData;
242
+ }
243
+
244
+ /**
245
+ * @brief Get entry data as a std::string.
246
+ * @return Returns a std::string with the file data.
247
+ */
248
+ std::string GetDataAsString() const
249
+ {
250
+ std::string result;
251
+ for (const auto& ch : m_EntryData) {
252
+ result += static_cast<char>(ch);
253
+ } // TODO: Should this use reinterpret_cast instead?
254
+
255
+ return result;
256
+ }
257
+
258
+ /**
259
+ * @brief Set the data for the entry.
260
+ * @param data A std::string with the file data.
261
+ */
262
+ void SetData(const std::string& data)
263
+ {
264
+ ZipEntryData result;
265
+
266
+ for (const auto& ch : data) {
267
+ result.push_back(ch);
268
+ }
269
+
270
+ m_EntryData = result;
271
+ m_IsModified = true;
272
+ }
273
+
274
+ /**
275
+ * @brief Set the data for the entry.
276
+ * @param data A ZipEntryData (alias for std::vector<std::byte>) object with the file data.
277
+ */
278
+ void SetData(const ZipEntryData& data)
279
+ {
280
+ m_EntryData = data;
281
+ m_IsModified = true;
282
+ }
283
+
284
+ /**
285
+ * @brief Get the name of the entry.
286
+ * @return Returns a std::string with the entry name.
287
+ */
288
+ std::string GetName() const
289
+ {
290
+ return m_EntryInfo.m_filename;
291
+ }
292
+
293
+ /**
294
+ * @brief Set the name of the entry.
295
+ * @param name A std::string with the new name of the entry.
296
+ */
297
+ void SetName(const std::string& name)
298
+ {
299
+ #if _MSC_VER // On MSVC, use the safe version of strcpy
300
+ strcpy_s(m_EntryInfo.m_filename, sizeof m_EntryInfo.m_filename, name.c_str());
301
+ #else // Otherwise, use the unsafe version as fallback :(
302
+ strncpy(m_EntryInfo.m_filename, name.c_str(), sizeof m_EntryInfo.m_filename);
303
+ #endif
304
+ }
305
+
306
+ // ===== Metadata Access
307
+
308
+ /**
309
+ * @brief Get the entry's index in the zip archive.
310
+ * @return Returns a uint32_t with the entry index.
311
+ */
312
+ uint32_t Index() const
313
+ {
314
+ return m_EntryInfo.m_file_index;
315
+ }
316
+
317
+ /**
318
+ * @brief Get the uncompressed size of the entry (in bytes)
319
+ * @return A uint64_t with the uncompressed size.
320
+ */
321
+ uint64_t CompressedSize() const
322
+ {
323
+ return m_EntryInfo.m_comp_size;
324
+ }
325
+
326
+ /**
327
+ * @brief Get the compressed size of the entry (in bytes)
328
+ * @return A uint64_t with the compressed size.
329
+ */
330
+ uint64_t UncompressedSize() const
331
+ {
332
+ return m_EntryInfo.m_uncomp_size;
333
+ }
334
+
335
+ /**
336
+ * @brief Is the zip entry a directory?
337
+ * @return Returns true if the entry is a directory; otherwise false.
338
+ */
339
+ bool IsDirectory() const
340
+ {
341
+ return m_EntryInfo.m_is_directory;
342
+ }
343
+
344
+ /**
345
+ * @brief
346
+ * @return Returns true if the entry is encrypted; otherwise false.
347
+ */
348
+ bool IsEncrypted() const
349
+ {
350
+ return m_EntryInfo.m_is_encrypted;
351
+ }
352
+
353
+ /**
354
+ * @brief Is the zip entry encryption supported?
355
+ * @return Returns true if the entry encryption (if any) is supported by the library; otherwise false.
356
+ */
357
+ bool IsSupported() const
358
+ {
359
+ return m_EntryInfo.m_is_supported;
360
+ }
361
+
362
+ /**
363
+ * @brief Get the zip entry comments.
364
+ * @return Returns a std::string with the comments.
365
+ */
366
+ std::string Comment() const
367
+ {
368
+ return m_EntryInfo.m_comment;
369
+ }
370
+
371
+ /**
372
+ * @brief Get the zip entry time stamp.
373
+ * @return Returns a time_t object with the time stamp.
374
+ */
375
+ const time_t& Time() const
376
+ {
377
+ return m_EntryInfo.m_time;
378
+ }
379
+
380
+ private:
381
+ ZipEntryInfo m_EntryInfo = ZipEntryInfo(); /**< The zip entry metadata. */
382
+ ZipEntryData m_EntryData = ZipEntryData(); /**< The zip entry data. */
383
+
384
+ bool m_IsModified = false; /**< Boolean flag indicating if the file has been modified since opening. */
385
+
386
+ /**
387
+ * @brief Has the zip entry been modified?
388
+ * @return Returns true if the entry is has been modified; otherwise false.
389
+ */
390
+ bool IsModified() const
391
+ {
392
+ return m_IsModified;
393
+ }
394
+
395
+ /**
396
+ * @brief Generate a new file index.
397
+ * @details The file index in existing zip archives may not be incrementing trivially. When opening existing
398
+ * zip archives, this function is simply used to update the index. When adding new entries to an existing
399
+ * archive, this function is guaranteed to provide a unique index.
400
+ * @return Returns a uint32_t (32 bit unsigned int) with the new index.
401
+ */
402
+ static uint32_t GetNewIndex(uint32_t latestIndex = 0)
403
+ {
404
+ // ===== Set up a static index counter (set to zero the first time the function is executed)
405
+ static uint32_t index { 0 };
406
+
407
+ // ===== If the input value is larger than the current index getValue, set the index equal to the input.
408
+ if (latestIndex > index) {
409
+ index = latestIndex;
410
+ return index;
411
+ }
412
+
413
+ // ===== Increment the index and return the getValue.
414
+ return ++index;
415
+ }
416
+
417
+ /**
418
+ * @brief Create a new ZipEntryInfo structure.
419
+ * @details This function will create a new ZipEntryInfo structure, based on the input file name. The
420
+ * structure values will mostly be dummy values, except for the file index, the time stamp, the file name
421
+ * and the is_directory flag.
422
+ * @param name The file name for the new ZipEntryInfo.
423
+ * @return The newly created ZipEntryInfo structure is returned
424
+ */
425
+ static ZipEntryInfo CreateInfo(const std::string& name)
426
+ {
427
+ ZipEntryInfo info;
428
+
429
+ info.m_file_index = GetNewIndex(0);
430
+ info.m_central_dir_ofs = 0;
431
+ info.m_version_made_by = 0;
432
+ info.m_version_needed = 0;
433
+ info.m_bit_flag = 0;
434
+ info.m_method = 0;
435
+ info.m_time = time(nullptr);
436
+ info.m_crc32 = 0;
437
+ info.m_comp_size = 0;
438
+ info.m_uncomp_size = 0;
439
+ info.m_internal_attr = 0;
440
+ info.m_external_attr = 0;
441
+ info.m_local_header_ofs = 0;
442
+ info.m_comment_size = 0;
443
+ info.m_is_directory = (name.back() == '/');
444
+ info.m_is_encrypted = false;
445
+ info.m_is_supported = true;
446
+
447
+ #if _MSC_VER // On MSVC, use the safe version of strcpy
448
+ strcpy_s(info.m_filename, sizeof info.m_filename, name.c_str());
449
+ strcpy_s(info.m_comment, sizeof info.m_comment, "");
450
+ #else // Otherwise, use the unsafe version as fallback :(
451
+ strncpy(info.m_filename, name.c_str(), sizeof info.m_filename);
452
+ strncpy(info.m_comment, "", sizeof info.m_comment);
453
+ #endif
454
+
455
+ return info;
456
+ }
457
+ };
458
+ } // namespace Impl
459
+
460
+ /**
461
+ * @brief The ZipEntry class implements the interface required for manipulating entries in a zip archive.
462
+ * @details This is the interface class. The ZipEntry class in the Zippy::Impl namespace implements the private implementation.
463
+ * The reason for having a separate implementation class, is that it enables easy copy and move operations of
464
+ * ZipEntry objects, without duplicating the underlying implementation objects.
465
+ */
466
+ class ZipEntry
467
+ {
468
+ friend class ZipArchive;
469
+
470
+ public:
471
+ // ===== Constructors, Destructor and Operators
472
+
473
+ /**
474
+ * @brief Constructor. Creates a new ZipEntry.
475
+ * @param entry A raw (non-owning) pointer to the existing Impl::ZipEntry object.
476
+ * @todo Can this be made private?
477
+ */
478
+ explicit ZipEntry(Impl::ZipEntry* entry) : m_ZipEntry(entry) {}
479
+
480
+ /**
481
+ * @brief Copy Constructor.
482
+ * @param other The object to be copied.
483
+ */
484
+ ZipEntry(const ZipEntry& other) = default;
485
+
486
+ /**
487
+ * @brief Move Constructor.
488
+ * @param other The object to be moved.
489
+ */
490
+ ZipEntry(ZipEntry&& other) noexcept = default;
491
+
492
+ /**
493
+ * @brief Destructor
494
+ */
495
+ virtual ~ZipEntry() = default;
496
+
497
+ /**
498
+ * @brief Copy Assignment Operator.
499
+ * @param other The object to be copied.
500
+ * @return A reference to the copied-to object.
501
+ */
502
+ ZipEntry& operator=(const ZipEntry& other) = default;
503
+
504
+ /**
505
+ * @brief Move Assignment Operator.
506
+ * @param other The object to be moved.
507
+ * @return A reference to the moved-to object.
508
+ */
509
+ ZipEntry& operator=(ZipEntry&& other) noexcept = default;
510
+
511
+ // ===== Data Access and Manipulation
512
+
513
+ /**
514
+ * @brief Get data from entry.
515
+ * @return Returns a ZipEntryData (alias for std::vector<std::byte>) object with the file data.
516
+ */
517
+ ZipEntryData GetData() const
518
+ {
519
+ return m_ZipEntry->GetData();
520
+ }
521
+
522
+ /**
523
+ * @brief Get entry data as a std::string.
524
+ * @return Returns a std::string with the file data.
525
+ */
526
+ std::string GetDataAsString() const
527
+ {
528
+ return m_ZipEntry->GetDataAsString();
529
+ }
530
+
531
+ /**
532
+ * @brief Set the data for the entry.
533
+ * @param data A std::string with the file data.
534
+ */
535
+ void SetData(const std::string& data)
536
+ {
537
+ m_ZipEntry->SetData(data);
538
+ }
539
+
540
+ /**
541
+ * @brief Set the data for the entry.
542
+ * @param data A ZipEntryData (alias for std::vector<std::byte>) object with the file data.
543
+ */
544
+ void SetData(const ZipEntryData& data)
545
+ {
546
+ m_ZipEntry->SetData(data);
547
+ }
548
+
549
+ // ===== Metadata Access
550
+
551
+ /**
552
+ * @brief Get the entry's index in the zip archive.
553
+ * @return Returns a uint32_t with the entry index.
554
+ */
555
+ uint32_t Index() const
556
+ {
557
+ return m_ZipEntry->Index();
558
+ }
559
+
560
+ /**
561
+ * @brief Get the uncompressed size of the entry (in bytes)
562
+ * @return A uint64_t with the uncompressed size.
563
+ */
564
+ uint64_t CompressedSize() const
565
+ {
566
+ return m_ZipEntry->CompressedSize();
567
+ }
568
+
569
+ /**
570
+ * @brief Get the compressed size of the entry (in bytes)
571
+ * @return A uint64_t with the compressed size.
572
+ */
573
+ uint64_t UncompressedSize() const
574
+ {
575
+ return m_ZipEntry->UncompressedSize();
576
+ }
577
+
578
+ /**
579
+ * @brief Is the entry a directory?
580
+ * @return Returns true if the entry is a directory; otherwise false.
581
+ */
582
+ bool IsDirectory() const
583
+ {
584
+ return m_ZipEntry->IsDirectory();
585
+ }
586
+
587
+ /**
588
+ * @brief Is the zip entry supported?
589
+ * @return Returns true if the entry is encrypted; otherwise false.
590
+ */
591
+ bool IsEncrypted() const
592
+ {
593
+ return m_ZipEntry->IsEncrypted();
594
+ }
595
+
596
+ /**
597
+ * @brief Is the zip entry encryption supported?
598
+ * @return Returns true if the entry encryption (if any) is supported; otherwise false.
599
+ */
600
+ bool IsSupported() const
601
+ {
602
+ return m_ZipEntry->IsSupported();
603
+ }
604
+
605
+ /**
606
+ * @brief Get the entry filename.
607
+ * @return Returns a std::string with the filename.
608
+ * @todo Consider renaming to GetName and add a SetName member function.
609
+ */
610
+ std::string Filename() const
611
+ {
612
+ return m_ZipEntry->GetName();
613
+ }
614
+
615
+ /**
616
+ * @brief Get the zip entry comments.
617
+ * @return Returns a std::string with the comments.
618
+ */
619
+ std::string Comment() const
620
+ {
621
+ return m_ZipEntry->Comment();
622
+ }
623
+
624
+ /**
625
+ * @brief Get the entry time stamp.
626
+ * @return Returns a time_t object with the time stamp.
627
+ */
628
+ const time_t& Time() const
629
+ {
630
+ return m_ZipEntry->Time();
631
+ }
632
+
633
+ private:
634
+ /**
635
+ * @brief Has the file been modified?
636
+ * @return Returns true if the entry has been modified; otherwise false.
637
+ */
638
+ bool IsModified() const
639
+ {
640
+ return m_ZipEntry->IsModified();
641
+ }
642
+
643
+ Impl::ZipEntry* m_ZipEntry; /**< A raw (non-owning) pointer to the implementation object. */
644
+ };
645
+ } // namespace Zippy
646
+
647
+ namespace Zippy
648
+ {
649
+ /**
650
+ * @brief The ZipArchive class represents the zip archive file as a whole. It consists of the individual zip entries, which
651
+ * can be both files and folders. It is the main access point into a .zip archive on disk and can be
652
+ * used to create new archives and to open and modify existing archives.
653
+ * @details
654
+ * #### Implementation and usage details
655
+ * Using the ZipArchive class, it is possible to create new .zip archive files, as well as open and modify existing ones.
656
+ *
657
+ * A ZipArchive object holds a mz_zip_archive object (a miniz struct representing a .zip archive) as well as a std::vector
658
+ * with ZipEntry objects representing each file (entry) in the archive. The actual entry data is lazy-instantiated, so that
659
+ * the data is only loaded from the .zip archive when it is actually needed.
660
+ *
661
+ * The following example shows how a new .zip archive can be created and new entries added.
662
+ * ```cpp
663
+ * int main() {
664
+ *
665
+ * // ===== Creating empty archive
666
+ * Zippy::ZipArchive arch;
667
+ * arch.Create("./TestArchive.zip");
668
+ *
669
+ * // ===== Adding 10 entries to the archive
670
+ * for (int i = 0; i <= 9; ++i)
671
+ * arch.AddEntry(to_string(i) + ".txt", "this is test " + to_string(i));
672
+ *
673
+ * // ===== Delete the first entry
674
+ * arch.DeleteEntry("0.txt");
675
+ *
676
+ * // ===== Save and close the archive
677
+ * arch.Save();
678
+ * arch.Close();
679
+ *
680
+ * // ===== Reopen and check contents
681
+ * arch.Open("./TestArchive.zip");
682
+ * cout << "Number of entries in archive: " << arch.GetNumEntries() << endl;
683
+ * cout << "Content of \"9.txt\": " << endl << arch.GetEntry("9.txt").GetDataAsString();
684
+ *
685
+ * return 0;
686
+ * }
687
+ * ```
688
+ *
689
+ * For further information, please refer to the full API documentation below.
690
+ *
691
+ * Note that the actual files in the .zip archive can be retrieved via the ZipEntry interface, not the ZipArchive interface.
692
+ */
693
+ class ZipArchive
694
+ {
695
+ public:
696
+ /**
697
+ * @brief Constructor. Constructs a null-archive, which can be used for creating a new .zip file
698
+ * or opening an existing one.
699
+ * @warning Before using an archive object that has been default constructed, a call to either Open() or Create() must be
700
+ * performed. Otherwise, the object will be in a null-state and calls to member functions will be undefined.
701
+ */
702
+ explicit ZipArchive()
703
+ : m_zipData(nullptr), m_zipSize(0) {}
704
+
705
+ /**
706
+ * @brief Constructor. Constructs an archive object, using the fileName input parameter. If the file already exists,
707
+ * it will be opened. Otherwise, a new object will be created.
708
+ * @details
709
+ * #### Implementation details
710
+ * The constructors tries to open an std::ifstream. If it is valid, it means that a file already exists
711
+ * and will be opened. Otherwise, the file does not exist and will be created.
712
+ * @param fileName The name of the file to open or create.
713
+ */
714
+ explicit ZipArchive(const std::string& fileName) : m_ArchivePath(fileName)
715
+ {
716
+ // ===== Open file stream
717
+ std::ifstream f(fileName.c_str());
718
+
719
+ // ===== If successful, continue to open the file.
720
+ if (f.good()) {
721
+ f.close();
722
+ Open(fileName);
723
+ }
724
+
725
+ // ===== If unsuccessful, create the archive file and continue.
726
+ else {
727
+ f.close();
728
+ Create(fileName);
729
+ }
730
+ }
731
+
732
+ /**
733
+ * @brief Copy Constructor (deleted).
734
+ * @param other The object to copy.
735
+ * @note The copy constructor has been deleted, because it is not obvious what should happen to the underlying .zip file
736
+ * when copying the archive object. Instead, if sharing of the resource is required, a std::shared_ptr can be used.
737
+ */
738
+ ZipArchive(const ZipArchive& other) = delete;
739
+
740
+ /**
741
+ * @brief Move Constructor.
742
+ * @param other The object to be moved.
743
+ */
744
+ ZipArchive(ZipArchive&& other) = default;
745
+
746
+ /**
747
+ * @brief Destructor.
748
+ * @note The destructor will call the Close() member function. If the archive has been modified but not saved,
749
+ * all changes will be discarded.
750
+ */
751
+ virtual ~ZipArchive()
752
+ {
753
+ Close();
754
+ }
755
+
756
+ /**
757
+ * @brief Copy Assignment Operator (deleted)
758
+ * @param other The object to be copied.
759
+ * @return A reference to the copied-to object.
760
+ * @note The copy assignment operator has been deleted, because it is not obvious what should happen to the underlying
761
+ * .zip file when copying the archive object. Instead, if sharing of the resource is required,
762
+ * a std::shared_ptr can be used.
763
+ */
764
+ ZipArchive& operator=(const ZipArchive& other) = delete;
765
+
766
+ /**
767
+ * @brief Move Assignment Operator.
768
+ * @param other The object to be moved.
769
+ * @return A reference to the moved-to object.
770
+ */
771
+ ZipArchive& operator=(ZipArchive&& other) = default;
772
+
773
+ /**
774
+ * @brief Creates a new (empty) archive file with the given filename.
775
+ * @details
776
+ * #### Implementation details
777
+ * A new archive file is created and initialized and thereafter closed, creating an empty archive file.
778
+ * After ensuring that the file is valid (i.e. not somehow corrupted), it is opened using the Open() member function.
779
+ * @param fileName The filename for the new archive.
780
+ */
781
+ void Create(const std::string& fileName)
782
+ {
783
+ // ===== Prepare an archive file;
784
+ mz_zip_archive archive = mz_zip_archive();
785
+ mz_zip_writer_init_file(&archive, fileName.c_str(), 0);
786
+
787
+ // ===== Finalize and close the temporary archive
788
+ mz_zip_writer_finalize_archive(&archive);
789
+ mz_zip_writer_end(&archive);
790
+
791
+ // ===== Validate the temporary file
792
+ mz_zip_error errordata;
793
+ if (!mz_zip_validate_file_archive(fileName.c_str(), 0, &errordata)) {
794
+ // throw ZipRuntimeError(mz_zip_get_error_string(errordata));
795
+ throw ZipRuntimeError(std::string(mz_zip_get_error_string(errordata)) + " (m_ArchivePath: " + m_ArchivePath + ")");
796
+ }
797
+
798
+ // ===== If everything is OK, open the newly created archive.
799
+ Open(fileName);
800
+ }
801
+
802
+ /**
803
+ * @brief load data from filename into this->m_zipData
804
+ * @param filename load a file from here
805
+ * @return 0 on success, -1 on failure
806
+ * @note 2026-06-01 this function is identical to the one defined in LibZip.hpp
807
+ */
808
+ int loadArchiveData(std::string filename)
809
+ {
810
+ m_zipSize = 0; // init in case of failure
811
+
812
+ off_t archiveSizeCompressed = OpenXLSX::fileSize( filename );
813
+ if (archiveSizeCompressed < 0) {
814
+ fprintf(stderr, "ZipArchive::loadArchiveData: can't obtain info about %s (errno %d: %s)\n", filename.c_str(), errno, strerror(errno));
815
+ m_zipData = nullptr;
816
+ return -1;
817
+ }
818
+
819
+ if ((m_zipData = malloc(static_cast<size_t>(archiveSizeCompressed))) == nullptr) {
820
+ fprintf(stderr, "ZipArchive::loadArchiveData: can't allocate buffer\n");
821
+ return -1;
822
+ }
823
+
824
+ FILE *fp = OpenXLSX::fopen(filename.c_str(), "rb");
825
+ if (fp == nullptr) {
826
+ free(m_zipData);
827
+ m_zipData = nullptr;
828
+ fprintf(stderr, "ZipArchive::loadArchiveData: can't open %s: %s\n", filename.c_str(), strerror(errno));
829
+ return -1;
830
+ }
831
+
832
+ if (fread(m_zipData, 1, static_cast<size_t>(archiveSizeCompressed), fp) < static_cast<size_t>(archiveSizeCompressed)) {
833
+ free(m_zipData);
834
+ m_zipData = nullptr;
835
+ fprintf(stderr, "ZipArchive::loadArchiveData: can't read %s: %s\n", filename.c_str(), strerror(errno));
836
+ fclose(fp);
837
+ return -1;
838
+ }
839
+
840
+ fclose(fp);
841
+
842
+ m_zipSize = static_cast<size_t>(archiveSizeCompressed);
843
+ return 0;
844
+ }
845
+
846
+ /**
847
+ * @warning unicode filename support on Windows to be tested!
848
+ * @brief Open an existing archive file with the given filename.
849
+ * @details
850
+ * ##### Implementation details
851
+ * The archive file is opened and meta data for all the entries in the archive is loaded into memory.
852
+ * @param fileName The filename of the archive to open.
853
+ * @note If more than one entry with the same name exists in the archive, only the newest one will be loaded.
854
+ * When saving the archive, only the loaded entries will be kept; other entries with the same name will be deleted.
855
+ */
856
+ void Open(const std::string& fileName)
857
+ {
858
+ // ===== Open the archive file for reading.
859
+ if (m_IsOpen) {
860
+ mz_zip_reader_end(&m_Archive);
861
+ }
862
+ m_ArchivePath = fileName;
863
+
864
+ /* get buffer with zip archive inside */
865
+ if (loadArchiveData(fileName) < 0) {
866
+ using namespace std::literals::string_literals;
867
+ throw ZipRuntimeError("ZipArchive::Open: failed to load archive data from file "s + fileName);
868
+ }
869
+
870
+ // if (!mz_zip_reader_init_file(&m_Archive, m_ArchivePath.c_str(), 0)) { // TBD: does miniz support unicode filenames on Windows?
871
+ if (!mz_zip_reader_init_mem(&m_Archive, m_zipData, m_zipSize, 0)) {
872
+ // throw ZipRuntimeError(mz_zip_get_error_string(m_Archive.m_last_error));
873
+ throw ZipRuntimeError(std::string(mz_zip_get_error_string(m_Archive.m_last_error)) + " (m_ArchivePath: " + m_ArchivePath + ")");
874
+ }
875
+ m_IsOpen = true;
876
+
877
+ // ===== Iterate through the archive and add the entries to the internal data structure
878
+ for (unsigned int i = 0; i < mz_zip_reader_get_num_files(&m_Archive); i++) {
879
+ ZipEntryInfo info;
880
+ if (!mz_zip_reader_file_stat(&m_Archive, i, &info)) {
881
+ // throw ZipRuntimeError(mz_zip_get_error_string(m_Archive.m_last_error));
882
+ throw ZipRuntimeError(std::string(mz_zip_get_error_string(m_Archive.m_last_error)) + " (m_ArchivePath: " + m_ArchivePath + ")");
883
+ }
884
+
885
+ m_ZipEntries.emplace_back(Impl::ZipEntry(info));
886
+ }
887
+
888
+ // ===== Remove entries with identical names. The newest entries will be retained.
889
+ auto isEqual = [](const Impl::ZipEntry& a, const Impl::ZipEntry& b) { return a.GetName() == b.GetName(); };
890
+ std::reverse(m_ZipEntries.begin(), m_ZipEntries.end());
891
+ m_ZipEntries.erase(std::unique(m_ZipEntries.begin(), m_ZipEntries.end(), isEqual), m_ZipEntries.end());
892
+ std::reverse(m_ZipEntries.begin(), m_ZipEntries.end());
893
+
894
+ // ===== Add folder entries if they don't exist
895
+ for (auto& entry : GetEntryNames(false, true)) {
896
+ if (entry.find('/') != std::string::npos) {
897
+ AddEntry(entry.substr(0, entry.rfind('/') + 1), "");
898
+ }
899
+ }
900
+ }
901
+
902
+ /**
903
+ * @brief Close the archive for reading and writing.
904
+ * @note If the archive has been modified but not saved, all changes will be discarded.
905
+ */
906
+ void Close()
907
+ {
908
+ if (IsOpen()) {
909
+ mz_zip_reader_end(&m_Archive);
910
+ free(m_zipData);
911
+ m_zipData = nullptr; // prevent double-free
912
+ }
913
+ m_ArchivePath = "";
914
+ m_IsOpen = false; // 2024-12-18: minor bugfix, m_IsOpen was not set to false
915
+ m_ZipEntries.clear();
916
+ }
917
+
918
+ /**
919
+ * @brief Checks if the archive file is open for reading and writing.
920
+ * @return true if it is open; otherwise false;
921
+ */
922
+ bool IsOpen() const
923
+ {
924
+ return m_IsOpen;
925
+ }
926
+
927
+ /**
928
+ * @brief Get a list of the entries in the archive. Depending on the input parameters, the list will include
929
+ * directories, files or both.
930
+ * @param includeDirs If true, the list will include directories; otherwise not. Default is true
931
+ * @param includeFiles If true, the list will include files; otherwise not. Default is true
932
+ * @return A std::vector of std::strings with the entry names.
933
+ */
934
+ std::vector<std::string> GetEntryNames(bool includeDirs = true, bool includeFiles = true) const
935
+ {
936
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetEntryNames on empty ZipArchive object!");
937
+
938
+ std::vector<std::string> result;
939
+
940
+ // ===== Iterate through all the entries in the archive
941
+ for (auto& item : m_ZipEntries) {
942
+ // ===== If directories should be included and the current entry is a directory, add it to the result.
943
+ if (includeDirs && item.IsDirectory()) {
944
+ result.emplace_back(item.GetName());
945
+ continue;
946
+ }
947
+
948
+ // ===== If files should be included and the current entry is a file, add it to the result.
949
+ if (includeFiles && !item.IsDirectory()) {
950
+ result.emplace_back(item.GetName());
951
+ continue;
952
+ }
953
+ }
954
+
955
+ return result;
956
+ }
957
+
958
+ /**
959
+ * @brief Get a list of the entries in a specific directory of the archive. Depending on the input parameters,
960
+ * the list will include directories, files or both.
961
+ * @details This function behaves slightly different than the GetEntryNames() function. All entries in the
962
+ * requested folder will be returned as absolute paths; however, only one level of subfolders will be returned.
963
+ * @param dir The directory with the entries to get.
964
+ * @param includeDirs If true, the list will include directories; otherwise not. Default is true
965
+ * @param includeFiles If true, the list will include files; otherwise not. Default is true
966
+ * @return A std::vector of std::strings with the entry names. The directory itself is not included.
967
+ */
968
+ std::vector<std::string> GetEntryNamesInDir(const std::string& dir, bool includeDirs = true, bool includeFiles = true) const
969
+ {
970
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetEntryNamesInDir on empty ZipArchive object!");
971
+
972
+ // ===== Get the full list of entries
973
+ auto result = GetEntryNames(includeDirs, includeFiles);
974
+
975
+ // ===== Remove all entries not in the directory in question, as well as the root directory itself.
976
+ if (!dir.empty()) {
977
+ auto theDir = dir;
978
+ if (theDir.back() != '/') {
979
+ theDir += '/';
980
+ }
981
+
982
+ result.erase(std::remove_if(result.begin(),
983
+ result.end(),
984
+ [&](const std::string& filename) {
985
+ return filename.substr(0, theDir.size()) != theDir || filename == theDir;
986
+ }),
987
+ result.end());
988
+ }
989
+
990
+ // ===== Ensure that only one level of subdirectories are included.
991
+ result.erase(std::remove_if(result.begin(),
992
+ result.end(),
993
+ [&](const std::string& filename) {
994
+ auto subFolderDepth = std::count(filename.begin(), filename.end(), '/');
995
+ auto rootDepth =
996
+ (dir.empty() ? 1
997
+ : (dir.back() == '/' ? std::count(dir.begin(), dir.end(), '/') + 1
998
+ : std::count(dir.begin(), dir.end(), '/') + 2));
999
+ return (subFolderDepth == rootDepth && filename.back() != '/') || (subFolderDepth > rootDepth);
1000
+ }),
1001
+ result.end());
1002
+
1003
+ return result;
1004
+ }
1005
+
1006
+ /**
1007
+ * @brief Get a list of the metadata for entries in the archive. Depending on the input parameters, the list will include
1008
+ * directories, files or both.
1009
+ * @param includeDirs If true, the list will include directories; otherwise not. Default is true
1010
+ * @param includeFiles If true, the list will include files; otherwise not. Default is true
1011
+ * @return A std::vector of ZipEntryMetaData structs with the entry metadata.
1012
+ */
1013
+ std::vector<ZipEntryMetaData> GetMetaData(bool includeDirs = true, bool includeFiles = true)
1014
+ {
1015
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetMetaData on empty ZipArchive object!");
1016
+
1017
+ std::vector<ZipEntryMetaData> result;
1018
+ for (auto& item : m_ZipEntries) {
1019
+ if (includeDirs && item.IsDirectory()) {
1020
+ result.emplace_back(ZipEntryMetaData(item.m_EntryInfo));
1021
+ continue;
1022
+ }
1023
+
1024
+ if (includeFiles && !item.IsDirectory()) {
1025
+ result.emplace_back(ZipEntryMetaData(item.m_EntryInfo));
1026
+ continue;
1027
+ }
1028
+ }
1029
+
1030
+ return result;
1031
+ }
1032
+
1033
+ /**
1034
+ * @brief Get a list of the metadata for entries in a specific directory of the archive. Depending on the input
1035
+ * parameters, the list will include directories, files or both.
1036
+ * @param dir The directory with the entries to get.
1037
+ * @param includeDirs If true, the list will include directories; otherwise not. Default is true
1038
+ * @param includeFiles If true, the list will include files; otherwise not. Default is true
1039
+ * @return A std::vector of ZipEntryMetaData structs with the entry metadata. The directory itself is not included.
1040
+ */
1041
+ std::vector<ZipEntryMetaData> GetMetaDataInDir(const std::string& dir, bool includeDirs = true, bool includeFiles = true)
1042
+ {
1043
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetMetaDataInDir on empty ZipArchive object!");
1044
+
1045
+ std::vector<ZipEntryMetaData> result;
1046
+ for (auto& item : m_ZipEntries) {
1047
+ if (item.GetName().substr(0, dir.size()) != dir) {
1048
+ continue;
1049
+ }
1050
+
1051
+ if (includeDirs && item.IsDirectory()) {
1052
+ result.emplace_back(ZipEntryMetaData(item.m_EntryInfo));
1053
+ continue;
1054
+ }
1055
+
1056
+ if (includeFiles && !item.IsDirectory()) {
1057
+ result.emplace_back(ZipEntryMetaData(item.m_EntryInfo));
1058
+ continue;
1059
+ }
1060
+ }
1061
+
1062
+ return result;
1063
+ }
1064
+
1065
+ /**
1066
+ * @brief Get the number of entries in the archive. Depending on the input parameters, the number will include
1067
+ * directories, files or both.
1068
+ * @param includeDirs If true, the number will include directories; otherwise not. Default is true
1069
+ * @param includeFiles If true, the number will include files; otherwise not. Default is true
1070
+ * @return An int with the number of entries.
1071
+ */
1072
+ int GetNumEntries(bool includeDirs = true, bool includeFiles = true) const
1073
+ {
1074
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetNumEntries on empty ZipArchive object!");
1075
+
1076
+ return GetEntryNames(includeDirs, includeFiles).size();
1077
+ }
1078
+
1079
+ /**
1080
+ * @brief Get the number of entries in a specific directory of the archive. Depending on the input parameters,
1081
+ * the list will include directories, files or both.
1082
+ * @param dir The directory with the entries to get.
1083
+ * @param includeDirs If true, the number will include directories; otherwise not. Default is true.
1084
+ * @param includeFiles If true, the number will include files; otherwise not. Default is true.
1085
+ * @return An int with the number of entries. The directory itself is not included.
1086
+ */
1087
+ int GetNumEntriesInDir(const std::string& dir, bool includeDirs = true, bool includeFiles = true) const
1088
+ {
1089
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetNumEntriesInDir on empty ZipArchive object!");
1090
+
1091
+ return GetEntryNamesInDir(dir, includeDirs, includeFiles).size();
1092
+ }
1093
+
1094
+ /**
1095
+ * @brief Check if an entry with a given name exists in the archive.
1096
+ * @param entryName The name of the entry to check for.
1097
+ * @return true if it exists; otherwise false;
1098
+ */
1099
+ bool HasEntry(const std::string& entryName) const
1100
+ {
1101
+ if (!IsOpen()) throw ZipLogicError("Cannot call HasEntry on empty ZipArchive object!");
1102
+
1103
+ auto result = GetEntryNames(true, true);
1104
+ return std::find(result.begin(), result.end(), entryName) != result.end();
1105
+ }
1106
+
1107
+ /**
1108
+ * @brief Save the archive with a new name. The original archive will remain unchanged.
1109
+ * @param filename The new filename.
1110
+ * @note If no filename is provided, the file will be saved with the existing name, overwriting any existing data.
1111
+ * @throws ZipException A ZipException object is thrown if calls to miniz function fails.
1112
+ */
1113
+ void Save(std::string filename = "")
1114
+ {
1115
+ if (!IsOpen()) throw ZipLogicError("Cannot call Save on empty ZipArchive object!");
1116
+
1117
+ if (filename.empty()) {
1118
+ filename = m_ArchivePath;
1119
+ }
1120
+
1121
+ // ===== Generate a random file name with the same path as the current file
1122
+ std::string tempPath = OpenXLSX::GenerateRandomNameInSamePath(filename, 20);
1123
+
1124
+ // ===== Prepare an temporary archive file with the random filename;
1125
+ mz_zip_archive tempArchive = mz_zip_archive();
1126
+ if (!mz_zip_writer_init_file(&tempArchive, tempPath.c_str(), 0)) // pull request #210
1127
+ throw ZipRuntimeError(mz_zip_get_error_string(tempArchive.m_last_error)); // "
1128
+
1129
+ // ===== Iterate through the ZipEntries and add entries to the temporary file
1130
+ for (auto& file : m_ZipEntries) {
1131
+ if (file.IsDirectory()) continue; // TODO: Ensure this is the right thing to do (Excel issue)
1132
+ if (!file.IsModified()) {
1133
+ if (!mz_zip_writer_add_from_zip_reader(&tempArchive, &m_Archive, file.Index())) {
1134
+ throw ZipRuntimeError(mz_zip_get_error_string(m_Archive.m_last_error));
1135
+ }
1136
+ }
1137
+
1138
+ else {
1139
+ if (!mz_zip_writer_add_mem(&tempArchive,
1140
+ file.GetName().c_str(),
1141
+ file.m_EntryData.data(),
1142
+ file.m_EntryData.size(),
1143
+ MZ_DEFAULT_COMPRESSION)) {
1144
+ throw ZipRuntimeError(mz_zip_get_error_string(m_Archive.m_last_error));
1145
+ }
1146
+ }
1147
+ }
1148
+
1149
+ // ===== Finalize and close the temporary archive
1150
+ mz_zip_writer_finalize_archive(&tempArchive);
1151
+ mz_zip_writer_end(&tempArchive);
1152
+
1153
+ // ===== Validate the temporary file
1154
+ mz_zip_error errordata;
1155
+ if (!mz_zip_validate_file_archive(tempPath.c_str(), 0, &errordata)) {
1156
+ throw ZipRuntimeError(mz_zip_get_error_string(errordata));
1157
+ }
1158
+
1159
+ // ===== Close the current archive, delete the file with input filename (if it exists), rename the temporary and call Open.
1160
+ Close();
1161
+ OpenXLSX::remove(filename.c_str());
1162
+ OpenXLSX::rename(tempPath.c_str(), filename.c_str()); // forward to function supporting unicode on Windows
1163
+ Open(filename);
1164
+ }
1165
+
1166
+ /**
1167
+ * @brief
1168
+ * @param stream
1169
+ */
1170
+ void Save(std::ostream& stream)
1171
+ {
1172
+ if (!IsOpen()) throw ZipLogicError("Cannot call ZipArchive::Save(std::ostream&) on empty ZipArchive object!");
1173
+ throw ZipLogicError("ZipArchive::Save(std::ostream&) is not yet implemented!"); // TODO: To be implemented
1174
+ (void) stream; // 2024-08-18: suppress -Wunused-parameter
1175
+ }
1176
+
1177
+ /**
1178
+ * @brief Deletes an entry from the archive.
1179
+ * @param name The name of the entry to delete.
1180
+ */
1181
+ void DeleteEntry(const std::string& name)
1182
+ {
1183
+ if (!IsOpen()) throw ZipLogicError("Cannot call DeleteEntry on empty ZipArchive object!");
1184
+
1185
+ m_ZipEntries.erase(std::remove_if(m_ZipEntries.begin(),
1186
+ m_ZipEntries.end(),
1187
+ [&](const Impl::ZipEntry& entry) { return name == entry.GetName(); }),
1188
+ m_ZipEntries.end());
1189
+ }
1190
+
1191
+ /**
1192
+ * @brief Get the entry with the specified name.
1193
+ * @param name The name of the entry in the archive.
1194
+ * @return A ZipEntry object with the requested entry.
1195
+ */
1196
+ ZipEntry GetEntry(const std::string& name)
1197
+ {
1198
+ if (!IsOpen()) throw ZipLogicError("Cannot call GetEntry on empty ZipArchive object!");
1199
+
1200
+ // ===== Look up ZipEntry object.
1201
+ auto result = std::find_if(m_ZipEntries.begin(), m_ZipEntries.end(), [&](const Impl::ZipEntry& entry) {
1202
+ return name == entry.GetName();
1203
+ });
1204
+
1205
+ // ===== If data has not been extracted from the archive (i.e., m_EntryData is empty),
1206
+ // ===== extract the data from the archive to the ZipEntry object.
1207
+ if (result->m_EntryData.empty()) {
1208
+ if (result->UncompressedSize())
1209
+ result->m_EntryData.resize(result->UncompressedSize());
1210
+ else
1211
+ result->m_EntryData.resize(1); // 2024-06-03 BUFIX: std::vector::data() can be nullptr when ::size() is 0, leading to a failure to load an empty file
1212
+ mz_zip_reader_extract_file_to_mem(&m_Archive, name.c_str(), result->m_EntryData.data(), result->m_EntryData.size(), 0);
1213
+ }
1214
+
1215
+ // ===== Check that the operation was successful
1216
+ if (!result->IsDirectory() && result->m_EntryData.data() == nullptr) {
1217
+ throw ZipRuntimeError(mz_zip_get_error_string(m_Archive.m_last_error));
1218
+ }
1219
+
1220
+ // ===== Return ZipEntry object with the file data.
1221
+ return ZipEntry(&*result);
1222
+ }
1223
+
1224
+ /**
1225
+ * @brief Extract the entry with the provided name to the destination path.
1226
+ * @param name The name of the entry to extract.
1227
+ * @param dest The path to extract the entry to.
1228
+ */
1229
+ void ExtractEntry(const std::string& name, const std::string& dest)
1230
+ {
1231
+ if (!IsOpen()) throw ZipLogicError("Cannot call ExtractEntry on empty ZipArchive object!");
1232
+
1233
+ auto entry = GetEntry(name);
1234
+
1235
+ // ===== If the entry is a directory, create the directory as a subdirectory to dest
1236
+ if (entry.IsDirectory()) {
1237
+ #ifdef _WIN32
1238
+ _mkdir((dest + entry.Filename()).c_str());
1239
+ #else
1240
+ mkdir((dest + entry.Filename()).c_str(), 0733);
1241
+ #endif
1242
+ }
1243
+
1244
+ // ===== If the entry is a file, stream the entry data to a file.
1245
+ else {
1246
+ std::ofstream output(dest + "/" + entry.Filename(), std::ios::binary);
1247
+ for (auto ch : entry.GetData()) {
1248
+ output << static_cast<unsigned char>(ch);
1249
+ }
1250
+ output.close();
1251
+ }
1252
+ }
1253
+
1254
+ /**
1255
+ * @brief Extract all entries in the given directory to the destination path.
1256
+ * @param dir The name of the directory to extract.
1257
+ * @param dest The path to extract the entry to.
1258
+ * @todo To be implemented
1259
+ */
1260
+ void ExtractDir(const std::string& dir, const std::string& dest)
1261
+ {
1262
+ if (!IsOpen()) throw ZipLogicError("Cannot call ZipArchive::ExtractDir(const std::string&, const std::string&) on empty ZipArchive object!");
1263
+ throw ZipLogicError("ZipArchive::ExtractDir(const std::string&, const std::string&) is not yet implemented!"); // TODO: To be implemented
1264
+ (void) dir; (void) dest; // 2024-08-18: suppress -Wunused-parameter
1265
+ }
1266
+
1267
+ /**
1268
+ * @brief Extract all archive contents to the destination path.
1269
+ * @param dest The path to extract the entry to.
1270
+ * @todo To be implemented
1271
+ */
1272
+ void ExtractAll(const std::string& dest)
1273
+ {
1274
+ if (!IsOpen()) throw ZipLogicError("Cannot call ZipArchive::ExtractAll(const std::string&) on empty ZipArchive object!");
1275
+ throw ZipLogicError("ZipArchive::ExtractAll(std::string const &) is not yet implemented!"); // TODO: To be implemented
1276
+ (void) dest; // 2024-08-18: suppress -Wunused-parameter
1277
+ }
1278
+
1279
+ /**
1280
+ * @brief Add a new entry to the archive.
1281
+ * @param name The name of the entry to add.
1282
+ * @param data The ZipEntryData to add. This is an alias of a std::vector<std::byte>, holding the binary data of the entry.
1283
+ * @return The ZipEntry object that has been added to the archive.
1284
+ * @note If an entry with given name already exists, it will be overwritten.
1285
+ */
1286
+ ZipEntry AddEntry(const std::string& name, const ZipEntryData& data)
1287
+ {
1288
+ return AddEntryImpl(name, data);
1289
+ }
1290
+
1291
+ /**
1292
+ * @brief Add a new entry to the archive.
1293
+ * @param name The name of the entry to add.
1294
+ * @param data The std::string data to add.
1295
+ * @return The ZipEntry object that has been added to the archive.
1296
+ * @note If an entry with given name already exists, it will be overwritten.
1297
+ */
1298
+ ZipEntry AddEntry(const std::string& name, const std::string& data)
1299
+ {
1300
+ ZipEntryData stringData;
1301
+ stringData.reserve(data.size());
1302
+ for (const auto& ch : data) {
1303
+ stringData.emplace_back(ch);
1304
+ }
1305
+
1306
+ return AddEntryImpl(name, stringData);
1307
+ }
1308
+
1309
+ /**
1310
+ * @brief Add a new entry to the archive, using another entry as input. This is mostly used for cloning existing entries
1311
+ * in the same archive, or for copying entries from one archive to another.
1312
+ * @param name The name of the entry to add.
1313
+ * @param entry The ZipEntry to add.
1314
+ * @return The ZipEntry object that has been added to the archive.
1315
+ * @note If an entry with given name already exists, it will be overwritten.
1316
+ */
1317
+ ZipEntry AddEntry(const std::string& name, const ZipEntry& entry)
1318
+ {
1319
+ return AddEntryImpl(name, entry.GetData());
1320
+ }
1321
+
1322
+ private:
1323
+ /**
1324
+ * @brief Add a new entry to the archive.
1325
+ * @param name The name of the entry to add.
1326
+ * @param data The ZipEntryData to add. This is an alias of a std::vector<std::byte>, holding the binary data of the entry.
1327
+ * @return The ZipEntry object that has been added to the archive.
1328
+ * @note If an entry with given name already exists, it will be overwritten.
1329
+ */
1330
+ ZipEntry AddEntryImpl(const std::string& name, const ZipEntryData& data)
1331
+ {
1332
+ if (!IsOpen()) throw ZipLogicError("Cannot call AddEntry on empty ZipArchive object!");
1333
+
1334
+ // ===== Ensure that all folders and subfolders have an entry in the archive
1335
+ auto folders = GetEntryNames(true, false);
1336
+ auto pos = 0;
1337
+ while (name.find('/', pos) != std::string::npos) {
1338
+ pos = name.find('/', pos) + 1;
1339
+ auto folder = name.substr(0, pos);
1340
+
1341
+ // ===== If folder isn't registered in the archive, add it.
1342
+ if (std::find(folders.begin(), folders.end(), folder) == folders.end()) {
1343
+ m_ZipEntries.emplace_back(Impl::ZipEntry(folder, ""));
1344
+ folders.emplace_back(folder);
1345
+ }
1346
+ }
1347
+
1348
+ // ===== Check if an entry with the given name already exists in the archive.
1349
+ auto result = std::find_if(m_ZipEntries.begin(), m_ZipEntries.end(), [&](const Impl::ZipEntry& entry) {
1350
+ return name == entry.GetName();
1351
+ });
1352
+
1353
+ // ===== If the entry exists, replace the existing data with the new data, and return the ZipEntry object.
1354
+ if (result != m_ZipEntries.end()) {
1355
+ result->SetData(data);
1356
+ return ZipEntry(&*result);
1357
+ }
1358
+
1359
+ // ===== Finally, add a new entry with the given name and data, and return the object.
1360
+ return ZipEntry(&m_ZipEntries.emplace_back(Impl::ZipEntry(name, data)));
1361
+ }
1362
+
1363
+ private:
1364
+ void *m_zipData; // the raw data of the unmodified source archive, TODO: (re-)set on ZipArchive::Open
1365
+ size_t m_zipSize; // the size in bytes of the unmodified source archive stored at m_zipData
1366
+ mz_zip_archive m_Archive = mz_zip_archive(); /**< The struct used by miniz, to handle archive files. */
1367
+ std::string m_ArchivePath = ""; /**< The path of the archive file. */
1368
+ bool m_IsOpen = false; /**< A flag indicating if the file is currently open for reading and writing. */
1369
+
1370
+ std::vector<Impl::ZipEntry> m_ZipEntries = std::vector<Impl::ZipEntry>(); /**< Data structure for all entries in the archive. */
1371
+ };
1372
+ } // namespace Zippy
1373
+
1374
+
1375
+ #endif // ZIPPY2_LIBRARY_H