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,255 @@
|
|
|
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
|
+
#include "io.h"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @brief Creates a directory, supporting paths longer than MAX_PATH.
|
|
12
|
+
*
|
|
13
|
+
* This function uses the Windows API CreateDirectoryW with the \\?\ prefix
|
|
14
|
+
* to reliably create directories even if their absolute path exceeds the
|
|
15
|
+
* traditional MAX_PATH limit.
|
|
16
|
+
*
|
|
17
|
+
* @param path A UTF-8 encoded string representing the absolute path of the
|
|
18
|
+
* directory to create. Relative paths are NOT reliably handled.
|
|
19
|
+
* Forward slashes '/' will be converted to backslashes '\'.
|
|
20
|
+
* @return 0 on success (directory created or already exists).
|
|
21
|
+
* Returns a non-zero Win32 error code on failure (see GetLastError()).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
DWORD zsv_mkdir_winlp(const char *path_utf8) {
|
|
25
|
+
if (path_utf8 == NULL || path_utf8[0] == '\0') {
|
|
26
|
+
fprintf(stderr, "Error: Input path is NULL or empty.\n");
|
|
27
|
+
return ERROR_INVALID_PARAMETER; // Or another suitable error code
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (strlen(path_utf8) < 260) { // try the easy way first
|
|
31
|
+
int rc = mkdir(path_utf8);
|
|
32
|
+
if (!rc || rc == EEXIST)
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
wchar_t *finalPath;
|
|
37
|
+
DWORD rc = zsv_pathToPrefixedWidePath(path_utf8, &finalPath);
|
|
38
|
+
if (rc)
|
|
39
|
+
return rc;
|
|
40
|
+
|
|
41
|
+
// 4. Call CreateDirectoryW
|
|
42
|
+
BOOL success = CreateDirectoryW(finalPath, NULL); // NULL for default security attributes
|
|
43
|
+
|
|
44
|
+
DWORD lastError = 0;
|
|
45
|
+
if (!success) {
|
|
46
|
+
lastError = GetLastError();
|
|
47
|
+
// It's okay if the directory already exists
|
|
48
|
+
if (lastError == ERROR_ALREADY_EXISTS) {
|
|
49
|
+
// printf("Debug: Directory already exists (considered success).\n");
|
|
50
|
+
lastError = 0; // Treat as success
|
|
51
|
+
} else {
|
|
52
|
+
fprintf(stderr, "Error: CreateDirectoryW failed (%lu) for path: %ls\n", lastError, finalPath);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
// printf("Debug: CreateDirectoryW succeeded for path: %ls\n", finalPath);
|
|
56
|
+
lastError = 0; // Success
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
free(finalPath);
|
|
60
|
+
|
|
61
|
+
return lastError; // Return 0 on success (or already exists), else the error code
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#ifdef DIRS_MKDIR_TEST
|
|
65
|
+
#define FILESLASH '\\'
|
|
66
|
+
|
|
67
|
+
#include "win/io.c"
|
|
68
|
+
#include "dirs_exists_longpath.c"
|
|
69
|
+
/**
|
|
70
|
+
* Check if a directory exists
|
|
71
|
+
* return true (non-zero) or false (zero)
|
|
72
|
+
*/
|
|
73
|
+
int zsv_dir_exists(const char *path) {
|
|
74
|
+
#ifdef WIN32
|
|
75
|
+
if (strlen(path) >= MAX_PATH)
|
|
76
|
+
return zsv_dir_exists_winlp(path);
|
|
77
|
+
|
|
78
|
+
// TO DO: support win long filepath prefix
|
|
79
|
+
// TO DO: work properly if dir exists but we don't have permission
|
|
80
|
+
wchar_t wpath[MAX_PATH];
|
|
81
|
+
mbstowcs(wpath, path, MAX_PATH);
|
|
82
|
+
|
|
83
|
+
DWORD attrs = GetFileAttributesW(wpath);
|
|
84
|
+
if (attrs == INVALID_FILE_ATTRIBUTES)
|
|
85
|
+
// Could check GetLastError() to see if it's a permission issue vs. not-found
|
|
86
|
+
return 0;
|
|
87
|
+
|
|
88
|
+
// If it has the directory attribute, it's presumably a directory
|
|
89
|
+
return (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
90
|
+
|
|
91
|
+
#else
|
|
92
|
+
struct stat path_stat;
|
|
93
|
+
if (!stat(path, &path_stat))
|
|
94
|
+
return S_ISDIR(path_stat.st_mode);
|
|
95
|
+
return 0;
|
|
96
|
+
#endif
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Make a directory, as well as any intermediate dirs
|
|
101
|
+
* return zero on success
|
|
102
|
+
*/
|
|
103
|
+
#ifdef WIN32
|
|
104
|
+
#define zsv_mkdir zsv_mkdir_winlp
|
|
105
|
+
#else
|
|
106
|
+
#define zsv_mkdir mkdir
|
|
107
|
+
#endif
|
|
108
|
+
int zsv_mkdirs(const char *path, char path_is_filename) {
|
|
109
|
+
// int rc = 0;
|
|
110
|
+
if (!path || !*path)
|
|
111
|
+
return -1;
|
|
112
|
+
size_t len = strlen(path);
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
#ifdef WIN32
|
|
116
|
+
// TO DO: handle windows long-file prefix "\\?\"
|
|
117
|
+
// for now, explicitly do not handle
|
|
118
|
+
if (len > 2 && path[2] == '?')
|
|
119
|
+
fprintf(stderr, "Invalid path (long file prefix not supported): %s\n", path);
|
|
120
|
+
#endif
|
|
121
|
+
*/
|
|
122
|
+
if (len < 1) // || len > FILENAME_MAX)
|
|
123
|
+
return -1;
|
|
124
|
+
|
|
125
|
+
char *tmp = strdup(path);
|
|
126
|
+
if (!tmp) {
|
|
127
|
+
perror(path);
|
|
128
|
+
return -1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (len && strchr("/\\", tmp[len - 1]))
|
|
132
|
+
tmp[--len] = 0;
|
|
133
|
+
|
|
134
|
+
int offset = 0;
|
|
135
|
+
#ifdef WIN32
|
|
136
|
+
if (len > 1) {
|
|
137
|
+
// starts with two slashes
|
|
138
|
+
if (strchr("/\\", tmp[0]) && strchr("/\\", tmp[1])) {
|
|
139
|
+
offset = 2;
|
|
140
|
+
// find the next slash
|
|
141
|
+
char *path_end = tmp + 3;
|
|
142
|
+
while (*path_end && !strchr("/\\", *path_end))
|
|
143
|
+
path_end++;
|
|
144
|
+
if (*path_end)
|
|
145
|
+
path_end++;
|
|
146
|
+
if (*path_end)
|
|
147
|
+
offset = path_end - tmp;
|
|
148
|
+
else {
|
|
149
|
+
fprintf(stderr, "Invalid path: %s\n", path);
|
|
150
|
+
free(tmp);
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// starts with *:
|
|
155
|
+
else if (tmp[1] == ':')
|
|
156
|
+
offset = 2;
|
|
157
|
+
}
|
|
158
|
+
#else
|
|
159
|
+
offset = 1;
|
|
160
|
+
#endif
|
|
161
|
+
|
|
162
|
+
// TO DO: first find the longest subdir that exists, in *reverse* order so as
|
|
163
|
+
// to properly handle case where no access to intermediate dir,
|
|
164
|
+
// and then only start mkdir from there
|
|
165
|
+
int last_dir_exists_rc = 0;
|
|
166
|
+
int last_errno = -1;
|
|
167
|
+
for (char *p = tmp + offset; /* !rc && */ *p; p++) {
|
|
168
|
+
if (strchr("/\\", *p)) {
|
|
169
|
+
char tmp_c = p[1];
|
|
170
|
+
p[0] = FILESLASH;
|
|
171
|
+
p[1] = '\0';
|
|
172
|
+
if (*tmp && !(last_dir_exists_rc = zsv_dir_exists(tmp))) {
|
|
173
|
+
if (zsv_mkdir(tmp
|
|
174
|
+
#ifndef WIN32
|
|
175
|
+
,
|
|
176
|
+
S_IRWXU
|
|
177
|
+
#endif
|
|
178
|
+
)) {
|
|
179
|
+
if (errno == EEXIST)
|
|
180
|
+
last_dir_exists_rc = 1;
|
|
181
|
+
else { // errno could be EEXIST if we have no permissions to an intermediate directory
|
|
182
|
+
last_errno = errno;
|
|
183
|
+
perror(tmp);
|
|
184
|
+
// rc = -1;
|
|
185
|
+
}
|
|
186
|
+
} else
|
|
187
|
+
last_dir_exists_rc = 1;
|
|
188
|
+
}
|
|
189
|
+
p[1] = tmp_c;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (/* !rc && */ path_is_filename == 0 && *tmp && !(last_dir_exists_rc = zsv_dir_exists(tmp))) {
|
|
194
|
+
if (zsv_mkdir(tmp
|
|
195
|
+
#ifndef WIN32
|
|
196
|
+
,
|
|
197
|
+
S_IRWXU
|
|
198
|
+
#endif
|
|
199
|
+
)) {
|
|
200
|
+
if (errno == EEXIST)
|
|
201
|
+
last_dir_exists_rc = 1;
|
|
202
|
+
else {
|
|
203
|
+
last_errno = errno;
|
|
204
|
+
perror(tmp);
|
|
205
|
+
// rc = -1;
|
|
206
|
+
}
|
|
207
|
+
} else
|
|
208
|
+
last_dir_exists_rc = 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
free(tmp);
|
|
212
|
+
return last_dir_exists_rc ? 0 : last_errno ? last_errno : -1;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
int main(int argc, char *argv[]) {
|
|
216
|
+
if (argc != 2) {
|
|
217
|
+
fprintf(stderr, "Usage: %s <absolute_directory_path>\n", argv[0]);
|
|
218
|
+
fprintf(stderr, "Example: %s \"C:\\Temp\\My Very Long Directory Name That Exceeds 260 Characters\\Subfolder\"\n",
|
|
219
|
+
argv[0]);
|
|
220
|
+
fprintf(stderr, " %s \"\\\\?\\C:\\Temp\\Another Long Path\\Subfolder\"\n", argv[0]);
|
|
221
|
+
fprintf(stderr, "Note: Provide an ABSOLUTE path.\n");
|
|
222
|
+
return 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const char *targetPath = argv[1];
|
|
226
|
+
|
|
227
|
+
printf("Attempting to create directory: %s\n", targetPath);
|
|
228
|
+
|
|
229
|
+
DWORD result = zsv_mkdirs(targetPath, 0);
|
|
230
|
+
|
|
231
|
+
if (result == 0) {
|
|
232
|
+
printf("Success: Directory created or already exists.\n");
|
|
233
|
+
return 0;
|
|
234
|
+
} else {
|
|
235
|
+
// You can provide more detailed error messages by checking specific Win32 error codes
|
|
236
|
+
// For example: if (result == ERROR_PATH_NOT_FOUND) { ... }
|
|
237
|
+
fprintf(stderr, "Error: Failed to create directory %s. Win32 Error Code: %lu\n", targetPath, result);
|
|
238
|
+
|
|
239
|
+
// Optionally print the system error message for the code
|
|
240
|
+
LPSTR messageBuffer = NULL;
|
|
241
|
+
size_t size =
|
|
242
|
+
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
|
|
243
|
+
result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
|
244
|
+
if (messageBuffer) {
|
|
245
|
+
fprintf(stderr, "System Message: %s\n", messageBuffer);
|
|
246
|
+
LocalFree(messageBuffer); // Free buffer allocated by FormatMessage
|
|
247
|
+
} else {
|
|
248
|
+
fprintf(stderr, "Could not format error message for code %lu.\n", result);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return 1; // Indicate failure
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#endif // DIRS_MKDIR_TEST
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
2
|
+
#include <windows.h>
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <stdlib.h> // Required for malloc, free
|
|
5
|
+
#include <string.h>
|
|
6
|
+
#include <wchar.h> // Required for wide character functions
|
|
7
|
+
|
|
8
|
+
#include "io.h"
|
|
9
|
+
#include <errno.h>
|
|
10
|
+
|
|
11
|
+
// Function to map common Windows error codes to POSIX errno values
|
|
12
|
+
// return 0 if could not map
|
|
13
|
+
int windows_error_to_errno(DWORD windows_error_code) {
|
|
14
|
+
switch (windows_error_code) {
|
|
15
|
+
case ERROR_FILE_NOT_FOUND:
|
|
16
|
+
return ENOENT;
|
|
17
|
+
case ERROR_PATH_NOT_FOUND:
|
|
18
|
+
return ENOENT;
|
|
19
|
+
case ERROR_INVALID_DRIVE:
|
|
20
|
+
return ENODEV; // Or ENOENT
|
|
21
|
+
|
|
22
|
+
case ERROR_ACCESS_DENIED:
|
|
23
|
+
return EACCES;
|
|
24
|
+
case ERROR_INVALID_ACCESS:
|
|
25
|
+
return EACCES; // Or EPERM depending on context
|
|
26
|
+
case ERROR_SHARING_VIOLATION:
|
|
27
|
+
return EACCES; // File is locked/in use
|
|
28
|
+
case ERROR_INVALID_HANDLE:
|
|
29
|
+
return EBADF;
|
|
30
|
+
case ERROR_INVALID_DATA:
|
|
31
|
+
return EILSEQ; // Or EINVAL
|
|
32
|
+
case ERROR_INVALID_PARAMETER:
|
|
33
|
+
return EINVAL;
|
|
34
|
+
case ERROR_NEGATIVE_SEEK:
|
|
35
|
+
return EINVAL;
|
|
36
|
+
case ERROR_WRITE_PROTECT:
|
|
37
|
+
return EROFS; // Read-only filesystem or media
|
|
38
|
+
case ERROR_DISK_FULL:
|
|
39
|
+
return ENOSPC; // No space left on device
|
|
40
|
+
case ERROR_ALREADY_EXISTS:
|
|
41
|
+
return EEXIST;
|
|
42
|
+
case ERROR_FILE_EXISTS:
|
|
43
|
+
return EEXIST;
|
|
44
|
+
case ERROR_TOO_MANY_OPEN_FILES:
|
|
45
|
+
return EMFILE;
|
|
46
|
+
case ERROR_DIRECTORY:
|
|
47
|
+
return ENOTDIR; // Attempted file op on directory (approx)
|
|
48
|
+
case ERROR_BROKEN_PIPE:
|
|
49
|
+
return EPIPE;
|
|
50
|
+
case ERROR_PIPE_NOT_CONNECTED:
|
|
51
|
+
return EPIPE;
|
|
52
|
+
case ERROR_WAIT_NO_CHILDREN:
|
|
53
|
+
return ECHILD; // Wait functions
|
|
54
|
+
case ERROR_CHILD_NOT_COMPLETE:
|
|
55
|
+
return ECHILD; // Wait functions
|
|
56
|
+
case ERROR_SUCCESS:
|
|
57
|
+
return 0; // Not really an error
|
|
58
|
+
default:
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
int zsv_remove_winlp(const char *path_utf8) {
|
|
64
|
+
if (path_utf8 == NULL || path_utf8[0] == '\0') {
|
|
65
|
+
return 0; // Invalid or empty path
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
wchar_t *path_to_use = NULL;
|
|
69
|
+
DWORD rc = zsv_pathToPrefixedWidePath(path_utf8, &path_to_use);
|
|
70
|
+
if (!rc) {
|
|
71
|
+
if (!DeleteFileW(path_to_use)) {
|
|
72
|
+
DWORD lastError = GetLastError();
|
|
73
|
+
#ifndef NDEBUG
|
|
74
|
+
fprintf(stderr, "Error deleting file '%ls': %lu\n", path_to_use, lastError);
|
|
75
|
+
#endif
|
|
76
|
+
if (windows_error_to_errno(lastError)) {
|
|
77
|
+
rc = windows_error_to_errno(lastError);
|
|
78
|
+
errno = rc;
|
|
79
|
+
} else {
|
|
80
|
+
fprintf(stderr, "Unable to delete file '%ls': %lu\n", path_to_use, GetLastError());
|
|
81
|
+
LPSTR messageBuffer = NULL;
|
|
82
|
+
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
83
|
+
NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
|
84
|
+
if (messageBuffer) {
|
|
85
|
+
fprintf(stderr, "Error message: %s\n", messageBuffer);
|
|
86
|
+
LocalFree(messageBuffer);
|
|
87
|
+
} else
|
|
88
|
+
fprintf(stderr, "Could not format error message for code %lu.\n", lastError);
|
|
89
|
+
errno = rc = -1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
free(path_to_use);
|
|
95
|
+
return rc;
|
|
96
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
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 <zsv/utils/writer.h>
|
|
10
|
+
#include <zsv/utils/compiler.h>
|
|
11
|
+
#include <zsv/utils/os.h>
|
|
12
|
+
#include <stdio.h>
|
|
13
|
+
#include <ctype.h>
|
|
14
|
+
#include <string.h>
|
|
15
|
+
#include <stdlib.h>
|
|
16
|
+
#include <errno.h>
|
|
17
|
+
|
|
18
|
+
// clang-format off
|
|
19
|
+
|
|
20
|
+
// return 0 on eof, +1 on error, > 0 if valid utf8 first byte read
|
|
21
|
+
static inline char UTF8_charLenC_noerr(int c) {
|
|
22
|
+
char len;
|
|
23
|
+
if (c == EOF) len = 0;
|
|
24
|
+
else if (!(c & 128)) len = 1;
|
|
25
|
+
else if ((c & 224) == 192) len = 2; // 110xxxxx
|
|
26
|
+
else if ((c & 240) == 224) len = 3; // 1110xxxx
|
|
27
|
+
else if ((c & 248) == 240) len = 4; // 11110xxx
|
|
28
|
+
else if ((c & 252) == 248) len = 5; // 111110xx
|
|
29
|
+
else if ((c & 254) == 252) len = 6; // 1111110x
|
|
30
|
+
else len = 1; // error, but we are going to ignore and just call it a 1-byte char
|
|
31
|
+
return len;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// clang-format on
|
|
35
|
+
|
|
36
|
+
static struct zsv_csv_writer_options zsv_csv_writer_default_opts = {0};
|
|
37
|
+
static char zsv_writer_default_opts_initd = 0;
|
|
38
|
+
void zsv_writer_set_default_opts(struct zsv_csv_writer_options opts) {
|
|
39
|
+
zsv_writer_default_opts_initd = 1;
|
|
40
|
+
zsv_csv_writer_default_opts = opts;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
struct zsv_csv_writer_options zsv_writer_get_default_opts(void) {
|
|
44
|
+
if (!zsv_writer_default_opts_initd) {
|
|
45
|
+
zsv_writer_default_opts_initd = 1;
|
|
46
|
+
zsv_csv_writer_default_opts.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
|
|
47
|
+
zsv_csv_writer_default_opts.stream = stdout;
|
|
48
|
+
}
|
|
49
|
+
return zsv_csv_writer_default_opts;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// zsv_csv_quote() returns:
|
|
53
|
+
// - NULL if no quoting needed
|
|
54
|
+
// - buff if buff size was large enough to hold result
|
|
55
|
+
// - newly-allocated char * if buff not large enough, and was able to get from heap
|
|
56
|
+
// in last case, caller must free
|
|
57
|
+
unsigned char *zsv_csv_quote(const unsigned char *utf8_value, size_t len, unsigned char *buff, size_t buffsize) {
|
|
58
|
+
char need = 0;
|
|
59
|
+
unsigned quotes = 0;
|
|
60
|
+
for (unsigned int i = 0; i < len; i++) {
|
|
61
|
+
switch (utf8_value[i]) {
|
|
62
|
+
case ',':
|
|
63
|
+
case '\n':
|
|
64
|
+
case '\r':
|
|
65
|
+
need = 1;
|
|
66
|
+
break;
|
|
67
|
+
case '"':
|
|
68
|
+
need = 1;
|
|
69
|
+
quotes++;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!need)
|
|
74
|
+
return NULL;
|
|
75
|
+
|
|
76
|
+
unsigned char *target;
|
|
77
|
+
unsigned mem_length = len + quotes + 3; // str + 2 quotes + terminating null
|
|
78
|
+
if (mem_length < buffsize)
|
|
79
|
+
target = buff;
|
|
80
|
+
else
|
|
81
|
+
target = malloc(mem_length * sizeof(*target));
|
|
82
|
+
|
|
83
|
+
if (target) {
|
|
84
|
+
*target = '"';
|
|
85
|
+
if (!quotes)
|
|
86
|
+
memcpy(target + 1, utf8_value, len);
|
|
87
|
+
else {
|
|
88
|
+
for (unsigned int i = 0, j = 1; i < len; i++) {
|
|
89
|
+
if ((target[j++] = utf8_value[i]) == '"')
|
|
90
|
+
target[j++] = '"';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
target[mem_length - 2] = '"';
|
|
94
|
+
target[mem_length - 1] = '\0';
|
|
95
|
+
}
|
|
96
|
+
return target;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// to do: test #define ZSV_OUTPUT_BUFF_SIZE 65536 * 4 * 8
|
|
100
|
+
#define ZSV_OUTPUT_BUFF_SIZE 65536 * 4
|
|
101
|
+
|
|
102
|
+
struct zsv_output_buff {
|
|
103
|
+
char *buff; // will be ZSV_OUTPUT_BUFF_SIZE. to do: option to modify buff size
|
|
104
|
+
size_t (*write)(const void *restrict, size_t size, size_t nitems, void *restrict stream);
|
|
105
|
+
void *stream;
|
|
106
|
+
size_t used;
|
|
107
|
+
uint64_t written;
|
|
108
|
+
unsigned char close_on_delete : 1;
|
|
109
|
+
unsigned char _ : 7;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
struct zsv_writer_data {
|
|
113
|
+
size_t buffsize; // corresponds to buf
|
|
114
|
+
unsigned char *buff; // option
|
|
115
|
+
|
|
116
|
+
struct zsv_output_buff out;
|
|
117
|
+
|
|
118
|
+
void (*table_init)(void *);
|
|
119
|
+
void *table_init_ctx;
|
|
120
|
+
|
|
121
|
+
const char *cell_prepend;
|
|
122
|
+
|
|
123
|
+
void (*on_row)(void *);
|
|
124
|
+
void *on_row_ctx;
|
|
125
|
+
|
|
126
|
+
void (*on_delete)(void *);
|
|
127
|
+
void *on_delete_ctx;
|
|
128
|
+
|
|
129
|
+
unsigned char with_bom : 1;
|
|
130
|
+
unsigned char started : 1;
|
|
131
|
+
unsigned char _ : 6;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
#include <unistd.h> // write
|
|
135
|
+
|
|
136
|
+
static inline void zsv_output_buff_flush(struct zsv_output_buff *b) {
|
|
137
|
+
b->write(b->buff, b->used, 1, b->stream);
|
|
138
|
+
b->written += b->used;
|
|
139
|
+
b->used = 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static inline void zsv_output_buff_write(struct zsv_output_buff *b, const unsigned char *s, size_t n) {
|
|
143
|
+
if (n) {
|
|
144
|
+
if (n + b->used > ZSV_OUTPUT_BUFF_SIZE) {
|
|
145
|
+
zsv_output_buff_flush(b);
|
|
146
|
+
if (n > ZSV_OUTPUT_BUFF_SIZE) { // n too big, so write directly
|
|
147
|
+
b->write(s, n, 1, b->stream);
|
|
148
|
+
b->written += n;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// n + used < buff size
|
|
153
|
+
memcpy(b->buff + b->used, s, n);
|
|
154
|
+
b->used += n;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
void zsv_writer_set_temp_buff(zsv_csv_writer w, unsigned char *buff, size_t buffsize) {
|
|
159
|
+
w->buff = buff;
|
|
160
|
+
w->buffsize = buffsize;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static int writer_opts_ok(struct zsv_csv_writer_options *opts) {
|
|
164
|
+
if (opts) {
|
|
165
|
+
if (opts->output_path) {
|
|
166
|
+
if ((opts->write && opts->write != (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite) ||
|
|
167
|
+
(opts->stream && opts->stream != stdout)) {
|
|
168
|
+
fprintf(stderr, "Invalid zsv writer options: non-NULL 'output_path' with invalid 'write' and/or 'stream'\n");
|
|
169
|
+
errno = EINVAL;
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
zsv_csv_writer zsv_writer_new(struct zsv_csv_writer_options *opts) {
|
|
178
|
+
if (!(writer_opts_ok(opts)))
|
|
179
|
+
return NULL;
|
|
180
|
+
struct zsv_writer_data *w = calloc(1, sizeof(*w));
|
|
181
|
+
if (w) {
|
|
182
|
+
if (!(w->out.buff = malloc(ZSV_OUTPUT_BUFF_SIZE)))
|
|
183
|
+
goto zsv_writer_new_err;
|
|
184
|
+
|
|
185
|
+
if (!opts) {
|
|
186
|
+
w->out.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
|
|
187
|
+
w->out.stream = stdout;
|
|
188
|
+
} else {
|
|
189
|
+
if (opts->output_path) {
|
|
190
|
+
if (!(w->out.stream = zsv_fopen(opts->output_path, "wb"))) {
|
|
191
|
+
perror(opts->output_path);
|
|
192
|
+
goto zsv_writer_new_err;
|
|
193
|
+
}
|
|
194
|
+
w->out.close_on_delete = 1;
|
|
195
|
+
w->out.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
|
|
196
|
+
} else if (opts->write) {
|
|
197
|
+
w->out.write = opts->write;
|
|
198
|
+
w->out.stream = opts->stream;
|
|
199
|
+
} else {
|
|
200
|
+
w->out.write = (size_t(*)(const void *restrict, size_t, size_t, void *restrict))fwrite;
|
|
201
|
+
w->out.stream = opts->stream ? opts->stream : stdout;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
w->with_bom = opts->with_bom;
|
|
205
|
+
w->table_init = opts->table_init;
|
|
206
|
+
w->table_init_ctx = opts->table_init_ctx;
|
|
207
|
+
w->on_row = opts->on_row;
|
|
208
|
+
w->on_row_ctx = opts->on_row_ctx;
|
|
209
|
+
w->on_delete = opts->on_delete;
|
|
210
|
+
w->on_delete_ctx = opts->on_delete_ctx;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return w;
|
|
214
|
+
|
|
215
|
+
int e;
|
|
216
|
+
zsv_writer_new_err:
|
|
217
|
+
e = errno;
|
|
218
|
+
zsv_writer_delete(w);
|
|
219
|
+
errno = e;
|
|
220
|
+
return NULL;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
enum zsv_writer_status zsv_writer_flush(zsv_csv_writer w) {
|
|
224
|
+
if (!w)
|
|
225
|
+
return zsv_writer_status_missing_handle;
|
|
226
|
+
|
|
227
|
+
zsv_output_buff_flush(&w->out);
|
|
228
|
+
return zsv_writer_status_ok;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
enum zsv_writer_status zsv_writer_delete(zsv_csv_writer w) {
|
|
232
|
+
if (!w)
|
|
233
|
+
return zsv_writer_status_missing_handle;
|
|
234
|
+
|
|
235
|
+
if (w->started)
|
|
236
|
+
zsv_output_buff_write(&w->out, (const unsigned char *)"\n", 1);
|
|
237
|
+
|
|
238
|
+
if (w->out.stream && w->out.write && w->out.buff)
|
|
239
|
+
zsv_output_buff_flush(&w->out);
|
|
240
|
+
|
|
241
|
+
if (w->on_delete)
|
|
242
|
+
w->on_delete(w->on_delete_ctx);
|
|
243
|
+
|
|
244
|
+
if (w->out.buff)
|
|
245
|
+
free(w->out.buff);
|
|
246
|
+
|
|
247
|
+
if (w->out.close_on_delete && w->out.stream)
|
|
248
|
+
fclose(w->out.stream);
|
|
249
|
+
|
|
250
|
+
free(w);
|
|
251
|
+
return zsv_writer_status_ok;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static inline enum zsv_writer_status zsv_writer_cell_aux(zsv_csv_writer w, const unsigned char *s, size_t len,
|
|
255
|
+
char check_if_needs_quoting) {
|
|
256
|
+
if (len) {
|
|
257
|
+
if (check_if_needs_quoting) {
|
|
258
|
+
unsigned char *quoted_s = zsv_csv_quote(s, len, w->buff, w->buffsize);
|
|
259
|
+
if (!quoted_s)
|
|
260
|
+
zsv_output_buff_write(&w->out, s, len);
|
|
261
|
+
else {
|
|
262
|
+
zsv_output_buff_write(&w->out, quoted_s, strlen((char *)quoted_s));
|
|
263
|
+
if (!(w->buff && quoted_s == w->buff))
|
|
264
|
+
free(quoted_s);
|
|
265
|
+
}
|
|
266
|
+
} else
|
|
267
|
+
zsv_output_buff_write(&w->out, s, len);
|
|
268
|
+
}
|
|
269
|
+
return zsv_writer_status_ok;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
uint64_t zsv_writer_cum_bytes_written(zsv_csv_writer w) {
|
|
273
|
+
return (uint64_t)(w->out.used + w->out.written);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
enum zsv_writer_status zsv_writer_cell(zsv_csv_writer w, char new_row, const unsigned char *s, size_t len,
|
|
277
|
+
char check_if_needs_quoting) {
|
|
278
|
+
if (!w)
|
|
279
|
+
return zsv_writer_status_missing_handle;
|
|
280
|
+
if (!w->started) {
|
|
281
|
+
if (w->table_init)
|
|
282
|
+
w->table_init(w->table_init_ctx);
|
|
283
|
+
if (w->with_bom)
|
|
284
|
+
zsv_output_buff_write(&w->out, (const unsigned char *)"\xef\xbb\xbf", 3);
|
|
285
|
+
w->started = 1;
|
|
286
|
+
} else if (new_row) {
|
|
287
|
+
if (VERY_UNLIKELY(w->on_row != NULL))
|
|
288
|
+
w->on_row(w->on_row_ctx);
|
|
289
|
+
zsv_output_buff_write(&w->out, (const unsigned char *)"\n", 1);
|
|
290
|
+
} else
|
|
291
|
+
zsv_output_buff_write(&w->out, (const unsigned char *)",", 1);
|
|
292
|
+
|
|
293
|
+
if (VERY_UNLIKELY(w->cell_prepend && *w->cell_prepend)) {
|
|
294
|
+
char *tmp = NULL;
|
|
295
|
+
asprintf(&tmp, "%s%.*s", w->cell_prepend, (int)len, s ? s : (const unsigned char *)"");
|
|
296
|
+
if (!tmp) {
|
|
297
|
+
perror(NULL);
|
|
298
|
+
return zsv_writer_status_error;
|
|
299
|
+
}
|
|
300
|
+
s = (const unsigned char *)tmp;
|
|
301
|
+
len = len + strlen(w->cell_prepend);
|
|
302
|
+
enum zsv_writer_status stat = zsv_writer_cell_aux(w, s, len, 1);
|
|
303
|
+
free(tmp);
|
|
304
|
+
return stat;
|
|
305
|
+
}
|
|
306
|
+
return zsv_writer_cell_aux(w, s, len, check_if_needs_quoting);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
void zsv_writer_cell_prepend(zsv_csv_writer w, const unsigned char *s) {
|
|
310
|
+
w->cell_prepend = (const char *)s;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
enum zsv_writer_status zsv_writer_cell_Lf(zsv_csv_writer w, char new_row, const char *fmt_spec, long double ldbl) {
|
|
314
|
+
char s[128];
|
|
315
|
+
char fmt[64];
|
|
316
|
+
int n = snprintf(fmt, sizeof(fmt), "%%%sLf", fmt_spec ? fmt_spec : "");
|
|
317
|
+
if (!(n > 0 && n < (int)sizeof(fmt)))
|
|
318
|
+
fprintf(stderr, "Invalid format specifier, should be X for format %%XLf e.g. '.2'\n");
|
|
319
|
+
else {
|
|
320
|
+
n = snprintf(s, sizeof(s), fmt, ldbl);
|
|
321
|
+
if (!(n > 0 && n < (int)sizeof(fmt)))
|
|
322
|
+
fprintf(stderr, "Unable to format value with fmt %s: %Lf\n", fmt, ldbl);
|
|
323
|
+
else
|
|
324
|
+
return zsv_writer_cell(w, new_row, (unsigned char *)s, n, 0);
|
|
325
|
+
}
|
|
326
|
+
zsv_writer_cell(w, new_row, NULL, 0, 0);
|
|
327
|
+
return zsv_writer_status_error;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
enum zsv_writer_status zsv_writer_cell_blank(zsv_csv_writer w, char new_row) {
|
|
331
|
+
return zsv_writer_cell(w, new_row, (const unsigned char *)"", 0, 0);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
enum zsv_writer_status zsv_writer_cell_zu(zsv_csv_writer w, char new_row, size_t zu) {
|
|
335
|
+
char s[64];
|
|
336
|
+
int n = snprintf(s, sizeof(s), "%zu", zu);
|
|
337
|
+
if (n < 1 || n >= (int)sizeof(s))
|
|
338
|
+
n = 0; // unexpected overflow
|
|
339
|
+
return zsv_writer_cell(w, new_row, (unsigned char *)s, n, 0);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
enum zsv_writer_status zsv_writer_cell_s(zsv_csv_writer w, char new_row, const unsigned char *s,
|
|
343
|
+
char check_if_needs_quoting) {
|
|
344
|
+
return zsv_writer_cell(w, new_row, s, s ? strlen((const char *)s) : 0, check_if_needs_quoting);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/*
|
|
348
|
+
* returns: newly allocated value (caller must free) or NULL
|
|
349
|
+
*/
|
|
350
|
+
unsigned char *zsv_writer_str_to_csv(const unsigned char *s, size_t len) {
|
|
351
|
+
if (len) {
|
|
352
|
+
unsigned char *csv_s = zsv_csv_quote(s, len, NULL, 0);
|
|
353
|
+
if (csv_s)
|
|
354
|
+
return csv_s;
|
|
355
|
+
csv_s = malloc(len + 1);
|
|
356
|
+
memcpy(csv_s, s, len);
|
|
357
|
+
csv_s[len] = '\0';
|
|
358
|
+
return csv_s;
|
|
359
|
+
}
|
|
360
|
+
return NULL;
|
|
361
|
+
}
|