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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE +21 -0
- data/README.md +311 -0
- data/ext/zsv/common.h +34 -0
- data/ext/zsv/extconf.rb +137 -0
- data/ext/zsv/options.c +126 -0
- data/ext/zsv/options.h +31 -0
- data/ext/zsv/options_internal.h +8 -0
- data/ext/zsv/parser.c +300 -0
- data/ext/zsv/parser.h +62 -0
- data/ext/zsv/row.c +122 -0
- data/ext/zsv/row.h +39 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/2db.c +756 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/2json.c +381 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/2tsv.c +228 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/help.c +123 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/license.c +39 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/register.c +104 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/thirdparty.c +41 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/unregister.c +1 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/builtin/version.c +14 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/check/simdutf_wrapper.h +19 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/check/utf8.c +116 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/check.c +194 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/cli.c +796 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/cli_const.h +41 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/cli_export.h +16 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/cli_ini.c +280 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/cli_internal.h +36 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare.c +913 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare.h +23 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare_added_column.c +20 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare_internal.h +140 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare_sort.c +91 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/compare_unique_colname.c +81 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/count-pull.c +82 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/count.c +404 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/desc.c +569 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/echo.c +365 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/ext_example/my_extension.c +366 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/ext_example/mysheet_extension.c +341 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/ext_template/YOUR_EXTENSION_zsvext.c +263 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/inih/ini.c +298 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/inih/ini.h +157 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/json_numeric.c +177 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/jsonwriter.c +444 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/jsonwriter.h +145 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/json_writer-1.01/utils.c +110 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/memfile-1.0/include/memfile.h +15 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/memfile-1.0/src/memfile.c +64 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sglib/sglib.h +1955 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/simdutf/simdutf.h +6802 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3.c +230517 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3.h +12174 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_and_csv_vtab.c +2 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-mem.c +142 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-mem.h +49 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab-zsv.c +485 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3_csv_vtab.c +1015 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/sqlite3ext.h +663 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/sqlite3/vtab_helper.c +85 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_common.h +75 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_gen.h +167 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_parse.h +228 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_tree.h +186 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/build/yajl-2.1.1/include/yajl/yajl_version.h +23 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_common.h +76 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_gen.h +167 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_parse.h +238 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/api/yajl_tree.h +186 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl.c +184 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_alloc.c +52 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_alloc.h +34 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_buf.c +103 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_buf.h +57 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_bytestack.h +69 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_encode.c +220 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_encode.h +34 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_gen.c +362 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_lex.c +764 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_lex.h +117 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_parser.c +508 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_parser.h +78 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_tree.c +505 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl/src/yajl_version.c +7 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper/json_value.h +59 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper/yajl_helper.h +208 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper.c +795 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/external/yajl_helper/yajl_helper_internal.h +28 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/flatten.c +851 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/jq.c +106 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/jq.h +6 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/mv.c +113 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/noop.c +90 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/overwrite.c +295 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/paste.c +175 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/pretty.c +693 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/prop.c +980 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/rm.c +131 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/fixed.c +130 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/internal.h +118 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/parallel.c +45 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/parallel.h +41 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/processing.c +107 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/rand.c +20 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/regex.c +61 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/search.c +14 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/selection.c +192 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select/usage.c +72 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select-pull.c +812 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/select.c +753 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/serialize.c +372 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/curses.h +15 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/cursor.c +119 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/errors.c +45 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/file.c +63 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/file.h +12 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/filter.c +166 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/handlers.c +214 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/handlers_internal.h +128 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/help.c +43 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/index.c +81 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/index.h +25 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/key-bindings.c +325 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/key-bindings.h +73 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/lexer.c +203 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/newline_handler.c +7 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/pivot.c +318 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/procedure.c +134 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/procedure.h +119 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/read-data.c +322 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/screen_buffer.c +203 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/screen_buffer.h +36 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sheet-sql.c +167 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sheet_internal.h +36 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/sqlfilter.c +153 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/terminfo.c +32 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/transformation.c +312 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/transformation.h +29 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/ui_buffer.c +266 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/usage.c +9 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet/utf8-width.c +60 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sheet.c +1007 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sql.c +453 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sql_internal.c +101 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/sql_internal.h +49 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/stack.c +393 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/arg.c +322 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/cache.c +228 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/cat.c +91 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/chunk.c +240 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/chunk.h +63 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/clock.c +57 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/db.c +148 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs-no-jq.c +2 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs.c +427 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs_from_json.c +253 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/dirs_to_json.c +121 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/dl.c +20 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/emcc/fs_api.c +159 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/err.c +24 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/file-mem.c +180 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/file.c +256 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/index.c +197 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/index.h +49 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/jq.c +400 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/json.c +120 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/mem.c +18 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/memmem.c +132 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/os.c +178 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/overwrite.c +258 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/overwrite_writer.c +246 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8-test.c +123 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8.c +153 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/pcre2-8/pcre2-8.h +54 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/prop.c +267 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/signal.c +53 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/string.c +357 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/dir_exists_longpath.c +83 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/dl.c +33 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/fopen_longpath.c +184 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/foreach_dirent_longpath.c +292 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/io.c +259 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/io.h +13 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/mkdir_longpath.c +255 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/win/remove_longpath.c +96 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/utils/writer.c +361 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/zsv_command.h +40 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/zsv_command_standalone.c +16 -0
- data/ext/zsv/vendor/zsv-1.3.0/app/zsv_main.h +44 -0
- data/ext/zsv/vendor/zsv-1.3.0/examples/js/zsv_parser_api_dummy.c +3 -0
- data/ext/zsv/vendor/zsv-1.3.0/examples/lib/parse_by_chunk.c +100 -0
- data/ext/zsv/vendor/zsv-1.3.0/examples/lib/print_my_column.c +143 -0
- data/ext/zsv/vendor/zsv-1.3.0/examples/lib/pull.c +89 -0
- data/ext/zsv/vendor/zsv-1.3.0/examples/lib/simple.c +123 -0
- data/ext/zsv/vendor/zsv-1.3.0/fuzz/fuzz.c +16 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/api.h +336 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/common.h +361 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/implementation.h +62 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/implementation_private.h +113 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext/sheet.h +73 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/ext.h +329 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/arg.h +90 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/cache.h +49 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/clock.h +36 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/compiler.h +58 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/db.h +19 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/dirs.h +147 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/dl.h +22 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/emcc/fs_api.h +28 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/err.h +22 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/file-mem.h +17 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/file.h +99 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/jq.h +65 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/json.h +19 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/mem.h +19 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/memmem.h +13 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/os.h +54 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/overwrite.h +71 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/overwrite_writer.h +53 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/prop.h +107 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/signal.h +18 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/sql.h +11 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/string.h +148 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/utf8.h +41 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/win/dl.h +25 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/utils/writer.h +101 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv/zsv_export.h +33 -0
- data/ext/zsv/vendor/zsv-1.3.0/include/zsv.h +20 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/vector_delim.c +60 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/zsv.c +484 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/zsv_internal.c +731 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/zsv_scan_delim.c +285 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/zsv_scan_fixed.c +88 -0
- data/ext/zsv/vendor/zsv-1.3.0/src/zsv_strencode.c +51 -0
- data/ext/zsv/zsv_ext.c +343 -0
- data/lib/zsv/version.rb +5 -0
- data/lib/zsv.rb +81 -0
- metadata +340 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2021-2022 Liquidaty and zsv contributors. All rights reserved.
|
|
3
|
+
* This file is part of zsv/lib, distributed under the license defined at
|
|
4
|
+
* https://opensource.org/licenses/MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <jsonwriter.h>
|
|
11
|
+
#include <sqlite3.h>
|
|
12
|
+
|
|
13
|
+
#define ZSV_COMMAND 2json
|
|
14
|
+
#include "zsv_command.h"
|
|
15
|
+
|
|
16
|
+
#include <zsv/utils/writer.h>
|
|
17
|
+
#include <zsv/utils/mem.h>
|
|
18
|
+
#include <zsv/utils/db.h>
|
|
19
|
+
|
|
20
|
+
struct zsv_2json_header {
|
|
21
|
+
struct zsv_2json_header *next;
|
|
22
|
+
char *name;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
#define LQ_2JSON_MAX_INDEXES 32
|
|
26
|
+
|
|
27
|
+
struct zsv_2json_data {
|
|
28
|
+
zsv_parser parser;
|
|
29
|
+
jsonwriter_handle jsw;
|
|
30
|
+
|
|
31
|
+
size_t rows_processed; // includes header row
|
|
32
|
+
|
|
33
|
+
struct {
|
|
34
|
+
const char *clauses[LQ_2JSON_MAX_INDEXES];
|
|
35
|
+
char unique[LQ_2JSON_MAX_INDEXES];
|
|
36
|
+
unsigned count;
|
|
37
|
+
} indexes;
|
|
38
|
+
|
|
39
|
+
struct zsv_2json_header *headers, *current_header;
|
|
40
|
+
struct zsv_2json_header **headers_next;
|
|
41
|
+
|
|
42
|
+
char *db_tablename;
|
|
43
|
+
|
|
44
|
+
#define ZSV_JSON_SCHEMA_OBJECT 1
|
|
45
|
+
#define ZSV_JSON_SCHEMA_DATABASE 2
|
|
46
|
+
unsigned char schema : 2;
|
|
47
|
+
unsigned char no_header : 1;
|
|
48
|
+
unsigned char no_empty : 1;
|
|
49
|
+
unsigned char err : 1;
|
|
50
|
+
unsigned char from_db : 1;
|
|
51
|
+
unsigned char compact : 1;
|
|
52
|
+
unsigned char _ : 1;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
static void zsv_2json_cleanup(struct zsv_2json_data *data) {
|
|
56
|
+
for (struct zsv_2json_header *next, *h = data->headers; h; h = next) {
|
|
57
|
+
next = h->next;
|
|
58
|
+
if (h->name)
|
|
59
|
+
free(h->name);
|
|
60
|
+
free(h);
|
|
61
|
+
}
|
|
62
|
+
free(data->db_tablename);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static void write_header_cell(struct zsv_2json_data *data, const unsigned char *utf8_value, size_t len) {
|
|
66
|
+
if (data->schema == ZSV_JSON_SCHEMA_OBJECT) {
|
|
67
|
+
struct zsv_2json_header *h;
|
|
68
|
+
if (!(h = calloc(1, sizeof(*h)))) {
|
|
69
|
+
fprintf(stderr, "Out of memory!\n");
|
|
70
|
+
data->err = 1;
|
|
71
|
+
} else {
|
|
72
|
+
*data->headers_next = h;
|
|
73
|
+
data->headers_next = &h->next;
|
|
74
|
+
if ((h->name = malloc(len + 1))) {
|
|
75
|
+
memcpy(h->name, utf8_value, len);
|
|
76
|
+
h->name[len] = '\0';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// to do: add options to set data type, etc
|
|
81
|
+
jsonwriter_start_object(data->jsw);
|
|
82
|
+
jsonwriter_object_key(data->jsw, "name");
|
|
83
|
+
jsonwriter_strn(data->jsw, utf8_value, len);
|
|
84
|
+
jsonwriter_end(data->jsw);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static void write_data_cell(struct zsv_2json_data *data, const unsigned char *utf8_value, size_t len) {
|
|
89
|
+
if (data->schema == ZSV_JSON_SCHEMA_OBJECT) {
|
|
90
|
+
if (!data->current_header)
|
|
91
|
+
return;
|
|
92
|
+
char *current_header_name = data->current_header->name;
|
|
93
|
+
data->current_header = data->current_header->next;
|
|
94
|
+
|
|
95
|
+
if (len || !data->no_empty)
|
|
96
|
+
jsonwriter_object_key(data->jsw, current_header_name);
|
|
97
|
+
else
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
jsonwriter_strn(data->jsw, utf8_value, len);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static char *zsv_2json_db_first_tname(sqlite3 *db) {
|
|
104
|
+
char *tname = NULL;
|
|
105
|
+
sqlite3_stmt *stmt = NULL;
|
|
106
|
+
const char *sql = "select name from sqlite_master where type = 'table'";
|
|
107
|
+
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK)
|
|
108
|
+
fprintf(stderr, "Unable to prepare %s: %s\n", sql, sqlite3_errmsg(db));
|
|
109
|
+
else if (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
110
|
+
const unsigned char *text = sqlite3_column_text(stmt, 0);
|
|
111
|
+
if (text) {
|
|
112
|
+
int len = sqlite3_column_bytes(stmt, 0);
|
|
113
|
+
tname = zsv_memdup(text, len);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (stmt)
|
|
117
|
+
sqlite3_finalize(stmt);
|
|
118
|
+
return tname;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static void zsv_2json_row(void *ctx) {
|
|
122
|
+
struct zsv_2json_data *data = ctx;
|
|
123
|
+
unsigned int cols = zsv_cell_count(data->parser);
|
|
124
|
+
if (cols) {
|
|
125
|
+
char obj = 0;
|
|
126
|
+
char arr = 0;
|
|
127
|
+
if (!data->rows_processed) { // header row
|
|
128
|
+
jsonwriter_start_array(data->jsw); // start array of rows
|
|
129
|
+
if (data->schema == ZSV_JSON_SCHEMA_DATABASE) {
|
|
130
|
+
jsonwriter_start_object(data->jsw); // start this row
|
|
131
|
+
obj = 1;
|
|
132
|
+
|
|
133
|
+
if (data->db_tablename)
|
|
134
|
+
jsonwriter_object_cstr(data->jsw, "name", data->db_tablename);
|
|
135
|
+
|
|
136
|
+
// to do: check index syntax (as of now, we just take argument value
|
|
137
|
+
// as-is and assume it will translate into a valid SQLITE3 command)
|
|
138
|
+
char have_index = 0;
|
|
139
|
+
for (unsigned i = 0; i < data->indexes.count; i++) {
|
|
140
|
+
const char *name_start = data->indexes.clauses[i];
|
|
141
|
+
const char *on = strstr(name_start, " on ");
|
|
142
|
+
if (on) {
|
|
143
|
+
on += 4;
|
|
144
|
+
while (*on == ' ')
|
|
145
|
+
on++;
|
|
146
|
+
}
|
|
147
|
+
if (!on || !*on)
|
|
148
|
+
continue;
|
|
149
|
+
|
|
150
|
+
const char *name_end = name_start;
|
|
151
|
+
while (name_end && *name_end && *name_end != ' ')
|
|
152
|
+
name_end++;
|
|
153
|
+
|
|
154
|
+
if (name_end > name_start) {
|
|
155
|
+
if (!have_index) {
|
|
156
|
+
have_index = 1;
|
|
157
|
+
jsonwriter_object_object(data->jsw, "indexes");
|
|
158
|
+
}
|
|
159
|
+
char *tmp = zsv_memdup(name_start, name_end - name_start);
|
|
160
|
+
jsonwriter_object_object(data->jsw, tmp); // this index
|
|
161
|
+
free(tmp);
|
|
162
|
+
jsonwriter_object_cstr(data->jsw, "on", on);
|
|
163
|
+
if (data->indexes.unique[i])
|
|
164
|
+
jsonwriter_object_bool(data->jsw, "unique", 1);
|
|
165
|
+
jsonwriter_end_object(data->jsw); // end this index
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (have_index)
|
|
169
|
+
jsonwriter_end_object(data->jsw); // indexes
|
|
170
|
+
|
|
171
|
+
jsonwriter_object_array(data->jsw, "columns");
|
|
172
|
+
arr = 1;
|
|
173
|
+
} else if (data->schema != ZSV_JSON_SCHEMA_OBJECT) {
|
|
174
|
+
jsonwriter_start_array(data->jsw); // start this row
|
|
175
|
+
arr = 1;
|
|
176
|
+
}
|
|
177
|
+
} else { // processing a data row, not header row
|
|
178
|
+
if (data->schema == ZSV_JSON_SCHEMA_OBJECT) {
|
|
179
|
+
jsonwriter_start_object(data->jsw); // start this row
|
|
180
|
+
obj = 1;
|
|
181
|
+
} else {
|
|
182
|
+
if (data->rows_processed == 1 && data->schema == ZSV_JSON_SCHEMA_DATABASE)
|
|
183
|
+
jsonwriter_start_array(data->jsw); // start the table-data element
|
|
184
|
+
jsonwriter_start_array(data->jsw); // start this row
|
|
185
|
+
arr = 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (unsigned int i = 0; i < cols; i++) {
|
|
190
|
+
struct zsv_cell cell = zsv_get_cell(data->parser, i);
|
|
191
|
+
// output this cell
|
|
192
|
+
if (!data->rows_processed && !data->no_header) // this is header row
|
|
193
|
+
write_header_cell(data, cell.str, cell.len);
|
|
194
|
+
else
|
|
195
|
+
write_data_cell(data, cell.str, cell.len);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// end this row
|
|
199
|
+
if (arr)
|
|
200
|
+
jsonwriter_end_array(data->jsw);
|
|
201
|
+
if (obj)
|
|
202
|
+
jsonwriter_end_object(data->jsw);
|
|
203
|
+
data->rows_processed++;
|
|
204
|
+
}
|
|
205
|
+
data->current_header = data->headers;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
static int zsv_db2json(const char *input_filename, char **tname, jsonwriter_handle jsw) {
|
|
209
|
+
sqlite3 *db;
|
|
210
|
+
int rc =
|
|
211
|
+
sqlite3_open_v2(input_filename, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_SHAREDCACHE, NULL);
|
|
212
|
+
int err = 0;
|
|
213
|
+
if (!db)
|
|
214
|
+
fprintf(stderr, "Unable to open db at %s\n", input_filename), err = 1;
|
|
215
|
+
else if (rc != SQLITE_OK)
|
|
216
|
+
fprintf(stderr, "Unable to open db at %s: %s\n", input_filename, sqlite3_errmsg(db)), err = 1;
|
|
217
|
+
else if (!*tname)
|
|
218
|
+
*tname = zsv_2json_db_first_tname(db);
|
|
219
|
+
|
|
220
|
+
if (!*tname)
|
|
221
|
+
fprintf(stderr, "No table name provided, and none found in %s\n", input_filename), err = 1;
|
|
222
|
+
else
|
|
223
|
+
err = zsv_dbtable2json(db, *tname, jsw, 0);
|
|
224
|
+
return err;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *optsp,
|
|
228
|
+
struct zsv_prop_handler *custom_prop_handler) {
|
|
229
|
+
struct zsv_opts opts = *optsp;
|
|
230
|
+
struct zsv_2json_data data = {0};
|
|
231
|
+
data.headers_next = &data.headers;
|
|
232
|
+
|
|
233
|
+
const char *usage[] = {
|
|
234
|
+
APPNAME ": streaming CSV to JSON converter, or SQLite3 DB to JSON converter",
|
|
235
|
+
"",
|
|
236
|
+
"Usage: " APPNAME " [options] [file.csv]",
|
|
237
|
+
"",
|
|
238
|
+
"Options:",
|
|
239
|
+
" -h,--help : show usage",
|
|
240
|
+
" -o,--output <filename> : filename to write output to",
|
|
241
|
+
" --compact : output compact JSON",
|
|
242
|
+
" --from-db : input is sqlite3 database",
|
|
243
|
+
" --db-table <table_name> : name of table in input database to convert",
|
|
244
|
+
" --object : output as array of objects",
|
|
245
|
+
" --no-empty : omit empty properties (only with --object)",
|
|
246
|
+
" --database : output in database schema",
|
|
247
|
+
" --no-header : treat the header row as a data row",
|
|
248
|
+
" --index <name_on_expr> : add index to database schema",
|
|
249
|
+
" --unique-index <name_on_expr> : add unique index to database schema",
|
|
250
|
+
NULL,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
FILE *out = NULL;
|
|
254
|
+
const char *input_path = NULL;
|
|
255
|
+
enum zsv_status err = zsv_status_ok;
|
|
256
|
+
int done = 0;
|
|
257
|
+
|
|
258
|
+
for (int i = 1; !err && !done && i < argc; i++) {
|
|
259
|
+
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
|
|
260
|
+
for (int j = 0; usage[j]; j++)
|
|
261
|
+
fprintf(stdout, "%s\n", usage[j]);
|
|
262
|
+
done = 1;
|
|
263
|
+
} else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) {
|
|
264
|
+
if (++i >= argc)
|
|
265
|
+
fprintf(stderr, "%s option requires a filename value\n", argv[i - 1]), err = zsv_status_error;
|
|
266
|
+
else if (out && out != stdout)
|
|
267
|
+
fprintf(stderr, "Output file specified more than once\n"), err = zsv_status_error;
|
|
268
|
+
else if (!(out = fopen(argv[i], "wb")))
|
|
269
|
+
fprintf(stderr, "Unable to open for writing: %s\n", argv[i]), err = zsv_status_error;
|
|
270
|
+
} else if (!strcmp(argv[i], "--index") || !strcmp(argv[i], "--unique-index")) {
|
|
271
|
+
if (++i >= argc)
|
|
272
|
+
fprintf(stderr, "%s option requires a filename value\n", argv[i - 1]), err = zsv_status_error;
|
|
273
|
+
else if (data.indexes.count > LQ_2JSON_MAX_INDEXES)
|
|
274
|
+
fprintf(stderr, "Max index count exceeded; ignoring %s\n", argv[i]), err = zsv_status_error;
|
|
275
|
+
else if (!strstr(argv[i], " on "))
|
|
276
|
+
fprintf(stderr, "Index value should be in the form of 'index_name on expr'; got %s\n", argv[i]),
|
|
277
|
+
err = zsv_status_error;
|
|
278
|
+
else {
|
|
279
|
+
if (!strcmp(argv[i - 1], "--unique-index"))
|
|
280
|
+
data.indexes.unique[data.indexes.count] = 1;
|
|
281
|
+
data.indexes.clauses[data.indexes.count++] = argv[i];
|
|
282
|
+
}
|
|
283
|
+
} else if (!strcmp(argv[i], "--no-empty")) {
|
|
284
|
+
data.no_empty = 1;
|
|
285
|
+
} else if (!strcmp(argv[i], "--db-table")) {
|
|
286
|
+
if (++i >= argc)
|
|
287
|
+
fprintf(stderr, "%s option requires a table name value\n", argv[i - 1]), err = zsv_status_error;
|
|
288
|
+
else if (data.db_tablename)
|
|
289
|
+
fprintf(stderr, "%s option specified more than once\n", argv[i - 1]), err = zsv_status_error;
|
|
290
|
+
else
|
|
291
|
+
data.db_tablename = strdup(argv[i]);
|
|
292
|
+
} else if (!strcmp(argv[i], "--from-db")) {
|
|
293
|
+
if (++i >= argc)
|
|
294
|
+
fprintf(stderr, "%s option requires a filename value\n", argv[i - 1]), err = zsv_status_error;
|
|
295
|
+
else if (opts.stream)
|
|
296
|
+
fprintf(stderr, "Input file specified more than once\n"), err = zsv_status_error;
|
|
297
|
+
else if (!(opts.stream = fopen(argv[i], "rb")))
|
|
298
|
+
fprintf(stderr, "Unable to open for reading: %s\n", argv[i]), err = zsv_status_error;
|
|
299
|
+
else {
|
|
300
|
+
input_path = argv[i];
|
|
301
|
+
data.from_db = 1;
|
|
302
|
+
}
|
|
303
|
+
} else if (!strcmp(argv[i], "--database") || !strcmp(argv[i], "--object")) {
|
|
304
|
+
if (data.schema)
|
|
305
|
+
fprintf(stderr, "Output schema specified more than once\n"), err = zsv_status_error;
|
|
306
|
+
else if (!strcmp(argv[i], "--database"))
|
|
307
|
+
data.schema = ZSV_JSON_SCHEMA_DATABASE;
|
|
308
|
+
else
|
|
309
|
+
data.schema = ZSV_JSON_SCHEMA_OBJECT;
|
|
310
|
+
} else if (!strcmp(argv[i], "--no-header"))
|
|
311
|
+
data.no_header = 1;
|
|
312
|
+
else if (!strcmp(argv[i], "--compact"))
|
|
313
|
+
data.compact = 1;
|
|
314
|
+
else {
|
|
315
|
+
if (opts.stream)
|
|
316
|
+
fprintf(stderr, "Input file specified more than once\n"), err = zsv_status_error;
|
|
317
|
+
else if (!(opts.stream = fopen(argv[i], "rb")))
|
|
318
|
+
fprintf(stderr, "Unable to open for reading: %s\n", argv[i]), err = zsv_status_error;
|
|
319
|
+
else
|
|
320
|
+
input_path = argv[i];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (!(err || done)) {
|
|
325
|
+
if (data.indexes.count && data.schema != ZSV_JSON_SCHEMA_DATABASE)
|
|
326
|
+
fprintf(stderr, "--index/--unique-index can only be used with --database\n"), err = zsv_status_error;
|
|
327
|
+
else if (data.no_header && data.schema)
|
|
328
|
+
fprintf(stderr, "--no-header cannot be used together with --object or --database\n"), err = zsv_status_error;
|
|
329
|
+
else if (data.no_empty && data.schema != ZSV_JSON_SCHEMA_OBJECT)
|
|
330
|
+
fprintf(stderr, "--no-empty can only be used with --object\n"), err = zsv_status_error;
|
|
331
|
+
else if (!opts.stream) {
|
|
332
|
+
if (data.from_db)
|
|
333
|
+
fprintf(stderr, "Database input specified, but no input file provided\n"), err = zsv_status_error;
|
|
334
|
+
else {
|
|
335
|
+
#ifdef NO_STDIN
|
|
336
|
+
fprintf(stderr, "Please specify an input file\n"), err = zsv_status_error;
|
|
337
|
+
#else
|
|
338
|
+
opts.stream = stdin;
|
|
339
|
+
#endif
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!(err || done)) {
|
|
345
|
+
if (!out)
|
|
346
|
+
out = stdout;
|
|
347
|
+
if (!(data.jsw = jsonwriter_new(out)))
|
|
348
|
+
err = zsv_status_error;
|
|
349
|
+
else {
|
|
350
|
+
if (data.compact)
|
|
351
|
+
jsonwriter_set_option(data.jsw, jsonwriter_option_compact);
|
|
352
|
+
if (data.from_db) {
|
|
353
|
+
if (opts.stream != stdin) {
|
|
354
|
+
fclose(opts.stream);
|
|
355
|
+
opts.stream = NULL;
|
|
356
|
+
}
|
|
357
|
+
err = zsv_db2json(input_path, &data.db_tablename, data.jsw);
|
|
358
|
+
} else {
|
|
359
|
+
opts.row_handler = zsv_2json_row;
|
|
360
|
+
opts.ctx = &data;
|
|
361
|
+
if (zsv_new_with_properties(&opts, custom_prop_handler, input_path, &data.parser) == zsv_status_ok) {
|
|
362
|
+
zsv_handle_ctrl_c_signal();
|
|
363
|
+
while (!data.err && !zsv_signal_interrupted && zsv_parse_more(data.parser) == zsv_status_ok)
|
|
364
|
+
;
|
|
365
|
+
zsv_finish(data.parser);
|
|
366
|
+
zsv_delete(data.parser);
|
|
367
|
+
jsonwriter_end_all(data.jsw);
|
|
368
|
+
}
|
|
369
|
+
err = data.err;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
jsonwriter_delete(data.jsw);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
zsv_2json_cleanup(&data);
|
|
376
|
+
if (opts.stream && opts.stream != stdin)
|
|
377
|
+
fclose(opts.stream);
|
|
378
|
+
if (out && out != stdout)
|
|
379
|
+
fclose(out);
|
|
380
|
+
return err;
|
|
381
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2021 Liquidaty and the zsv/lib contributors
|
|
3
|
+
* All rights reserved
|
|
4
|
+
*
|
|
5
|
+
* This file is part of zsv/lib, distributed under the license defined at
|
|
6
|
+
* https://opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include <stdio.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
#define ZSV_COMMAND 2tsv
|
|
13
|
+
#include "zsv_command.h"
|
|
14
|
+
|
|
15
|
+
#include <zsv/utils/utf8.h>
|
|
16
|
+
|
|
17
|
+
enum zsv_2tsv_status {
|
|
18
|
+
zsv_2tsv_status_ok = 0,
|
|
19
|
+
zsv_2tsv_status_out_of_memory
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
#define ZSV_2TSV_BUFF_SIZE 65536
|
|
23
|
+
|
|
24
|
+
struct static_buff {
|
|
25
|
+
char *buff; // will be ZSV_2TSV_BUFF_SIZE
|
|
26
|
+
size_t used;
|
|
27
|
+
FILE *stream;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
struct zsv_2tsv_data {
|
|
31
|
+
zsv_parser parser;
|
|
32
|
+
struct static_buff out;
|
|
33
|
+
unsigned char convert_to_space : 1;
|
|
34
|
+
unsigned char _ : 7;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
__attribute__((always_inline)) static inline void zsv_2tsv_flush(struct static_buff *b) {
|
|
38
|
+
fwrite(b->buff, b->used, 1, b->stream);
|
|
39
|
+
b->used = 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static inline void zsv_2tsv_write(struct static_buff *b, const unsigned char *s, size_t n) {
|
|
43
|
+
if (n) {
|
|
44
|
+
if (VERY_UNLIKELY(n + b->used > ZSV_2TSV_BUFF_SIZE)) {
|
|
45
|
+
zsv_2tsv_flush(b);
|
|
46
|
+
if (VERY_UNLIKELY(n > ZSV_2TSV_BUFF_SIZE)) { // n too big, so write directly
|
|
47
|
+
fwrite(s, n, 1, b->stream);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// n + used < buff size
|
|
52
|
+
memcpy(b->buff + b->used, s, n);
|
|
53
|
+
b->used += n;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// zsv_to_tsv(): convert, if necessary, to text suitable for tab-delimited output.
|
|
58
|
+
// - return NULL if nothing to convert
|
|
59
|
+
// - else, return allocated char * of converted tsv (caller must free), and update *lenp
|
|
60
|
+
// - on error, set *err
|
|
61
|
+
__attribute__((always_inline)) static inline unsigned char *zsv_to_tsv(const unsigned char *utf8, size_t *len,
|
|
62
|
+
enum zsv_2tsv_status *err,
|
|
63
|
+
char convert_to_space) {
|
|
64
|
+
// replace tab, newline and lf with \t, \n or \r or backslash
|
|
65
|
+
size_t do_convert = 0;
|
|
66
|
+
for (size_t i = 0; i < *len; i++) {
|
|
67
|
+
if (UNLIKELY(utf8[i] == '\t' || utf8[i] == '\n' || utf8[i] == '\r' || utf8[i] == '\\'))
|
|
68
|
+
do_convert++;
|
|
69
|
+
}
|
|
70
|
+
if (LIKELY(do_convert == 0))
|
|
71
|
+
return NULL;
|
|
72
|
+
|
|
73
|
+
unsigned char *converted = malloc(*len + do_convert + 1);
|
|
74
|
+
if (!converted)
|
|
75
|
+
*err = zsv_2tsv_status_out_of_memory;
|
|
76
|
+
else {
|
|
77
|
+
size_t j = 0;
|
|
78
|
+
for (size_t i = 0; i < *len; i++) {
|
|
79
|
+
if (UNLIKELY(utf8[i] == '\t' || utf8[i] == '\n' || utf8[i] == '\r' || utf8[i] == '\\')) {
|
|
80
|
+
if (convert_to_space)
|
|
81
|
+
converted[j++] = utf8[i] == '\\' ? '\\' : ' ';
|
|
82
|
+
else {
|
|
83
|
+
converted[j++] = '\\';
|
|
84
|
+
converted[j++] = utf8[i] == '\t' ? 't' : utf8[i] == '\n' ? 'n' : utf8[i] == '\r' ? 'r' : '\\';
|
|
85
|
+
}
|
|
86
|
+
} else
|
|
87
|
+
converted[j++] = utf8[i];
|
|
88
|
+
}
|
|
89
|
+
converted[j] = '\0';
|
|
90
|
+
*len = j;
|
|
91
|
+
}
|
|
92
|
+
return converted;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
__attribute__((always_inline)) static inline void zsv_2tsv_cell(struct zsv_2tsv_data *data, unsigned char *utf8_value,
|
|
96
|
+
size_t len, char no_newline_or_slash,
|
|
97
|
+
char convert_to_space) {
|
|
98
|
+
// output cell contents (converted if necessary)
|
|
99
|
+
if (len) {
|
|
100
|
+
enum zsv_2tsv_status err = zsv_2tsv_status_ok;
|
|
101
|
+
if (VERY_LIKELY(no_newline_or_slash && !memchr(utf8_value, '\t', len))) {
|
|
102
|
+
zsv_2tsv_write(&data->out, utf8_value, len);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// if we're here, there either definitely an embedded tab, or maybe an embedded \n or \r
|
|
107
|
+
unsigned char *converted = zsv_to_tsv(utf8_value, &len, &err, convert_to_space);
|
|
108
|
+
if (converted != NULL) {
|
|
109
|
+
zsv_2tsv_write(&data->out, converted, len);
|
|
110
|
+
free(converted);
|
|
111
|
+
} else if (UNLIKELY(err))
|
|
112
|
+
fprintf(stderr, "Out of memory!\n");
|
|
113
|
+
else
|
|
114
|
+
zsv_2tsv_write(&data->out, utf8_value, len);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static void zsv_2tsv_row(void *ctx) {
|
|
119
|
+
struct zsv_2tsv_data *data = ctx;
|
|
120
|
+
unsigned int cols = zsv_cell_count(data->parser);
|
|
121
|
+
if (cols) {
|
|
122
|
+
struct zsv_cell cell = zsv_get_cell(data->parser, 0);
|
|
123
|
+
struct zsv_cell end = zsv_get_cell(data->parser, cols - 1);
|
|
124
|
+
unsigned char *start = cell.str;
|
|
125
|
+
size_t row_len = end.str + end.len - start;
|
|
126
|
+
char no_newline_or_slash = 0;
|
|
127
|
+
if (LIKELY(!(memchr(start, '\n', row_len) || memchr(start, '\r', row_len) || memchr(start, '\\', row_len))))
|
|
128
|
+
no_newline_or_slash = 1;
|
|
129
|
+
|
|
130
|
+
const char convert_to_space = data->convert_to_space;
|
|
131
|
+
zsv_2tsv_cell(ctx, cell.str, cell.len, no_newline_or_slash, convert_to_space);
|
|
132
|
+
for (unsigned int i = 1; i < cols; i++) {
|
|
133
|
+
zsv_2tsv_write(&data->out, (const unsigned char *)"\t", 1);
|
|
134
|
+
cell = zsv_get_cell(data->parser, i);
|
|
135
|
+
zsv_2tsv_cell(ctx, cell.str, cell.len, no_newline_or_slash, convert_to_space);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
zsv_2tsv_write(&data->out, (const unsigned char *)"\n", 1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
int zsv_2tsv_usage(int rc) {
|
|
142
|
+
static const char *zsv_2tsv_usage_msg[] = {
|
|
143
|
+
APPNAME ": convert CSV to TSV (tab-delimited text) suitable for simple-delimiter",
|
|
144
|
+
" text processing. By default, embedded tabs or multilines will be escaped",
|
|
145
|
+
" to \\t, \\n or \\r, respectively",
|
|
146
|
+
"",
|
|
147
|
+
"Usage: " APPNAME " [filename] [options]",
|
|
148
|
+
" e.g. " APPNAME " < file.csv > file.tsv",
|
|
149
|
+
"",
|
|
150
|
+
"Options:",
|
|
151
|
+
" -o <file> : save output to <file>",
|
|
152
|
+
" --convert-to-space: convert embedded \\t, \\n or \\r to space",
|
|
153
|
+
NULL,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
for (size_t i = 0; zsv_2tsv_usage_msg[i]; i++)
|
|
157
|
+
fprintf(stdout, "%s\n", zsv_2tsv_usage_msg[i]);
|
|
158
|
+
|
|
159
|
+
return rc;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
int ZSV_MAIN_FUNC(ZSV_COMMAND)(int argc, const char *argv[], struct zsv_opts *optsp,
|
|
163
|
+
struct zsv_prop_handler *custom_prop_handler) {
|
|
164
|
+
struct zsv_opts opts = *optsp;
|
|
165
|
+
struct zsv_2tsv_data data = {0};
|
|
166
|
+
const char *input_path = NULL;
|
|
167
|
+
int err = 0;
|
|
168
|
+
for (int i = 1; !err && i < argc; i++) {
|
|
169
|
+
if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
|
|
170
|
+
return zsv_2tsv_usage(0);
|
|
171
|
+
if (!strcmp(argv[i], "--convert-to-space"))
|
|
172
|
+
data.convert_to_space = 1;
|
|
173
|
+
else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--output")) {
|
|
174
|
+
if (++i >= argc)
|
|
175
|
+
fprintf(stderr, "%s option requires a filename value\n", argv[i - 1]), err = 1;
|
|
176
|
+
else if (data.out.stream && data.out.stream != stdout)
|
|
177
|
+
fprintf(stderr, "Output file specified more than once\n"), err = 1;
|
|
178
|
+
else if (!(data.out.stream = fopen(argv[i], "wb")))
|
|
179
|
+
fprintf(stderr, "Unable to open for writing: %s\n", argv[i]), err = 1;
|
|
180
|
+
} else {
|
|
181
|
+
if (opts.stream)
|
|
182
|
+
fprintf(stderr, "Input file specified more than once\n"), err = 1;
|
|
183
|
+
else if (!(opts.stream = fopen(argv[i], "rb")))
|
|
184
|
+
fprintf(stderr, "Unable to open for reading: %s\n", argv[i]), err = 1;
|
|
185
|
+
else
|
|
186
|
+
input_path = argv[i];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (err) {
|
|
191
|
+
goto exit_2tsv;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!opts.stream) {
|
|
195
|
+
#ifdef NO_STDIN
|
|
196
|
+
fprintf(stderr, "Please specify an input file\n");
|
|
197
|
+
err = 1;
|
|
198
|
+
goto exit_2tsv;
|
|
199
|
+
#else
|
|
200
|
+
opts.stream = stdin;
|
|
201
|
+
#endif
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!data.out.stream)
|
|
205
|
+
data.out.stream = stdout;
|
|
206
|
+
|
|
207
|
+
opts.row_handler = zsv_2tsv_row;
|
|
208
|
+
opts.ctx = &data;
|
|
209
|
+
if (zsv_new_with_properties(&opts, custom_prop_handler, input_path, &data.parser) == zsv_status_ok) {
|
|
210
|
+
char output[ZSV_2TSV_BUFF_SIZE];
|
|
211
|
+
data.out.buff = output;
|
|
212
|
+
|
|
213
|
+
zsv_handle_ctrl_c_signal();
|
|
214
|
+
enum zsv_status status;
|
|
215
|
+
while (!zsv_signal_interrupted && (status = zsv_parse_more(data.parser)) == zsv_status_ok)
|
|
216
|
+
;
|
|
217
|
+
zsv_finish(data.parser);
|
|
218
|
+
zsv_delete(data.parser);
|
|
219
|
+
zsv_2tsv_flush(&data.out);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
exit_2tsv:
|
|
223
|
+
if (opts.stream && opts.stream != stdin)
|
|
224
|
+
fclose(opts.stream);
|
|
225
|
+
if (data.out.stream && data.out.stream != stdout)
|
|
226
|
+
fclose(data.out.stream);
|
|
227
|
+
return err;
|
|
228
|
+
}
|