zsv 1.3.0

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 (240) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +36 -0
  3. data/LICENSE +21 -0
  4. data/README.md +311 -0
  5. data/ext/zsv/common.h +34 -0
  6. data/ext/zsv/extconf.rb +137 -0
  7. data/ext/zsv/options.c +126 -0
  8. data/ext/zsv/options.h +31 -0
  9. data/ext/zsv/options_internal.h +8 -0
  10. data/ext/zsv/parser.c +300 -0
  11. data/ext/zsv/parser.h +62 -0
  12. data/ext/zsv/row.c +122 -0
  13. data/ext/zsv/row.h +39 -0
  14. data/ext/zsv/vendor/zsv-1.3.0/app/2db.c +756 -0
  15. data/ext/zsv/vendor/zsv-1.3.0/app/2json.c +381 -0
  16. data/ext/zsv/vendor/zsv-1.3.0/app/2tsv.c +228 -0
  17. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/help.c +123 -0
  18. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/license.c +39 -0
  19. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/register.c +104 -0
  20. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/thirdparty.c +41 -0
  21. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/unregister.c +1 -0
  22. data/ext/zsv/vendor/zsv-1.3.0/app/builtin/version.c +14 -0
  23. data/ext/zsv/vendor/zsv-1.3.0/app/check/simdutf_wrapper.h +19 -0
  24. data/ext/zsv/vendor/zsv-1.3.0/app/check/utf8.c +116 -0
  25. data/ext/zsv/vendor/zsv-1.3.0/app/check.c +194 -0
  26. data/ext/zsv/vendor/zsv-1.3.0/app/cli.c +796 -0
  27. data/ext/zsv/vendor/zsv-1.3.0/app/cli_const.h +41 -0
  28. data/ext/zsv/vendor/zsv-1.3.0/app/cli_export.h +16 -0
  29. data/ext/zsv/vendor/zsv-1.3.0/app/cli_ini.c +280 -0
  30. data/ext/zsv/vendor/zsv-1.3.0/app/cli_internal.h +36 -0
  31. data/ext/zsv/vendor/zsv-1.3.0/app/compare.c +913 -0
  32. data/ext/zsv/vendor/zsv-1.3.0/app/compare.h +23 -0
  33. data/ext/zsv/vendor/zsv-1.3.0/app/compare_added_column.c +20 -0
  34. data/ext/zsv/vendor/zsv-1.3.0/app/compare_internal.h +140 -0
  35. data/ext/zsv/vendor/zsv-1.3.0/app/compare_sort.c +91 -0
  36. data/ext/zsv/vendor/zsv-1.3.0/app/compare_unique_colname.c +81 -0
  37. data/ext/zsv/vendor/zsv-1.3.0/app/count-pull.c +82 -0
  38. data/ext/zsv/vendor/zsv-1.3.0/app/count.c +404 -0
  39. data/ext/zsv/vendor/zsv-1.3.0/app/desc.c +569 -0
  40. data/ext/zsv/vendor/zsv-1.3.0/app/echo.c +365 -0
  41. data/ext/zsv/vendor/zsv-1.3.0/app/ext_example/my_extension.c +366 -0
  42. data/ext/zsv/vendor/zsv-1.3.0/app/ext_example/mysheet_extension.c +341 -0
  43. data/ext/zsv/vendor/zsv-1.3.0/app/ext_template/YOUR_EXTENSION_zsvext.c +263 -0
  44. data/ext/zsv/vendor/zsv-1.3.0/app/external/inih/ini.c +298 -0
  45. data/ext/zsv/vendor/zsv-1.3.0/app/external/inih/ini.h +157 -0
  46. data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/json_numeric.c +177 -0
  47. data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/jsonwriter.c +444 -0
  48. data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/jsonwriter.h +145 -0
  49. data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/utils.c +110 -0
  50. data/ext/zsv/vendor/zsv-1.3.0/app/external/memfile-1.0/include/memfile.h +15 -0
  51. data/ext/zsv/vendor/zsv-1.3.0/app/external/memfile-1.0/src/memfile.c +64 -0
  52. data/ext/zsv/vendor/zsv-1.3.0/app/external/sglib/sglib.h +1955 -0
  53. data/ext/zsv/vendor/zsv-1.3.0/app/external/simdutf/simdutf.h +6802 -0
  54. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3.c +230517 -0
  55. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3.h +12174 -0
  56. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_and_csv_vtab.c +2 -0
  57. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-mem.c +142 -0
  58. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-mem.h +49 -0
  59. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-zsv.c +485 -0
  60. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab.c +1015 -0
  61. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3ext.h +663 -0
  62. data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/vtab_helper.c +85 -0
  63. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_common.h +75 -0
  64. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_gen.h +167 -0
  65. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_parse.h +228 -0
  66. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_tree.h +186 -0
  67. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_version.h +23 -0
  68. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_common.h +76 -0
  69. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_gen.h +167 -0
  70. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_parse.h +238 -0
  71. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_tree.h +186 -0
  72. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl.c +184 -0
  73. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_alloc.c +52 -0
  74. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_alloc.h +34 -0
  75. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_buf.c +103 -0
  76. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_buf.h +57 -0
  77. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_bytestack.h +69 -0
  78. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_encode.c +220 -0
  79. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_encode.h +34 -0
  80. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_gen.c +362 -0
  81. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_lex.c +764 -0
  82. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_lex.h +117 -0
  83. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_parser.c +508 -0
  84. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_parser.h +78 -0
  85. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_tree.c +505 -0
  86. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_version.c +7 -0
  87. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper/json_value.h +59 -0
  88. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper/yajl_helper.h +208 -0
  89. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper.c +795 -0
  90. data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper_internal.h +28 -0
  91. data/ext/zsv/vendor/zsv-1.3.0/app/flatten.c +851 -0
  92. data/ext/zsv/vendor/zsv-1.3.0/app/jq.c +106 -0
  93. data/ext/zsv/vendor/zsv-1.3.0/app/jq.h +6 -0
  94. data/ext/zsv/vendor/zsv-1.3.0/app/mv.c +113 -0
  95. data/ext/zsv/vendor/zsv-1.3.0/app/noop.c +90 -0
  96. data/ext/zsv/vendor/zsv-1.3.0/app/overwrite.c +295 -0
  97. data/ext/zsv/vendor/zsv-1.3.0/app/paste.c +175 -0
  98. data/ext/zsv/vendor/zsv-1.3.0/app/pretty.c +693 -0
  99. data/ext/zsv/vendor/zsv-1.3.0/app/prop.c +980 -0
  100. data/ext/zsv/vendor/zsv-1.3.0/app/rm.c +131 -0
  101. data/ext/zsv/vendor/zsv-1.3.0/app/select/fixed.c +130 -0
  102. data/ext/zsv/vendor/zsv-1.3.0/app/select/internal.h +118 -0
  103. data/ext/zsv/vendor/zsv-1.3.0/app/select/parallel.c +45 -0
  104. data/ext/zsv/vendor/zsv-1.3.0/app/select/parallel.h +41 -0
  105. data/ext/zsv/vendor/zsv-1.3.0/app/select/processing.c +107 -0
  106. data/ext/zsv/vendor/zsv-1.3.0/app/select/rand.c +20 -0
  107. data/ext/zsv/vendor/zsv-1.3.0/app/select/regex.c +61 -0
  108. data/ext/zsv/vendor/zsv-1.3.0/app/select/search.c +14 -0
  109. data/ext/zsv/vendor/zsv-1.3.0/app/select/selection.c +192 -0
  110. data/ext/zsv/vendor/zsv-1.3.0/app/select/usage.c +72 -0
  111. data/ext/zsv/vendor/zsv-1.3.0/app/select-pull.c +812 -0
  112. data/ext/zsv/vendor/zsv-1.3.0/app/select.c +753 -0
  113. data/ext/zsv/vendor/zsv-1.3.0/app/serialize.c +372 -0
  114. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/curses.h +15 -0
  115. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/cursor.c +119 -0
  116. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/errors.c +45 -0
  117. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/file.c +63 -0
  118. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/file.h +12 -0
  119. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/filter.c +166 -0
  120. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/handlers.c +214 -0
  121. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/handlers_internal.h +128 -0
  122. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/help.c +43 -0
  123. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/index.c +81 -0
  124. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/index.h +25 -0
  125. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/key-bindings.c +325 -0
  126. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/key-bindings.h +73 -0
  127. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/lexer.c +203 -0
  128. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/newline_handler.c +7 -0
  129. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/pivot.c +318 -0
  130. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/procedure.c +134 -0
  131. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/procedure.h +119 -0
  132. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/read-data.c +322 -0
  133. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/screen_buffer.c +203 -0
  134. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/screen_buffer.h +36 -0
  135. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sheet-sql.c +167 -0
  136. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sheet_internal.h +36 -0
  137. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sqlfilter.c +153 -0
  138. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/terminfo.c +32 -0
  139. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/transformation.c +312 -0
  140. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/transformation.h +29 -0
  141. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/ui_buffer.c +266 -0
  142. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/usage.c +9 -0
  143. data/ext/zsv/vendor/zsv-1.3.0/app/sheet/utf8-width.c +60 -0
  144. data/ext/zsv/vendor/zsv-1.3.0/app/sheet.c +1007 -0
  145. data/ext/zsv/vendor/zsv-1.3.0/app/sql.c +453 -0
  146. data/ext/zsv/vendor/zsv-1.3.0/app/sql_internal.c +101 -0
  147. data/ext/zsv/vendor/zsv-1.3.0/app/sql_internal.h +49 -0
  148. data/ext/zsv/vendor/zsv-1.3.0/app/stack.c +393 -0
  149. data/ext/zsv/vendor/zsv-1.3.0/app/utils/arg.c +322 -0
  150. data/ext/zsv/vendor/zsv-1.3.0/app/utils/cache.c +228 -0
  151. data/ext/zsv/vendor/zsv-1.3.0/app/utils/cat.c +91 -0
  152. data/ext/zsv/vendor/zsv-1.3.0/app/utils/chunk.c +240 -0
  153. data/ext/zsv/vendor/zsv-1.3.0/app/utils/chunk.h +63 -0
  154. data/ext/zsv/vendor/zsv-1.3.0/app/utils/clock.c +57 -0
  155. data/ext/zsv/vendor/zsv-1.3.0/app/utils/db.c +148 -0
  156. data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs-no-jq.c +2 -0
  157. data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs.c +427 -0
  158. data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs_from_json.c +253 -0
  159. data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs_to_json.c +121 -0
  160. data/ext/zsv/vendor/zsv-1.3.0/app/utils/dl.c +20 -0
  161. data/ext/zsv/vendor/zsv-1.3.0/app/utils/emcc/fs_api.c +159 -0
  162. data/ext/zsv/vendor/zsv-1.3.0/app/utils/err.c +24 -0
  163. data/ext/zsv/vendor/zsv-1.3.0/app/utils/file-mem.c +180 -0
  164. data/ext/zsv/vendor/zsv-1.3.0/app/utils/file.c +256 -0
  165. data/ext/zsv/vendor/zsv-1.3.0/app/utils/index.c +197 -0
  166. data/ext/zsv/vendor/zsv-1.3.0/app/utils/index.h +49 -0
  167. data/ext/zsv/vendor/zsv-1.3.0/app/utils/jq.c +400 -0
  168. data/ext/zsv/vendor/zsv-1.3.0/app/utils/json.c +120 -0
  169. data/ext/zsv/vendor/zsv-1.3.0/app/utils/mem.c +18 -0
  170. data/ext/zsv/vendor/zsv-1.3.0/app/utils/memmem.c +132 -0
  171. data/ext/zsv/vendor/zsv-1.3.0/app/utils/os.c +178 -0
  172. data/ext/zsv/vendor/zsv-1.3.0/app/utils/overwrite.c +258 -0
  173. data/ext/zsv/vendor/zsv-1.3.0/app/utils/overwrite_writer.c +246 -0
  174. data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8-test.c +123 -0
  175. data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8.c +153 -0
  176. data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8.h +54 -0
  177. data/ext/zsv/vendor/zsv-1.3.0/app/utils/prop.c +267 -0
  178. data/ext/zsv/vendor/zsv-1.3.0/app/utils/signal.c +53 -0
  179. data/ext/zsv/vendor/zsv-1.3.0/app/utils/string.c +357 -0
  180. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/dir_exists_longpath.c +83 -0
  181. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/dl.c +33 -0
  182. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/fopen_longpath.c +184 -0
  183. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/foreach_dirent_longpath.c +292 -0
  184. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/io.c +259 -0
  185. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/io.h +13 -0
  186. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/mkdir_longpath.c +255 -0
  187. data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/remove_longpath.c +96 -0
  188. data/ext/zsv/vendor/zsv-1.3.0/app/utils/writer.c +361 -0
  189. data/ext/zsv/vendor/zsv-1.3.0/app/zsv_command.h +40 -0
  190. data/ext/zsv/vendor/zsv-1.3.0/app/zsv_command_standalone.c +16 -0
  191. data/ext/zsv/vendor/zsv-1.3.0/app/zsv_main.h +44 -0
  192. data/ext/zsv/vendor/zsv-1.3.0/examples/js/zsv_parser_api_dummy.c +3 -0
  193. data/ext/zsv/vendor/zsv-1.3.0/examples/lib/parse_by_chunk.c +100 -0
  194. data/ext/zsv/vendor/zsv-1.3.0/examples/lib/print_my_column.c +143 -0
  195. data/ext/zsv/vendor/zsv-1.3.0/examples/lib/pull.c +89 -0
  196. data/ext/zsv/vendor/zsv-1.3.0/examples/lib/simple.c +123 -0
  197. data/ext/zsv/vendor/zsv-1.3.0/fuzz/fuzz.c +16 -0
  198. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/api.h +336 -0
  199. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/common.h +361 -0
  200. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/implementation.h +62 -0
  201. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/implementation_private.h +113 -0
  202. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/sheet.h +73 -0
  203. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext.h +329 -0
  204. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/arg.h +90 -0
  205. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/cache.h +49 -0
  206. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/clock.h +36 -0
  207. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/compiler.h +58 -0
  208. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/db.h +19 -0
  209. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/dirs.h +147 -0
  210. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/dl.h +22 -0
  211. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/emcc/fs_api.h +28 -0
  212. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/err.h +22 -0
  213. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/file-mem.h +17 -0
  214. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/file.h +99 -0
  215. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/jq.h +65 -0
  216. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/json.h +19 -0
  217. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/mem.h +19 -0
  218. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/memmem.h +13 -0
  219. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/os.h +54 -0
  220. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/overwrite.h +71 -0
  221. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/overwrite_writer.h +53 -0
  222. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/prop.h +107 -0
  223. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/signal.h +18 -0
  224. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/sql.h +11 -0
  225. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/string.h +148 -0
  226. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/utf8.h +41 -0
  227. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/win/dl.h +25 -0
  228. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/writer.h +101 -0
  229. data/ext/zsv/vendor/zsv-1.3.0/include/zsv/zsv_export.h +33 -0
  230. data/ext/zsv/vendor/zsv-1.3.0/include/zsv.h +20 -0
  231. data/ext/zsv/vendor/zsv-1.3.0/src/vector_delim.c +60 -0
  232. data/ext/zsv/vendor/zsv-1.3.0/src/zsv.c +484 -0
  233. data/ext/zsv/vendor/zsv-1.3.0/src/zsv_internal.c +731 -0
  234. data/ext/zsv/vendor/zsv-1.3.0/src/zsv_scan_delim.c +285 -0
  235. data/ext/zsv/vendor/zsv-1.3.0/src/zsv_scan_fixed.c +88 -0
  236. data/ext/zsv/vendor/zsv-1.3.0/src/zsv_strencode.c +51 -0
  237. data/ext/zsv/zsv_ext.c +343 -0
  238. data/lib/zsv/version.rb +5 -0
  239. data/lib/zsv.rb +81 -0
  240. metadata +340 -0
@@ -0,0 +1,292 @@
1
+ #include <windows.h>
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+ #include <sys/stat.h> // For struct stat definition (might need MinGW or similar)
6
+ #include <time.h> // For time_t
7
+ #include "io.h"
8
+
9
+ char *zsv_ensureLongPathPrefix(const char *original_path, unsigned char always_prefix);
10
+
11
+ // Helpers
12
+ static WCHAR *utf8_to_wide(const char *utf8_str);
13
+ static char *wide_to_utf8(const WCHAR *wide_str);
14
+ static void filetime_to_time_t(const FILETIME *ft, time_t *t);
15
+ static void populate_stat_from_find_data(struct stat *s, const WIN32_FIND_DATAW *findData);
16
+
17
+ // Windows-specific implementation
18
+ static int zsv_foreach_dirent_aux(const char *dir_path_utf8, size_t depth, size_t max_depth,
19
+ zsv_foreach_dirent_handler handler, void *ctx, char verbose) {
20
+ int err = 0;
21
+ HANDLE hFind = INVALID_HANDLE_VALUE;
22
+ WCHAR *search_path_wide = NULL;
23
+ WCHAR *dir_path_wide = NULL;
24
+ char *prefixed_dir_path_utf8 = NULL; // For potential long path
25
+
26
+ if (!dir_path_utf8)
27
+ return 1; // Invalid input path
28
+
29
+ if (max_depth > 0 && depth >= max_depth)
30
+ return 0; // Max depth reached
31
+
32
+ // 1. Handle potential long path prefix (result must be freed)
33
+ prefixed_dir_path_utf8 = zsv_ensureLongPathPrefix(dir_path_utf8, 1);
34
+ const char *prefixed_dir_to_use = prefixed_dir_path_utf8 ? prefixed_dir_path_utf8 : dir_path_utf8;
35
+
36
+ // 2. Convert the (potentially prefixed) UTF-8 path to WCHAR (UTF-16) for Windows API
37
+ dir_path_wide = utf8_to_wide(prefixed_dir_to_use);
38
+ if (!dir_path_wide) {
39
+ fprintf(stderr, "Failed to convert path to wide char: %s\n", prefixed_dir_to_use);
40
+ free(prefixed_dir_path_utf8);
41
+ return 1;
42
+ }
43
+
44
+ // 3. Create the search pattern (e.g., "C:\\path\\*")
45
+ size_t dir_len = wcslen(dir_path_wide);
46
+ // Need space for path, backslash (optional), wildcard, and null terminator
47
+ search_path_wide = (WCHAR *)malloc((dir_len + 3) * sizeof(WCHAR));
48
+ if (!search_path_wide) {
49
+ fprintf(stderr, "Out of memory allocating search path!\n");
50
+ err = 1;
51
+ goto cleanup;
52
+ }
53
+ wcscpy_s(search_path_wide, dir_len + 3, dir_path_wide);
54
+ // Add trailing backslash if needed (robust check)
55
+ if (dir_len > 0 && search_path_wide[dir_len - 1] != L'\\' && search_path_wide[dir_len - 1] != L'/') {
56
+ search_path_wide[dir_len] = L'\\';
57
+ search_path_wide[dir_len + 1] = L'\0'; // Ensure null termination before appending wildcard
58
+ }
59
+ wcscat_s(search_path_wide, dir_len + 3, L"*"); // Append wildcard
60
+
61
+ // 4. Start finding files
62
+ WIN32_FIND_DATAW findData;
63
+ hFind = FindFirstFileW(search_path_wide, &findData);
64
+
65
+ if (hFind == INVALID_HANDLE_VALUE) {
66
+ // Check if the error is simply "directory not found" or similar benign cases
67
+ DWORD dwError = GetLastError();
68
+ if (dwError != ERROR_FILE_NOT_FOUND && dwError != ERROR_PATH_NOT_FOUND && dwError != ERROR_NO_MORE_FILES) {
69
+ // Report more serious errors
70
+ fprintf(stderr, "FindFirstFileW failed for %ls (Error %lu)\n", search_path_wide, dwError);
71
+ err = 1;
72
+ // Maybe add more specific error handling here if needed
73
+ }
74
+ // Otherwise, it's okay (e.g., empty dir or dir doesn't exist), just return
75
+ goto cleanup;
76
+ }
77
+
78
+ // 5. Iterate through directory entries
79
+ do {
80
+ // Skip "." and ".." entries
81
+ if (wcscmp(findData.cFileName, L".") == 0 || wcscmp(findData.cFileName, L"..") == 0) {
82
+ continue;
83
+ }
84
+
85
+ char *entry_name_utf8 = NULL;
86
+ char *full_path_utf8 = NULL;
87
+
88
+ // 6. Convert the found entry name (WCHAR) back to UTF-8
89
+ entry_name_utf8 = wide_to_utf8(findData.cFileName);
90
+ if (!entry_name_utf8) {
91
+ fprintf(stderr, "Failed to convert entry name to UTF-8\n");
92
+ err = 1;
93
+ // Attempt to continue with the next file? Or break? Let's try continuing.
94
+ continue;
95
+ }
96
+
97
+ // 7. Construct the full path IN UTF-8 using the ORIGINAL dir_path_utf8
98
+ // Need space for original path, separator, entry name, null terminator
99
+ size_t original_path_len = strlen(dir_path_utf8);
100
+ size_t entry_name_len = strlen(entry_name_utf8);
101
+ size_t full_path_len_needed = original_path_len + 1 + entry_name_len + 1;
102
+ full_path_utf8 = (char *)malloc(full_path_len_needed);
103
+
104
+ if (!full_path_utf8) {
105
+ fprintf(stderr, "Out of memory allocating full path!\n");
106
+ free(entry_name_utf8);
107
+ err = 1;
108
+ // If we can't construct the path, we probably should stop.
109
+ break;
110
+ }
111
+
112
+ strcpy_s(full_path_utf8, full_path_len_needed, dir_path_utf8);
113
+ // Append backslash if needed
114
+ if (original_path_len > 0 && dir_path_utf8[original_path_len - 1] != '\\' &&
115
+ dir_path_utf8[original_path_len - 1] != '/') {
116
+ strcat_s(full_path_utf8, full_path_len_needed, "\\");
117
+ }
118
+ strcat_s(full_path_utf8, full_path_len_needed, entry_name_utf8);
119
+
120
+ // 8. Prepare the handler structure
121
+ struct zsv_foreach_dirent_handle h = {0};
122
+ h.verbose = verbose;
123
+ h.parent = dir_path_utf8; // Use original non-prefixed path for handler
124
+ h.entry = entry_name_utf8;
125
+ h.parent_and_entry = full_path_utf8;
126
+ h.ctx = ctx;
127
+ h.is_dir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
128
+
129
+ // Populate the stat structure (best effort)
130
+ populate_stat_from_find_data(&h.stat, &findData);
131
+
132
+ // 9. Call the handler
133
+ if (handler) {
134
+ // Handler might set h.no_recurse
135
+ handler(&h, depth + 1);
136
+ }
137
+
138
+ // 10. Recurse if it's a directory and recursion is not disabled
139
+ if (h.is_dir && !h.no_recurse) {
140
+ int recurse_err = zsv_foreach_dirent_aux(full_path_utf8, depth + 1, max_depth, handler, ctx, verbose);
141
+ if (recurse_err) {
142
+ err = recurse_err; // Propagate error
143
+ // Potentially break here if recursive errors should stop processing
144
+ }
145
+ }
146
+
147
+ // 11. Cleanup per-entry allocations
148
+ free(entry_name_utf8);
149
+ free(full_path_utf8);
150
+
151
+ if (err && !h.is_dir) { // Option: break if a non-recursive error occurred
152
+ // break;
153
+ }
154
+
155
+ } while (FindNextFileW(hFind, &findData) != 0);
156
+
157
+ // Check for errors after the loop (FindNextFileW returns 0 on end or error)
158
+ DWORD dwError = GetLastError();
159
+ if (dwError != ERROR_NO_MORE_FILES) {
160
+ fprintf(stderr, "FindNextFileW failed (Error %lu)\n", dwError);
161
+ err = 1;
162
+ }
163
+
164
+ cleanup:
165
+ // 12. Final Cleanup
166
+ if (hFind != INVALID_HANDLE_VALUE) {
167
+ FindClose(hFind);
168
+ }
169
+ free(search_path_wide);
170
+ free(dir_path_wide);
171
+ free(prefixed_dir_path_utf8); // Free the path possibly returned by zsv_ensureLongPathPrefix
172
+
173
+ return err;
174
+ }
175
+
176
+ // --- Helper Function Implementations ---
177
+
178
+ // Converts a UTF-8 string to a newly allocated WCHAR (UTF-16) string.
179
+ // Returns NULL on failure. Caller must free the returned pointer.
180
+ static WCHAR *utf8_to_wide(const char *utf8_str) {
181
+ if (!utf8_str)
182
+ return NULL;
183
+ int chars_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);
184
+ if (chars_needed <= 0) {
185
+ // fprintf(stderr, "MultiByteToWideChar (pre-flight) failed with error %lu\n", GetLastError());
186
+ return NULL;
187
+ }
188
+ WCHAR *wide_str = (WCHAR *)malloc(chars_needed * sizeof(WCHAR));
189
+ if (!wide_str) {
190
+ // fprintf(stderr, "Failed to allocate memory for wide string\n");
191
+ return NULL;
192
+ }
193
+ int chars_converted = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, wide_str, chars_needed);
194
+ if (chars_converted <= 0) {
195
+ // fprintf(stderr, "MultiByteToWideChar failed with error %lu\n", GetLastError());
196
+ free(wide_str);
197
+ return NULL;
198
+ }
199
+ return wide_str;
200
+ }
201
+
202
+ // Converts a WCHAR (UTF-16) string to a newly allocated UTF-8 string.
203
+ // Returns NULL on failure. Caller must free the returned pointer.
204
+ static char *wide_to_utf8(const WCHAR *wide_str) {
205
+ if (!wide_str)
206
+ return NULL;
207
+ int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, NULL, 0, NULL, NULL);
208
+ if (bytes_needed <= 0) {
209
+ // fprintf(stderr, "WideCharToMultiByte (pre-flight) failed with error %lu\n", GetLastError());
210
+ return NULL;
211
+ }
212
+ char *utf8_str = (char *)malloc(bytes_needed); // Bytes includes null terminator
213
+ if (!utf8_str) {
214
+ // fprintf(stderr, "Failed to allocate memory for utf8 string\n");
215
+ return NULL;
216
+ }
217
+ int bytes_converted = WideCharToMultiByte(CP_UTF8, 0, wide_str, -1, utf8_str, bytes_needed, NULL, NULL);
218
+ if (bytes_converted <= 0) {
219
+ // fprintf(stderr, "WideCharToMultiByte failed with error %lu\n", GetLastError());
220
+ free(utf8_str);
221
+ return NULL;
222
+ }
223
+ return utf8_str;
224
+ }
225
+
226
+ // Converts Windows FILETIME to POSIX time_t.
227
+ // See https://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux
228
+ static void filetime_to_time_t(const FILETIME *ft, time_t *t) {
229
+ ULARGE_INTEGER uli;
230
+ uli.LowPart = ft->dwLowDateTime;
231
+ uli.HighPart = ft->dwHighDateTime;
232
+ // Windows FILETIME is 100-nanosecond intervals since January 1, 1601.
233
+ // time_t is seconds since January 1, 1970.
234
+ // Difference is 11644473600 seconds.
235
+ const ULONGLONG epoch_diff = 116444736000000000ULL;
236
+ if (uli.QuadPart < epoch_diff) {
237
+ *t = 0; // Time is before the Unix epoch
238
+ } else {
239
+ *t = (time_t)((uli.QuadPart - epoch_diff) / 10000000ULL);
240
+ }
241
+ }
242
+
243
+ // Populates a struct stat (partially) from WIN32_FIND_DATAW.
244
+ // This is a best-effort mapping as not all fields have direct equivalents.
245
+ static void populate_stat_from_find_data(struct stat *s, const WIN32_FIND_DATAW *findData) {
246
+ memset(s, 0, sizeof(struct stat)); // Initialize all fields to 0
247
+
248
+ // File Type and Mode (Basic)
249
+ if (findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
250
+ s->st_mode |= S_IFDIR | 0755; // Directory with typical permissions
251
+ } else {
252
+ s->st_mode |= S_IFREG | 0644; // Regular file with typical permissions
253
+ }
254
+ if (findData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
255
+ s->st_mode &= ~0222; // Remove write permissions
256
+ }
257
+
258
+ // File Size
259
+ ULARGE_INTEGER fileSize;
260
+ fileSize.LowPart = findData->nFileSizeLow;
261
+ fileSize.HighPart = findData->nFileSizeHigh;
262
+ // Be careful about potential truncation if using a 32-bit build and large files
263
+ // Using LONGLONG cast for intermediate calculation might help, but st_size might still be 32-bit
264
+ #ifdef _WIN64
265
+ s->st_size = fileSize.QuadPart;
266
+ #else
267
+ // Potential truncation for files > 4GB on 32-bit builds where st_size is 32-bit
268
+ if (fileSize.QuadPart > 0xFFFFFFFF) {
269
+ s->st_size = 0xFFFFFFFF; // Max value for 32-bit unsigned? Or -1? Check definition.
270
+ // Consider logging a warning here
271
+ } else {
272
+ s->st_size = fileSize.LowPart; // Assuming st_size is 32-bit here
273
+ }
274
+ // A better approach for 32-bit might involve using _stati64 and struct __stat64
275
+ // but that changes the struct zsv_foreach_dirent_handle definition slightly.
276
+ // Sticking to standard 'struct stat' as requested for now.
277
+ #endif
278
+
279
+ // Timestamps
280
+ filetime_to_time_t(&findData->ftCreationTime, &s->st_ctime);
281
+ filetime_to_time_t(&findData->ftLastAccessTime, &s->st_atime);
282
+ filetime_to_time_t(&findData->ftLastWriteTime, &s->st_mtime);
283
+
284
+ // Other fields (st_dev, st_ino, st_nlink, st_uid, st_gid)
285
+ // These don't have direct, easily accessible equivalents in WIN32_FIND_DATAW.
286
+ // Setting them to 0 or default values.
287
+ s->st_nlink = 1; // Typically 1 for files on Windows, can be >1 for dirs but hard to get
288
+ // s->st_dev = 0; // No easy equivalent
289
+ // s->st_ino = 0; // No easy equivalent (File ID requires GetFileInformationByHandle)
290
+ // s->st_uid = 0; // No concept of Unix UID/GID
291
+ // s->st_gid = 0;
292
+ }
@@ -0,0 +1,259 @@
1
+ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
2
+ #include <windows.h> // For CreateDirectoryW, MultiByteToWideChar, GetLastError etc.
3
+ #include <stdio.h> // For printf, perror
4
+ #include <stdlib.h> // For malloc, free, exit
5
+ #include <string.h> // For strlen, strcpy, strncpy
6
+ #include <wchar.h> // For wide character types and functions like wcslen, wcscpy
7
+
8
+ static char *slashes_to_backslashes_if_needed(const char *path, DWORD *rc) {
9
+ char *tmp = NULL;
10
+ if (strchr(path, '/')) {
11
+ tmp = strdup(path);
12
+ if (!tmp) {
13
+ perror(path);
14
+ *rc = ERROR_OUTOFMEMORY;
15
+ return NULL;
16
+ }
17
+ for (size_t i = 0, j = strlen(path); i < j; i++)
18
+ if (tmp[i] == '/')
19
+ tmp[i] = '\\';
20
+ }
21
+ *rc = 0;
22
+ return tmp;
23
+ }
24
+
25
+ // Convert slashes to backslashes and then
26
+ // make sure we have "\\\\?\\" or "\\\\?\\UNC\\" prefix if/as necessary or requested
27
+ // Note: We assume the input 'original_path' is UTF-8 encoded.
28
+ // The returned path, if not NULL, will also be UTF-8 encoded.
29
+ char *zsv_ensureLongPathPrefix(const char *original_path, unsigned char always_prefix) {
30
+ if (original_path == NULL || original_path[0] == '\0') {
31
+ return NULL; // Handle NULL or empty input
32
+ }
33
+
34
+ wchar_t *wide_original_path = NULL;
35
+ wchar_t *wide_full_path = NULL;
36
+ wchar_t *prefixed_wide_path = NULL;
37
+ char *result_path_utf8 = NULL;
38
+ DWORD last_error = 0;
39
+
40
+ // --- 1. Convert input UTF-8 path to wide char (UTF-16) ---
41
+ int original_len_bytes = strlen(original_path);
42
+ int wide_len_needed = MultiByteToWideChar(CP_UTF8, 0, original_path, original_len_bytes, NULL, 0);
43
+ if (wide_len_needed == 0) {
44
+ last_error = GetLastError();
45
+ fprintf(stderr, "zsv_ensureLongPathPrefix: MultiByteToWideChar (size check) failed: %lu\n", last_error);
46
+ goto cleanup;
47
+ }
48
+
49
+ wide_original_path = (wchar_t *)malloc((wide_len_needed + 1) * sizeof(wchar_t));
50
+ if (!wide_original_path) {
51
+ perror("zsv_ensureLongPathPrefix: malloc for wide_original_path failed");
52
+ goto cleanup;
53
+ }
54
+ int converted_chars =
55
+ MultiByteToWideChar(CP_UTF8, 0, original_path, original_len_bytes, wide_original_path, wide_len_needed);
56
+ if (converted_chars == 0) {
57
+ last_error = GetLastError();
58
+ fprintf(stderr, "zsv_ensureLongPathPrefix: MultiByteToWideChar failed: %lu\n", last_error);
59
+ goto cleanup;
60
+ }
61
+ wide_original_path[wide_len_needed] = L'\0'; // Null-terminate
62
+
63
+ // --- 2. Get the full absolute path using GetFullPathNameW ---
64
+ DWORD full_path_len_needed_wchars = GetFullPathNameW(wide_original_path, 0, NULL, NULL);
65
+ if (full_path_len_needed_wchars == 0) {
66
+ last_error = GetLastError();
67
+ fprintf(stderr, "zsv_ensureLongPathPrefix: GetFullPathNameW (size check) failed for '%ls': %lu\n",
68
+ wide_original_path, last_error);
69
+ goto cleanup;
70
+ }
71
+
72
+ wide_full_path = (wchar_t *)malloc(full_path_len_needed_wchars * sizeof(wchar_t));
73
+ if (!wide_full_path) {
74
+ perror("zsv_ensureLongPathPrefix: malloc for wide_full_path failed");
75
+ goto cleanup;
76
+ }
77
+
78
+ DWORD full_path_len_copied_wchars =
79
+ GetFullPathNameW(wide_original_path, full_path_len_needed_wchars, wide_full_path, NULL);
80
+ if (full_path_len_copied_wchars == 0 || full_path_len_copied_wchars >= full_path_len_needed_wchars) {
81
+ last_error = GetLastError();
82
+ // Error or buffer too small (shouldn't happen with correct size check)
83
+ fprintf(stderr, "zsv_ensureLongPathPrefix: GetFullPathNameW failed or buffer issue for '%ls': %lu\n",
84
+ wide_original_path, last_error);
85
+ goto cleanup;
86
+ }
87
+ // wide_full_path now contains the absolute path, null-terminated, with backslashes.
88
+
89
+ // Original wide path no longer needed
90
+ free(wide_original_path);
91
+ wide_original_path = NULL;
92
+
93
+ // --- 3. Check length of the ABSOLUTE path and always_prefix flag ---
94
+ size_t absolute_path_wlen = wcslen(wide_full_path); // Use wcslen for clarity
95
+
96
+ if (absolute_path_wlen < MAX_PATH && !always_prefix) {
97
+ // Path is short, and we don't always prefix.
98
+ // Return NULL as per the original function's contract for non-prefixing cases.
99
+ goto cleanup; // result_path_utf8 is already NULL
100
+ }
101
+
102
+ // --- Path is long (>= MAX_PATH) or always_prefix is set ---
103
+
104
+ // --- Define prefixes (wide char) ---
105
+ const wchar_t *prefix_std = L"\\\\?\\";
106
+ const wchar_t *prefix_unc = L"\\\\?\\UNC\\";
107
+ const size_t prefix_std_len = 4; // wcslen(prefix_std)
108
+ const size_t prefix_unc_len = 8; // wcslen(prefix_unc)
109
+
110
+ // --- 4. Check if the absolute path is already correctly prefixed ---
111
+ if (wcsncmp(wide_full_path, prefix_std, prefix_std_len) == 0 ||
112
+ wcsncmp(wide_full_path, prefix_unc, prefix_unc_len) == 0) {
113
+ // Already has a standard or UNC prefix.
114
+ // We still need to return it (converted back to UTF-8) because
115
+ // prefixing *was* deemed necessary (long path or always_prefix=true).
116
+ prefixed_wide_path = wide_full_path; // Use the existing full path
117
+ wide_full_path = NULL; // Prevent double free
118
+ } else {
119
+ // --- Needs prefixing ---
120
+
121
+ // Determine if it's a UNC path (starts with \\ but not \\?)
122
+ // GetFullPathNameW should return paths like \\server\share...
123
+ BOOL is_unc = (absolute_path_wlen >= 2 && wide_full_path[0] == L'\\' && wide_full_path[1] == L'\\');
124
+
125
+ size_t new_wide_size_wchars = 0;
126
+
127
+ if (is_unc) {
128
+ // UNC Path: Needs \\?\UNC\ prefix
129
+ // Path part starts after the leading '\\'
130
+ const wchar_t *path_part = wide_full_path + 2;
131
+ size_t path_part_len = wcslen(path_part);
132
+ // New size = prefix len + path part len + 1 (null)
133
+ new_wide_size_wchars = prefix_unc_len + path_part_len + 1;
134
+ prefixed_wide_path = (wchar_t *)malloc(new_wide_size_wchars * sizeof(wchar_t));
135
+ if (!prefixed_wide_path) {
136
+ perror("zsv_ensureLongPathPrefix (UNC): malloc failed");
137
+ goto cleanup;
138
+ }
139
+ // Construct: wcscpy + wcscat is safe here due to exact size calculation
140
+ wcscpy(prefixed_wide_path, prefix_unc);
141
+ wcscat(prefixed_wide_path, path_part); // Append server\share...
142
+ } else {
143
+ // Standard Path: Needs \\?\ prefix
144
+ // New size = prefix len + original absolute path len + 1 (null)
145
+ new_wide_size_wchars = prefix_std_len + absolute_path_wlen + 1;
146
+ prefixed_wide_path = (wchar_t *)malloc(new_wide_size_wchars * sizeof(wchar_t));
147
+ if (!prefixed_wide_path) {
148
+ perror("zsv_ensureLongPathPrefix (STD): malloc failed");
149
+ goto cleanup;
150
+ }
151
+ // Construct: wcscpy + wcscat is safe here
152
+ wcscpy(prefixed_wide_path, prefix_std);
153
+ wcscat(prefixed_wide_path, wide_full_path);
154
+ }
155
+ }
156
+
157
+ // --- 5. Convert the (potentially) prefixed wide path back to UTF-8 ---
158
+ if (prefixed_wide_path) { // Should always be true if we reach here
159
+ // Pass -1 for null termination handling
160
+ int utf8_len_needed = WideCharToMultiByte(CP_UTF8, 0, prefixed_wide_path, -1, NULL, 0, NULL, NULL);
161
+ if (utf8_len_needed == 0) {
162
+ last_error = GetLastError();
163
+ fprintf(stderr, "zsv_ensureLongPathPrefix: WideCharToMultiByte (size check) failed: %lu\n", last_error);
164
+ goto cleanup;
165
+ }
166
+
167
+ result_path_utf8 = (char *)malloc(utf8_len_needed); // Includes null terminator space
168
+ if (!result_path_utf8) {
169
+ perror("zsv_ensureLongPathPrefix: malloc for final result_path_utf8 failed");
170
+ goto cleanup;
171
+ }
172
+
173
+ int bytes_written =
174
+ WideCharToMultiByte(CP_UTF8, 0, prefixed_wide_path, -1, result_path_utf8, utf8_len_needed, NULL, NULL);
175
+ if (bytes_written == 0) {
176
+ last_error = GetLastError();
177
+ fprintf(stderr, "zsv_ensureLongPathPrefix: WideCharToMultiByte failed: %lu\n", last_error);
178
+ // Free the allocated result buffer before cleaning up others
179
+ free(result_path_utf8);
180
+ result_path_utf8 = NULL;
181
+ goto cleanup;
182
+ }
183
+ }
184
+
185
+ cleanup:
186
+ // Free intermediate allocations
187
+ free(wide_original_path); // Safe to free NULL
188
+ free(wide_full_path); // Safe to free NULL (will be NULL if assigned to prefixed_wide_path or on early exit)
189
+ free(prefixed_wide_path); // Safe to free NULL (will be NULL if no prefixing happened or on early exit)
190
+
191
+ // Return the final UTF-8 path (which is NULL if no prefixing was needed or if an error occurred)
192
+ return result_path_utf8;
193
+ }
194
+
195
+ DWORD zsv_pathToPrefixedWidePath(const char *path_utf8, wchar_t **result) {
196
+ // --- Input Validation ---
197
+ if (!path_utf8 || !result) {
198
+ return ERROR_INVALID_PARAMETER;
199
+ }
200
+ *result = NULL; // Initialize result pointer
201
+
202
+ // --- Intermediate Variables ---
203
+ char *prefixed_utf8 = NULL; // Result from zsv_ensureLongPathPrefix
204
+ const char *utf8_to_convert = NULL; // Points to the UTF-8 string to be converted
205
+ wchar_t *final_wide_path = NULL; // Final result path pointer (was intermediate)
206
+ DWORD error_code = 0; // Holds error codes
207
+
208
+ // --- 2. Add prefix
209
+ prefixed_utf8 = zsv_ensureLongPathPrefix(path_utf8, 1);
210
+ // If prefixed_utf8 is not NULL, it's newly allocated (with correct prefix).
211
+ // If NULL, the path was short or already correctly prefixed.
212
+
213
+ // Determine the final UTF-8 string to convert to wide char
214
+ utf8_to_convert = prefixed_utf8 ? prefixed_utf8 : path_utf8;
215
+
216
+ // --- 3. Convert Selected UTF-8 Path to Wide Char (UTF-16) ---
217
+ // Calculate required buffer size (including null terminator)
218
+ int wideCharLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_to_convert, -1, NULL, 0);
219
+ if (wideCharLen == 0) {
220
+ error_code = GetLastError();
221
+ fprintf(stderr, "Error calculating wide char length for '%s': %lu\n", utf8_to_convert, error_code);
222
+ goto cleanup;
223
+ }
224
+
225
+ // Allocate buffer
226
+ final_wide_path = (wchar_t *)calloc(wideCharLen + 1, sizeof(wchar_t));
227
+ if (!final_wide_path) {
228
+ perror("Error allocating memory for final wide path");
229
+ error_code = ERROR_OUTOFMEMORY;
230
+ goto cleanup;
231
+ }
232
+
233
+ // Perform conversion
234
+ if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_to_convert, -1, final_wide_path, wideCharLen) == 0) {
235
+ error_code = GetLastError();
236
+ fprintf(stderr, "Error converting path '%s' to wide char: %lu\n", utf8_to_convert, error_code);
237
+ // final_wide_path will be freed in cleanup
238
+ goto cleanup;
239
+ }
240
+ // final_wide_path now holds the result, correctly prefixed if needed.
241
+
242
+ // --- 4. Set Result and Return Success ---
243
+ // The intermediate wide path is now the final path.
244
+ *result = final_wide_path;
245
+ final_wide_path = NULL; // Prevent freeing in cleanup as ownership is transferred
246
+ error_code = 0; // Success
247
+
248
+ cleanup:
249
+ // Free all intermediate allocations. free(NULL) is safe.
250
+ free(prefixed_utf8);
251
+ free(final_wide_path); // Free only if not transferred to *result
252
+
253
+ // If an error occurred before setting *result, ensure it's NULL
254
+ if (error_code != 0) {
255
+ *result = NULL;
256
+ }
257
+
258
+ return error_code;
259
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef ZSV_WIN_UTILS_IO_H
2
+ #define ZSV_WIN_UTILS_IO_H
3
+
4
+ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
5
+ #include <windows.h> // For CreateDirectoryW, MultiByteToWideChar, GetLastError etc.
6
+ #include <stdio.h> // For printf, perror
7
+ #include <stdlib.h> // For malloc, free, exit
8
+ #include <string.h> // For strlen, strcpy, strncpy
9
+ #include <wchar.h> // For wide character types and functions like wcslen, wcscpy
10
+
11
+ DWORD zsv_pathToPrefixedWidePath(const char *path_utf8, wchar_t **result);
12
+
13
+ #endif