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,980 @@
1
+ /* Copyright (C) 2022 Guarnerix Inc dba Liquidaty - All Rights Reserved
2
+ * Unauthorized copying of this file, via any medium is strictly prohibited
3
+ * Proprietary and confidential
4
+ * Written by Matt Wong <matt@guarnerix.com>
5
+ */
6
+
7
+ /*
8
+ for a given file, edits properties saved in ZSV_CACHE_DIR/<filepath>/props.json
9
+ */
10
+
11
+ #include <stdio.h>
12
+ #include <string.h>
13
+ #include <stdlib.h>
14
+ #include <stdint.h>
15
+ #include <inttypes.h>
16
+ #include <limits.h>
17
+ #include <errno.h>
18
+ #include <unistd.h> // unlink, access
19
+
20
+ #define ZSV_COMMAND_NO_OPTIONS
21
+ #define ZSV_COMMAND prop
22
+ #include "zsv_command.h"
23
+
24
+ #include <zsv/utils/os.h>
25
+ #include <zsv/utils/file.h>
26
+ #include <zsv/utils/json.h>
27
+ #include <zsv/utils/jq.h>
28
+ #include <zsv/utils/dirs.h>
29
+ #include <zsv/utils/cache.h>
30
+ #include <zsv/utils/string.h>
31
+
32
+ const char *zsv_property_usage_msg[] = {
33
+ APPNAME ": view or save parsing options associated with a file",
34
+ " saved options will be applied by default when processing that file",
35
+ "",
36
+ "Usage: " APPNAME " <filepath> [options]",
37
+ " where filepath is the path to the input CSV file, or",
38
+ " when using --auto, input CSV file or - for stdin",
39
+ " when using --clean, directory to clean from (use '.' for current directory)",
40
+ "",
41
+ "Options:",
42
+ " -d,--header-row-span <value> : set/unset/auto-detect header depth (see below)",
43
+ " -R,--skip-head <value> : set/unset/auto-detect initial rows to skip (see below)",
44
+ " --list-files : list all property sets associated with the given file", // list cached files
45
+ " --clear : delete all properties of the specified file",
46
+ // TO DO: --clear-file relative-path
47
+ " --clean : delete all files / dirs in the property cache of the given directory",
48
+ " that do not have a corresponding file in that directory",
49
+ " --dry : dry run, outputs files/dirs to remove. only for use with --clean",
50
+ " --auto : guess the best property values. This is equivalent to: -d auto -R auto",
51
+ " when using this option, a dash (-) can be used instead to read from stdin",
52
+ " --save : save the detected result (only applicable with --auto)",
53
+ " --copy <dest_filepath> : copy properties to another file", // TO DO: validate JSON
54
+ " --export <output_path> : export all properties to a single JSON file (- for stdout)", // TO DO: validate JSON
55
+ " --import <input_path> : import properties from a single JSON file (- for stdin)", // TO DO: validate JSON
56
+ " -f,--overwrite : overwrite any previously-saved properties",
57
+ "",
58
+ "For --header-row-span or --skip-head options, <value> can be:",
59
+ "- a positive integer, to save the value to the associated file's properties",
60
+ "- a zero (0), or \"none\" or \"-\" to remove the value from the associated file's properties",
61
+ "- \"auto\" to auto-detect the property value (to save, use --save/--overwrite)",
62
+ "",
63
+ "If no options are provided, currently saved properties are output in JSON format.",
64
+ "",
65
+ "Properties are saved in " ZSV_CACHE_DIR "/<filename>/" ZSV_CACHE_PROPERTIES_NAME,
66
+ "which is deleted when the file is removed using `rm`.",
67
+ "",
68
+ "The --auto feature is provided for convenience only, and is not intended to be smart enough",
69
+ "to make guesses that can be blindly assumed to be correct. You have been warned!",
70
+ NULL,
71
+ };
72
+
73
+ static int zsv_property_usage(FILE *target) {
74
+ for (size_t j = 0; zsv_property_usage_msg[j]; j++)
75
+ fprintf(target, "%s\n", zsv_property_usage_msg[j]);
76
+ return target == stdout ? 0 : 1;
77
+ }
78
+
79
+ static int show_all_properties(const unsigned char *filepath) {
80
+ int err = 0;
81
+ // TO DO: show all files, not just prop file
82
+
83
+ if (!zsv_file_readable((const char *)filepath, &err, NULL)) {
84
+ perror((const char *)filepath);
85
+ return err;
86
+ }
87
+ err = zsv_cache_print(filepath, zsv_cache_type_property, (const unsigned char *)"{}");
88
+ if (err == ENOENT)
89
+ err = 0;
90
+ return err;
91
+ }
92
+
93
+ #define ZSV_PROP_TYPE_CHECK_NUM 1
94
+ #define ZSV_PROP_TYPE_CHECK_DATE 2
95
+ #define ZSV_PROP_TYPE_CHECK_BOOL 4
96
+ #define ZSV_PROP_TYPE_CHECK_NULL 8
97
+
98
+ /**
99
+ * Very basic test to check if a string looks like a number:
100
+ * - ignore leading whitespace and currency
101
+ * - ignore trailing whitespace
102
+ * - ignore leading dash or plus
103
+ * - len < 1 or > 30 => not a number
104
+ * - scan characters one by one:
105
+ * if the char isn't a digit, comma or period, it's not a number
106
+ * digits are ignored
107
+ * commas are counted (we ignore the requirement for them to be spaced out e.g. every 3 digits)
108
+ * periods are counted
109
+ * if at any point we have more than 1 comma AND more than 1 digit, it's not a number
110
+ * @param s input string
111
+ * @param len length of input
112
+ * @param flags reserved for future use
113
+ * @return 1 if it looks like a number, else 0
114
+ */
115
+ static char looks_like_num(const unsigned char *s, size_t len, unsigned flags) {
116
+ (void)(flags);
117
+ // trim
118
+ s = zsv_strtrim(s, &len);
119
+
120
+ // strip +/- sign, if any
121
+ size_t sign = zsv_strnext_is_sign(s, len);
122
+ if (sign) {
123
+ s += sign;
124
+ len -= sign;
125
+ s = zsv_strtrim_left(s, &len);
126
+ }
127
+
128
+ // strip currency, if any
129
+ size_t currency = zsv_strnext_is_currency(s, len);
130
+ if (currency) {
131
+ s += currency;
132
+ len -= currency;
133
+ s = zsv_strtrim_left(s, &len);
134
+ }
135
+
136
+ // strip +/- sign, if we didn't find one earlier
137
+ if (!sign && (sign = zsv_strnext_is_sign(s, len))) {
138
+ s += sign;
139
+ len -= sign;
140
+ s = zsv_strtrim_left(s, &len);
141
+ }
142
+
143
+ if (len < 1 || len > 30)
144
+ return 0;
145
+
146
+ unsigned digits = 0;
147
+ unsigned period = 0;
148
+ for (size_t i = 0; i < len; i++) {
149
+ unsigned char c = s[i];
150
+ if (c >= '0' && c <= '9') // to do: allow utf8 digits, commas, periods?
151
+ digits++;
152
+ else if (c == ',' && i > 0 && period == 0) { // comma can't be first char, or follow a period
153
+ // do nothing. to do: check that the last comma was either 3 or 4 numbers away?
154
+ } else if (c == '.' && period == 0) // only 1 period allowed (to do: relax this as it isn't true in all localities)
155
+ period++;
156
+ else
157
+ return 0;
158
+ }
159
+ return digits > 0 && period < 2;
160
+ }
161
+
162
+ /**
163
+ * Super crude "test" to check if a string looks like a date or timestamp:
164
+ * we are just going to disqualify if len < 5 or len > 30
165
+ * or any chars are not digits, slash, dash, colon, space
166
+ * or in any of the following which is made up of chars from the English months, plus am/pm
167
+ * abcdefghijlmnoprstuvy
168
+ * @param s input string
169
+ * @param len length of input
170
+ * @param flags reserved for future use
171
+ * @return 1 if it looks like a date, else 0
172
+ */
173
+ static char looks_like_date(const unsigned char *s, size_t len, unsigned flags) {
174
+ (void)(flags);
175
+ // trim
176
+ s = zsv_strtrim(s, &len);
177
+ if (len <= 5 || len > 30)
178
+ return 0;
179
+ #define LOOKS_LIKE_DATE_CHARS "0123456789-/:, abcdefghijlmnoprstuvy"
180
+ for (size_t i = 0; i < len; i++)
181
+ if (!memchr(LOOKS_LIKE_DATE_CHARS, s[i], strlen(LOOKS_LIKE_DATE_CHARS)))
182
+ return 0;
183
+ return 1;
184
+ }
185
+
186
+ /**
187
+ * Very basic test to check if a string looks like a bool:
188
+ * - ignore leading and trailing whitespace
189
+ * - look for true, false, yes, no, T, F, 1, 0, Y, N
190
+ * - to do: add localization options?
191
+ * @param s input string
192
+ * @param len length of input
193
+ * @param flags reserved for future use
194
+ * @return 1 if it looks like a bool, else 0
195
+ */
196
+ static char looks_like_bool(const unsigned char *s, size_t len, unsigned flags) {
197
+ (void)(flags);
198
+ // trim
199
+ s = zsv_strtrim(s, &len);
200
+
201
+ if (!len)
202
+ return 0;
203
+
204
+ if (len == 1)
205
+ return strchr("TtFf10YyNn", *s) ? 1 : 0;
206
+
207
+ if (len <= 5) {
208
+ char *lower = (char *)zsv_strtolowercase(s, &len);
209
+ if (lower) {
210
+ char result = 0;
211
+ switch (len) {
212
+ case 2:
213
+ result = !strcmp(lower, "no");
214
+ break;
215
+ case 3:
216
+ result = !strcmp(lower, "yes");
217
+ break;
218
+ case 4:
219
+ result = !strcmp(lower, "true");
220
+ break;
221
+ case 5:
222
+ result = !strcmp(lower, "false");
223
+ break;
224
+ }
225
+ free(lower);
226
+ return result;
227
+ }
228
+ }
229
+ return 0;
230
+ }
231
+
232
+ static unsigned int type_detect(const unsigned char *s, size_t slen) {
233
+ unsigned int result = 0;
234
+ if (slen == 0) {
235
+ result += ZSV_PROP_TYPE_CHECK_NULL;
236
+ return result;
237
+ }
238
+ if (looks_like_num(s, slen, 0))
239
+ result += ZSV_PROP_TYPE_CHECK_NUM;
240
+ if (looks_like_date(s, slen, 0))
241
+ result += ZSV_PROP_TYPE_CHECK_DATE;
242
+ if (looks_like_bool(s, slen, 0))
243
+ result += ZSV_PROP_TYPE_CHECK_BOOL;
244
+ return result;
245
+ }
246
+
247
+ #define ZSV_PROP_DETECT_ROW_MAX 10
248
+ struct detect_properties_data {
249
+ zsv_parser parser;
250
+ struct {
251
+ size_t date;
252
+ size_t num;
253
+ size_t is_bool;
254
+ size_t null;
255
+ size_t cols_used;
256
+ } rows[ZSV_PROP_DETECT_ROW_MAX];
257
+ size_t rows_processed;
258
+ };
259
+
260
+ static void detect_properties_row(void *ctx) {
261
+ struct detect_properties_data *data = ctx;
262
+ size_t cols_used = data->rows[data->rows_processed].cols_used = zsv_cell_count(data->parser);
263
+ for (size_t i = 0; i < cols_used; i++) {
264
+ struct zsv_cell c = zsv_get_cell(data->parser, i);
265
+ unsigned int result = type_detect(c.str, c.len);
266
+ if (result & ZSV_PROP_TYPE_CHECK_NULL)
267
+ data->rows[data->rows_processed].null++;
268
+ else {
269
+ if (result & ZSV_PROP_TYPE_CHECK_NUM)
270
+ data->rows[data->rows_processed].num++;
271
+ if (result & ZSV_PROP_TYPE_CHECK_DATE)
272
+ data->rows[data->rows_processed].date++;
273
+ if (result & ZSV_PROP_TYPE_CHECK_BOOL)
274
+ data->rows[data->rows_processed].is_bool++;
275
+ }
276
+ }
277
+ if (++data->rows_processed >= ZSV_PROP_DETECT_ROW_MAX)
278
+ zsv_abort(data->parser);
279
+ }
280
+
281
+ static struct zsv_file_properties guess_properties(struct detect_properties_data *data) {
282
+ struct zsv_file_properties result = {0};
283
+ size_t i;
284
+ for (i = 0; i < data->rows_processed; i++) {
285
+ if (data->rows[i].cols_used <= data->rows[i].null)
286
+ result.skip++;
287
+ else if (i + 1 < data->rows_processed && data->rows[i + 1].cols_used <= data->rows[i + 1].null)
288
+ result.skip++;
289
+ else
290
+ break;
291
+ }
292
+
293
+ result.header_span = 1;
294
+ for (i = i + 1; i < data->rows_processed; i++, result.header_span++) {
295
+ if (data->rows[i].cols_used == data->rows[i].null)
296
+ continue;
297
+ if (data->rows[i].cols_used > data->rows[i - 1].cols_used)
298
+ continue;
299
+ unsigned int data_like = data->rows[i].date + data->rows[i].num + data->rows[i].is_bool;
300
+ if (data_like > 5 || (data_like > 0 && data_like >= data->rows[i].cols_used / 2))
301
+ break;
302
+ if (data_like + data->rows[i].null == data->rows[i].cols_used)
303
+ break;
304
+ if (data_like <= 5 && data_like > 0 && data->rows[i].cols_used <= 5)
305
+ break;
306
+ }
307
+ if (result.header_span == data->rows_processed)
308
+ result.header_span = 1;
309
+ return result;
310
+ }
311
+
312
+ static int detect_properties(const unsigned char *filepath, struct zsv_file_properties *result,
313
+ int64_t detect_headerspan, /* reserved for future use */
314
+ int64_t detect_rows_to_skip, /* reserved for future use */
315
+ const struct zsv_opts *optsp) {
316
+ (void)(detect_headerspan);
317
+ (void)(detect_rows_to_skip);
318
+ struct detect_properties_data data = {0};
319
+ struct zsv_opts opts = *optsp;
320
+ opts.row_handler = detect_properties_row;
321
+ opts.ctx = &data;
322
+ if (!strcmp((void *)filepath, "-"))
323
+ opts.stream = stdin;
324
+ else {
325
+ opts.stream = fopen((const char *)filepath, "rb");
326
+ if (!opts.stream) {
327
+ perror((const char *)filepath);
328
+ return 1;
329
+ }
330
+ }
331
+
332
+ opts.keep_empty_header_rows = 1;
333
+ data.parser = zsv_new(&opts);
334
+ while (!zsv_signal_interrupted && zsv_parse_more(data.parser) == zsv_status_ok)
335
+ ;
336
+ zsv_finish(data.parser);
337
+ zsv_delete(data.parser);
338
+
339
+ fclose(opts.stream);
340
+ *result = guess_properties(&data);
341
+ result->header_span += opts.header_span;
342
+ result->skip += opts.rows_to_ignore;
343
+
344
+ return 0;
345
+ }
346
+
347
+ #define ZSV_PROP_ARG_NONE -1
348
+ #define ZSV_PROP_ARG_AUTO -2
349
+ #define ZSV_PROP_ARG_REMOVE -3
350
+
351
+ static int prop_arg_value(int i, int argc, const char *argv[], int64_t *value) {
352
+ if (i >= argc) {
353
+ fprintf(stderr, "Option %s requires a value\n", argv[i - 1]);
354
+ return 1;
355
+ }
356
+
357
+ const char *arg = argv[i];
358
+ if (!strcmp(arg, "auto"))
359
+ *value = ZSV_PROP_ARG_AUTO;
360
+ else if (!strcmp(arg, "none") || !strcmp(arg, "0") || !strcmp(arg, "-"))
361
+ *value = ZSV_PROP_ARG_REMOVE;
362
+ else {
363
+ char *end = NULL;
364
+ intmax_t j = strtoimax(arg, &end, 0);
365
+ if (arg && *arg && end && *end == '\0') {
366
+ if (j == 0)
367
+ *value = ZSV_PROP_ARG_REMOVE;
368
+ else if (j > 0 && j <= UINT_MAX)
369
+ *value = j;
370
+ return 0;
371
+ }
372
+ fprintf(stderr, "Invalid property value '%s'.\n", arg);
373
+ fprintf(stderr, "Please use an integer greater than or equal to zero,"
374
+ "'auto', 'none', or '-'\n");
375
+ return 1;
376
+ }
377
+ return 0;
378
+ }
379
+
380
+ static int merge_properties(int64_t values[2], struct zsv_file_properties *fp, char keep[2], int *remove_any) {
381
+ int err = 0;
382
+ // for each of d and R
383
+ // if the corresponding argument > 0, save it
384
+ // if the corresponding argument = ZSV_PROP_ARG_NONE, use fp value if fp is nonzero, else don't include it in the
385
+ // saved file if the corresponding argument = ZSV_PROP_ARG_REMOVE else error / unexpected value
386
+ char fp_specified[2] = {fp->header_span_specified, fp->skip_specified};
387
+ unsigned fp_val[2] = {fp->header_span, fp->skip};
388
+ for (int i = 0; i < 2; i++) {
389
+ switch (values[i]) {
390
+ case ZSV_PROP_ARG_NONE:
391
+ if (fp_specified[i]) {
392
+ keep[i] = 1;
393
+ values[i] = fp_val[i];
394
+ }
395
+ break;
396
+ case ZSV_PROP_ARG_REMOVE:
397
+ *remove_any = 1;
398
+ values[i] = 0;
399
+ break;
400
+ default:
401
+ if (values[i] >= 0)
402
+ keep[i] = 1;
403
+ else {
404
+ fprintf(stderr, "save_properties: unexpected unhandled case!\n");
405
+ err = 1;
406
+ }
407
+ break;
408
+ }
409
+ }
410
+ return err;
411
+ }
412
+
413
+ // print_properties: return 1 if something was printed
414
+ static char print_properties_helper(FILE *f, int64_t values[2], char keep[2], const char *prop_id[2]) {
415
+ char started = 0;
416
+ for (int i = 0; i < 2; i++) {
417
+ if (keep[i]) {
418
+ if (!started) {
419
+ fprintf(f, "{\n");
420
+ started = 1;
421
+ } else
422
+ fprintf(f, ",\n");
423
+ fprintf(f, " \"%s\": %u", prop_id[i], (unsigned)values[i]);
424
+ }
425
+ }
426
+ if (started)
427
+ fprintf(f, "\n}\n");
428
+ return started;
429
+ }
430
+
431
+ // print_properties: return 1 if something was printed
432
+ // print to stdout
433
+ // in addition, print to f, if provided
434
+ static char print_properties(FILE *f, int64_t values[2], char keep[2], const char *prop_id[2]) {
435
+ char result = 0;
436
+ if (f)
437
+ result = print_properties_helper(f, values, keep, prop_id);
438
+ if (!print_properties_helper(stdout, values, keep, prop_id))
439
+ printf("{}\n");
440
+ return result;
441
+ }
442
+
443
+ static int merge_and_save_properties(const unsigned char *filepath, char save, char overwrite, int64_t d, int64_t R) {
444
+ int err = 0;
445
+ unsigned char *props_fn = zsv_cache_filepath(filepath, zsv_cache_type_property, 0, 0);
446
+ if (!props_fn)
447
+ err = 1;
448
+ else {
449
+ struct zsv_opts zsv_opts = {0};
450
+ struct zsv_prop_handler custom_prop_handler = {0};
451
+ struct zsv_file_properties fp = zsv_cache_load_props((const char *)filepath, &zsv_opts, &custom_prop_handler);
452
+ err = fp.stat;
453
+ if (!err) {
454
+ if (save && !overwrite) {
455
+ if ((fp.header_span_specified && d) || (fp.skip_specified && R)) {
456
+ fprintf(stderr, "Properties for this file already exist; use -f or --overwrite option to overwrite\n");
457
+ err = 1;
458
+ }
459
+ }
460
+ }
461
+
462
+ if (!err) {
463
+ unsigned char *props_fn_tmp = save ? zsv_cache_filepath(filepath, zsv_cache_type_property, 1, 1) : NULL;
464
+ if (save && !props_fn_tmp)
465
+ err = 1;
466
+ else {
467
+ // open a temp file, then write, then replace the orig file
468
+ FILE *f = props_fn_tmp ? fopen((char *)props_fn_tmp, "wb") : NULL;
469
+ if (props_fn_tmp && !f) {
470
+ perror((char *)props_fn_tmp);
471
+ err = 1;
472
+ } else {
473
+ int64_t final_values[2] = {d, R};
474
+ const char *prop_id[2] = {"header-row-span", "skip-head"};
475
+ char keep[2] = {'\0', '\0'};
476
+ int remove_any = 0;
477
+ err = merge_properties(final_values, &fp, keep, &remove_any);
478
+ char printed_something = 0;
479
+ if (!err)
480
+ printed_something = print_properties(f, final_values, keep, prop_id);
481
+ if (f)
482
+ fclose(f);
483
+
484
+ if (!err) {
485
+ if (save && f) {
486
+ if (printed_something) {
487
+ if (zsv_replace_file(props_fn_tmp, props_fn))
488
+ err = zsv_printerr(-1, "Unable to save %s", props_fn);
489
+ } else if (remove_any)
490
+ err = zsv_cache_remove(filepath, zsv_cache_type_property);
491
+ }
492
+ }
493
+ }
494
+ free(props_fn_tmp);
495
+ }
496
+ }
497
+ free(props_fn);
498
+ }
499
+ return err;
500
+ }
501
+
502
+ enum zsv_prop_mode {
503
+ zsv_prop_mode_default = 0,
504
+ zsv_prop_mode_list_files = 'l',
505
+ zsv_prop_mode_clean = 'K',
506
+ zsv_prop_mode_export = 'e',
507
+ zsv_prop_mode_import = 'i',
508
+ zsv_prop_mode_copy = 'c',
509
+ zsv_prop_mode_clear = 'r'
510
+ };
511
+
512
+ static enum zsv_prop_mode zsv_prop_get_mode(const char *opt) {
513
+ if (!strcmp(opt, "--clean"))
514
+ return zsv_prop_mode_clean;
515
+ if (!strcmp(opt, "--list-files"))
516
+ return zsv_prop_mode_list_files;
517
+ if (!strcmp(opt, "--copy"))
518
+ return zsv_prop_mode_copy;
519
+ if (!strcmp(opt, "--export"))
520
+ return zsv_prop_mode_export;
521
+ if (!strcmp(opt, "--import"))
522
+ return zsv_prop_mode_import;
523
+ if (!strcmp(opt, "--clear"))
524
+ return zsv_prop_mode_clear;
525
+ return zsv_prop_mode_default;
526
+ }
527
+
528
+ struct prop_opts {
529
+ int64_t d; // ZSV_PROP_ARG_AUTO, ZSV_PROP_ARG_REMOVE or > 0
530
+ int64_t R; // ZSV_PROP_ARG_AUTO, ZSV_PROP_ARG_REMOVE or > 0
531
+ unsigned char clear : 1;
532
+ unsigned char save : 1;
533
+ unsigned char overwrite : 1;
534
+ unsigned char _ : 3;
535
+ };
536
+
537
+ static int zsv_prop_execute_default(const unsigned char *filepath, struct zsv_opts *zsv_opts, struct prop_opts opts) {
538
+ int err = 0;
539
+ struct zsv_file_properties fp = {0};
540
+ if (opts.d >= 0 || opts.R >= 0 || opts.d == ZSV_PROP_ARG_REMOVE || opts.R == ZSV_PROP_ARG_REMOVE)
541
+ opts.overwrite = 1;
542
+ if (opts.d == ZSV_PROP_ARG_AUTO || opts.R == ZSV_PROP_ARG_AUTO) {
543
+ err = detect_properties(filepath, &fp, opts.d == ZSV_PROP_ARG_AUTO, opts.R == ZSV_PROP_ARG_AUTO, zsv_opts);
544
+ }
545
+
546
+ if (!err) {
547
+ if (opts.d == ZSV_PROP_ARG_AUTO)
548
+ opts.d = fp.header_span;
549
+
550
+ if (opts.R == ZSV_PROP_ARG_AUTO)
551
+ opts.R = fp.skip;
552
+ err = merge_and_save_properties(filepath, opts.save, opts.overwrite, opts.d, opts.R);
553
+ }
554
+ return err;
555
+ }
556
+
557
+ int zsv_is_prop_file(struct zsv_foreach_dirent_handle *h, size_t depth) {
558
+ return depth == 1 && !strcmp(h->entry, "props.json");
559
+ }
560
+
561
+ #ifdef ZSV_IS_PROP_FILE_HANDLER
562
+ int ZSV_IS_PROP_FILE_HANDLER(struct zsv_foreach_dirent_handle *, size_t);
563
+ #endif
564
+
565
+ struct zsv_dir_filter *zsv_prop_get_or_set_is_prop_file(int (*custom_is_prop_file)(struct zsv_foreach_dirent_handle *,
566
+ size_t),
567
+ int max_depth, char set) {
568
+ static struct zsv_dir_filter ctx = {
569
+ #ifndef ZSV_IS_PROP_FILE_HANDLER
570
+ .filter = zsv_is_prop_file,
571
+ #else
572
+ .filter = ZSV_IS_PROP_FILE_HANDLER,
573
+ #endif
574
+ #ifndef ZSV_IS_PROP_FILE_DEPTH
575
+ .max_depth = 1
576
+ #else
577
+ .max_depth = ZSV_IS_PROP_FILE_DEPTH
578
+ #endif
579
+ };
580
+
581
+ if (set) {
582
+ if (!(ctx.filter = custom_is_prop_file)) {
583
+ ctx.filter = zsv_is_prop_file;
584
+ max_depth = 1;
585
+ } else
586
+ ctx.max_depth = max_depth;
587
+ }
588
+ return &ctx;
589
+ }
590
+
591
+ static int zsv_prop_foreach_list(struct zsv_foreach_dirent_handle *h, size_t depth) {
592
+ if (!h->is_dir) {
593
+ struct zsv_dir_filter *ctx = (struct zsv_dir_filter *)h->ctx;
594
+ h->ctx = ctx->ctx;
595
+ if (ctx->filter(h, depth))
596
+ printf("%s\n", h->entry);
597
+ h->ctx = ctx;
598
+ }
599
+ return 0;
600
+ }
601
+
602
+ zsv_foreach_dirent_handler zsv_prop_get_or_set_is_prop_dir(int (*custom_is_prop_dir)(struct zsv_foreach_dirent_handle *,
603
+ size_t),
604
+ char set) {
605
+ static int (*func)(struct zsv_foreach_dirent_handle *, size_t) = NULL;
606
+ if (set)
607
+ func = custom_is_prop_dir;
608
+ return func;
609
+ }
610
+
611
+ static int zsv_prop_execute_list_files(const unsigned char *filepath, char verbose) {
612
+ int err = 0;
613
+ unsigned char *cache_path = zsv_cache_path(filepath, NULL, 0);
614
+ struct zsv_dir_filter ctx = *zsv_prop_get_or_set_is_prop_file(NULL, 0, 0);
615
+ if (cache_path) {
616
+ zsv_foreach_dirent((const char *)cache_path, ctx.max_depth, zsv_prop_foreach_list, &ctx, verbose);
617
+ free(cache_path);
618
+ }
619
+ return err;
620
+ }
621
+
622
+ struct zsv_prop_foreach_clean_ctx {
623
+ const char *dirpath;
624
+ unsigned char dry;
625
+ };
626
+
627
+ static int zsv_prop_foreach_clean(struct zsv_foreach_dirent_handle *h, size_t depth) {
628
+ int err = 0;
629
+ if (depth == 1) {
630
+ struct zsv_prop_foreach_clean_ctx *ctx = h->ctx;
631
+ if (h->is_dir) {
632
+ // h->entry is the name of the top-level file that this folder relates to
633
+ // make sure that the top-level file exists
634
+ h->no_recurse = 1;
635
+
636
+ char *cache_owner_path;
637
+ asprintf(&cache_owner_path, "%s%c%s", ctx->dirpath, FILESLASH, h->entry);
638
+ if (!cache_owner_path) {
639
+ fprintf(stderr, "Out of memory!\n");
640
+ return 1;
641
+ }
642
+ if (!zsv_file_exists(cache_owner_path)) {
643
+ if (ctx->dry)
644
+ printf("Orphaned: %s\n", h->parent_and_entry);
645
+ else
646
+ err = zsv_remove_dir_recursive((const unsigned char *)h->parent_and_entry);
647
+ }
648
+ free(cache_owner_path);
649
+ } else {
650
+ // there should be no files at depth 1, so just delete
651
+ if (ctx->dry)
652
+ printf("Unrecognized: %s\n", h->parent_and_entry);
653
+ else if (unlink(h->parent_and_entry)) {
654
+ perror(h->parent_and_entry);
655
+ err = 1;
656
+ }
657
+ }
658
+ }
659
+ return err;
660
+ }
661
+
662
+ enum zsv_prop_foreach_copy_mode {
663
+ zsv_prop_foreach_copy_mode_check = 1,
664
+ zsv_prop_foreach_copy_mode_copy
665
+ };
666
+
667
+ struct zsv_prop_foreach_copy_ctx {
668
+ struct zsv_dir_filter zsv_dir_filter;
669
+ const unsigned char *src_cache_dir;
670
+ const unsigned char *dest_cache_dir;
671
+ enum zsv_prop_foreach_copy_mode mode;
672
+ int err;
673
+ unsigned char output_started : 1;
674
+ unsigned char force : 1;
675
+ unsigned char dry : 1;
676
+ unsigned char _ : 5;
677
+ };
678
+
679
+ static int zsv_prop_foreach_copy(struct zsv_foreach_dirent_handle *h, size_t depth) {
680
+ if (!h->is_dir) {
681
+ struct zsv_prop_foreach_copy_ctx *ctx = h->ctx;
682
+ h->ctx = ctx->zsv_dir_filter.ctx;
683
+ if (ctx->zsv_dir_filter.filter(h, depth)) {
684
+ char *dest_prop_filepath;
685
+ asprintf(&dest_prop_filepath, "%s%s", ctx->dest_cache_dir,
686
+ h->parent_and_entry + strlen((const char *)ctx->src_cache_dir));
687
+ if (!dest_prop_filepath) {
688
+ ctx->err = errno = ENOMEM;
689
+ perror(NULL);
690
+ } else {
691
+ switch (ctx->mode) {
692
+ case zsv_prop_foreach_copy_mode_check: {
693
+ if (!zsv_file_readable(h->parent_and_entry, &ctx->err, NULL)) { // check if source is not readable
694
+ perror(h->parent_and_entry);
695
+ } else if (!ctx->force && access(dest_prop_filepath, F_OK) != -1) { // check if dest already exists
696
+ ctx->err = EEXIST;
697
+ if (!ctx->output_started) {
698
+ ctx->output_started = 1;
699
+ const char *msg = strerror(EEXIST);
700
+ fprintf(stderr, "%s:\n", msg ? msg : "File already exists");
701
+ }
702
+ fprintf(stderr, " %s\n", dest_prop_filepath);
703
+ } else if (ctx->dry)
704
+ printf("%s => %s\n", h->parent_and_entry, dest_prop_filepath);
705
+ } break;
706
+ case zsv_prop_foreach_copy_mode_copy:
707
+ if (!ctx->dry) {
708
+ char *dest_prop_filepath_tmp;
709
+ asprintf(&dest_prop_filepath_tmp, "%s.temp", dest_prop_filepath);
710
+ if (!dest_prop_filepath_tmp) {
711
+ ctx->err = errno = ENOMEM;
712
+ perror(NULL);
713
+ } else {
714
+ if (h->verbose)
715
+ fprintf(stderr, "Copying temp: %s => %s\n", h->parent_and_entry, dest_prop_filepath_tmp);
716
+ int err = zsv_copy_file(h->parent_and_entry, dest_prop_filepath_tmp);
717
+ if (err)
718
+ ctx->err = err;
719
+ else {
720
+ if (h->verbose)
721
+ fprintf(stderr, "Renaming: %s => %s\n", dest_prop_filepath_tmp, dest_prop_filepath);
722
+ if (zsv_replace_file(dest_prop_filepath_tmp, dest_prop_filepath)) {
723
+ fprintf(stderr, "Unable to rename %s -> %s: ", dest_prop_filepath_tmp, dest_prop_filepath);
724
+ zsv_perror(NULL);
725
+ ctx->err = errno;
726
+ }
727
+ }
728
+ free(dest_prop_filepath_tmp);
729
+ }
730
+ }
731
+ break;
732
+ }
733
+ free(dest_prop_filepath);
734
+ }
735
+ }
736
+ h->ctx = ctx;
737
+ }
738
+ return 0;
739
+ }
740
+
741
+ static int zsv_prop_execute_copy(const char *src, const char *dest, unsigned char force, unsigned char dry,
742
+ unsigned char verbose) {
743
+ int err = 0;
744
+ unsigned char *src_cache_dir = zsv_cache_path((const unsigned char *)src, NULL, 0);
745
+ unsigned char *dest_cache_dir = zsv_cache_path((const unsigned char *)dest, NULL, 0);
746
+
747
+ if (!(src_cache_dir && dest_cache_dir))
748
+ err = errno = ENOMEM, perror(NULL);
749
+ else {
750
+ // if !force, only proceed if:
751
+ // - src exists (file)
752
+ // - dest exists (file)
753
+ // - dest file property cache d.n. have conflicts
754
+ struct zsv_prop_foreach_copy_ctx ctx = {0};
755
+ ctx.zsv_dir_filter = *zsv_prop_get_or_set_is_prop_file(NULL, 0, 0);
756
+ ctx.dest_cache_dir = dest_cache_dir;
757
+ ctx.src_cache_dir = src_cache_dir;
758
+ ctx.force = force;
759
+ ctx.dry = dry;
760
+
761
+ if (!force) {
762
+ if (!zsv_file_exists(src))
763
+ err = errno = ENOENT, perror(src);
764
+ if (!zsv_file_exists(dest))
765
+ err = errno = ENOENT, perror(dest);
766
+ }
767
+
768
+ if (!err) {
769
+ // for each property file, check if dest has same-named property file
770
+ ctx.mode = zsv_prop_foreach_copy_mode_check;
771
+ zsv_foreach_dirent((const char *)src_cache_dir, ctx.zsv_dir_filter.max_depth, zsv_prop_foreach_copy, &ctx,
772
+ verbose);
773
+ }
774
+
775
+ if (!err && !(ctx.err && !force)) {
776
+ // copy the files
777
+ ctx.mode = zsv_prop_foreach_copy_mode_copy;
778
+ zsv_foreach_dirent((const char *)src_cache_dir, ctx.zsv_dir_filter.max_depth, zsv_prop_foreach_copy, &ctx,
779
+ verbose);
780
+ }
781
+ }
782
+ free(src_cache_dir);
783
+ free(dest_cache_dir);
784
+ return err;
785
+ }
786
+
787
+ static int zsv_prop_execute_clean(const char *dirpath, unsigned char dry, unsigned char verbose) {
788
+ // TO DO: if ZSV_CACHE_DIR-tmp exists, delete it (file or dir)
789
+ int err = 0;
790
+ size_t dirpath_len = strlen(dirpath);
791
+ while (dirpath_len && memchr("/\\", dirpath[dirpath_len - 1], 2) != NULL)
792
+ dirpath_len--;
793
+ if (!dirpath_len)
794
+ return 0;
795
+
796
+ // TO DO: if NO_STDIN, require --force, else prompt user
797
+
798
+ char *cache_parent;
799
+ if (!strcmp(dirpath, "."))
800
+ cache_parent = strdup(ZSV_CACHE_DIR);
801
+ else
802
+ asprintf(&cache_parent, "%.*s%c%s", (int)dirpath_len, dirpath, FILESLASH, ZSV_CACHE_DIR);
803
+ if (!cache_parent) {
804
+ fprintf(stderr, "Out of memory!\n");
805
+ return 1;
806
+ }
807
+
808
+ struct zsv_prop_foreach_clean_ctx ctx = {0};
809
+ ctx.dirpath = dirpath;
810
+ ctx.dry = dry;
811
+
812
+ zsv_foreach_dirent(cache_parent, 0, zsv_prop_foreach_clean, &ctx, verbose);
813
+ free(cache_parent);
814
+ return err;
815
+ }
816
+
817
+ static int zsv_prop_execute_export(const char *src, const char *dest, unsigned char verbose) {
818
+ int err = 0;
819
+ unsigned char *parent_dir = zsv_cache_path((const unsigned char *)src, NULL, 0);
820
+ if (!(parent_dir))
821
+ err = errno = ENOMEM, perror(NULL);
822
+ else {
823
+ struct zsv_dir_filter zsv_dir_filter = *zsv_prop_get_or_set_is_prop_file(NULL, 0, 0);
824
+ err = zsv_dir_to_json(parent_dir, (const unsigned char *)dest, &zsv_dir_filter, verbose);
825
+ }
826
+ free(parent_dir);
827
+ return err;
828
+ }
829
+
830
+ static int zsv_prop_execute_import(const char *dest, const char *src, unsigned char force, unsigned char dry,
831
+ unsigned char verbose) {
832
+ int err = 0;
833
+ unsigned char *target_dir = NULL;
834
+ FILE *fsrc = NULL;
835
+ if (!force && !zsv_file_exists(dest)) {
836
+ err = errno = ENOENT;
837
+ perror(dest);
838
+ } else if (!(target_dir = zsv_cache_path((const unsigned char *)dest, NULL, 0))) {
839
+ err = errno = ENOMEM;
840
+ perror(NULL);
841
+ } else if (!(fsrc = src ? fopen(src, "rb") : stdin)) {
842
+ err = errno;
843
+ perror(src);
844
+ } else {
845
+ int flags = (force ? ZSV_DIR_FLAG_FORCE : 0) | (dry ? ZSV_DIR_FLAG_DRY : 0);
846
+ err = zsv_dir_from_json(target_dir, fsrc, flags, verbose);
847
+ }
848
+ if (fsrc && fsrc != stdin)
849
+ fclose(fsrc);
850
+ free(target_dir);
851
+ return err;
852
+ }
853
+
854
+ int ZSV_MAIN_NO_OPTIONS_FUNC(ZSV_COMMAND)(int m_argc, const char *m_argv[]) {
855
+ int err = 0;
856
+ char verbose = 0;
857
+ if (m_argc < 2 || (m_argc > 1 && (!strcmp(m_argv[1], "-h") || !strcmp(m_argv[1], "--help"))))
858
+ err = zsv_property_usage(stdout);
859
+ else {
860
+ struct prop_opts opts = {0};
861
+ opts.d = ZSV_PROP_ARG_NONE;
862
+ opts.R = ZSV_PROP_ARG_NONE;
863
+
864
+ const unsigned char *filepath = (const unsigned char *)m_argv[1];
865
+ if (m_argc == 2)
866
+ return show_all_properties(filepath);
867
+
868
+ enum zsv_prop_mode mode = zsv_prop_mode_default;
869
+ unsigned char dry = 0;
870
+ const char *mode_arg = NULL; // e.g. "--export"
871
+ const char *mode_value = NULL; // e.g. "saved_export.json"
872
+ for (int i = 2; !err && i < m_argc; i++) {
873
+ const char *opt = m_argv[i];
874
+ if (!strcmp(opt, "-v") || !strcmp(opt, "--verbose"))
875
+ verbose = 1;
876
+ else if (!strcmp(opt, "-d") || !strcmp(opt, "--header-row-span"))
877
+ err = prop_arg_value(++i, m_argc, m_argv, &opts.d);
878
+ else if (!strcmp(opt, "-R") || !strcmp(opt, "--skip-head"))
879
+ err = prop_arg_value(++i, m_argc, m_argv, &opts.R);
880
+ else if (zsv_prop_get_mode(opt)) {
881
+ if (mode_arg)
882
+ err = fprintf(stderr, "Option %s cannot be used together with %s\n", opt, mode_arg);
883
+ else {
884
+ mode = zsv_prop_get_mode(opt);
885
+ mode_arg = opt;
886
+ if (mode == zsv_prop_mode_export || mode == zsv_prop_mode_import || mode == zsv_prop_mode_copy) {
887
+ if (++i < m_argc)
888
+ mode_value = m_argv[i];
889
+ else
890
+ err = fprintf(stderr, "Option %s requires a value\n", opt);
891
+ }
892
+ }
893
+ } else if (!strcmp(opt, "--auto")) {
894
+ if (opts.d != ZSV_PROP_ARG_NONE && opts.R != ZSV_PROP_ARG_NONE)
895
+ err = fprintf(stderr, "--auto specified, but all other properties also specified");
896
+ else {
897
+ if (opts.d == ZSV_PROP_ARG_NONE)
898
+ opts.d = ZSV_PROP_ARG_AUTO;
899
+ if (opts.R == ZSV_PROP_ARG_NONE)
900
+ opts.R = ZSV_PROP_ARG_AUTO;
901
+ }
902
+ } else if (!strcmp(opt, "--save"))
903
+ opts.save = 1;
904
+ else if (!strcmp(opt, "-f") || !strcmp(opt, "--overwrite"))
905
+ opts.overwrite = 1;
906
+ else if (!strcmp(opt, "--dry"))
907
+ dry = 1;
908
+ else {
909
+ fprintf(stderr, "Unrecognized option: %s\n", opt);
910
+ err = 1;
911
+ }
912
+ }
913
+
914
+ // check if option combination is invalid
915
+ // TO DO: check with zsv_prop_mode_clear
916
+ if (!err) {
917
+ char have_auto = opts.d == ZSV_PROP_ARG_AUTO || opts.R == ZSV_PROP_ARG_AUTO;
918
+ char have_specified = opts.d >= 0 || opts.R >= 0;
919
+ char have_remove = opts.d == ZSV_PROP_ARG_REMOVE || opts.R == ZSV_PROP_ARG_REMOVE;
920
+
921
+ if (have_auto && (have_specified || have_remove)) {
922
+ fprintf(stderr, "Non-auto options may not be mixed with auto options\n");
923
+ err = 1;
924
+ } else if ((have_auto || have_specified || have_remove || opts.save) && mode != zsv_prop_mode_default)
925
+ err = fprintf(stderr, "Invalid options in combination with %s\n", mode_arg);
926
+
927
+ if (have_specified || have_remove) {
928
+ opts.save = 1;
929
+ opts.overwrite = 1;
930
+ }
931
+ }
932
+
933
+ if (!err) {
934
+ switch (mode) {
935
+ case zsv_prop_mode_clear:
936
+ if (!(filepath && *filepath))
937
+ err = fprintf(stderr, "--clear: please specify an input file\n");
938
+ else {
939
+ struct prop_opts opts2 = {0};
940
+ opts2.d = ZSV_PROP_ARG_NONE;
941
+ opts2.R = ZSV_PROP_ARG_NONE;
942
+ if (memcmp(&opts, &opts2, sizeof(opts)))
943
+ err = fprintf(stderr, "--clear cannot be used in conjunction with any other options\n");
944
+ else {
945
+ unsigned char *cache_path = zsv_cache_path(filepath, NULL, 0);
946
+ if (!cache_path)
947
+ err = ENOMEM;
948
+ else if (zsv_dir_exists((const char *)cache_path))
949
+ err = zsv_remove_dir_recursive(cache_path);
950
+ free(cache_path);
951
+ }
952
+ }
953
+ break;
954
+ case zsv_prop_mode_list_files:
955
+ err = zsv_prop_execute_list_files(filepath, verbose);
956
+ break;
957
+ case zsv_prop_mode_clean:
958
+ err = zsv_prop_execute_clean((const char *)filepath, dry, verbose);
959
+ break;
960
+ case zsv_prop_mode_copy:
961
+ err = zsv_prop_execute_copy((const char *)filepath, mode_value, opts.overwrite, dry, verbose);
962
+ break;
963
+ case zsv_prop_mode_export:
964
+ err = zsv_prop_execute_export((const char *)filepath, mode_value && strcmp(mode_value, "-") ? mode_value : NULL,
965
+ verbose);
966
+ break;
967
+ case zsv_prop_mode_import:
968
+ err = zsv_prop_execute_import((const char *)filepath, mode_value && strcmp(mode_value, "-") ? mode_value : NULL,
969
+ opts.overwrite, dry, verbose);
970
+ break;
971
+ case zsv_prop_mode_default: {
972
+ struct zsv_opts zsv_opts;
973
+ zsv_args_to_opts(m_argc, m_argv, &m_argc, m_argv, &zsv_opts);
974
+ err = zsv_prop_execute_default(filepath, &zsv_opts, opts);
975
+ } break;
976
+ }
977
+ }
978
+ }
979
+ return err;
980
+ }