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