Skip to content

Commit e475fa2

Browse files
authored
mtmd, arg: fix utf8 handling on windows (ggml-org#24779)
* mtmd, arg: fix utf8 handling on windows * also fix ggml_fopen * fix build fail * also fix CLI
1 parent 175147e commit e475fa2

9 files changed

Lines changed: 106 additions & 13 deletions

File tree

common/arg.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# define NOMINMAX
1818
#endif
1919
#include <windows.h>
20+
#include <shellapi.h>
2021
#endif
2122

2223
#define JSON_ASSERT GGML_ASSERT
@@ -893,7 +894,44 @@ bool common_params_to_map(int argc, char ** argv, llama_example ex, std::map<com
893894
return true;
894895
}
895896

897+
#ifdef _WIN32
898+
struct utf8_argv {
899+
std::vector<std::string> buf;
900+
std::vector<char*> ptrs;
901+
};
902+
903+
static utf8_argv make_utf8_argv() {
904+
utf8_argv out;
905+
int wargc = 0;
906+
LPWSTR* wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
907+
if (!wargv) return out;
908+
909+
out.buf.reserve(wargc);
910+
for (int i = 0; i < wargc; ++i) {
911+
int n = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wargv[i], -1, nullptr, 0, nullptr, nullptr);
912+
if (n <= 0) { out.buf.emplace_back(); continue; }
913+
auto& s = out.buf.emplace_back();
914+
s.resize(static_cast<size_t>(n - 1));
915+
(void)WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, s.data(), n, nullptr, nullptr);
916+
}
917+
LocalFree(wargv);
918+
919+
out.ptrs.reserve(out.buf.size() + 1);
920+
for (auto& s : out.buf) out.ptrs.push_back(s.data());
921+
out.ptrs.push_back(nullptr);
922+
return out;
923+
}
924+
#endif
925+
896926
bool common_params_parse(int argc, char ** argv, common_params & params, llama_example ex, void(*print_usage)(int, char **)) {
927+
#ifdef _WIN32
928+
auto utf8 = make_utf8_argv();
929+
if (!utf8.ptrs.empty()) {
930+
argc = static_cast<int>(utf8.buf.size());
931+
argv = utf8.ptrs.data();
932+
}
933+
#endif
934+
897935
auto ctx_arg = common_params_parser_init(params, ex, print_usage);
898936
const common_params params_org = ctx_arg.params; // the example can modify the default params
899937

common/common.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,18 @@ std::vector<common_file_info> fs_list(const std::string & path, bool include_dir
10741074
return files;
10751075
}
10761076

1077+
std::ifstream fs_open_ifstream(const std::string & fname, std::ios_base::openmode mode) {
1078+
#ifdef _WIN32
1079+
int wlen = MultiByteToWideChar(CP_UTF8, 0, fname.c_str(), -1, NULL, 0);
1080+
if (!wlen) { return std::ifstream(); }
1081+
std::vector<wchar_t> wfname(wlen);
1082+
(void)MultiByteToWideChar(CP_UTF8, 0, fname.c_str(), -1, wfname.data(), wlen);
1083+
return std::ifstream(wfname.data(), mode);
1084+
#else
1085+
return std::ifstream(fname, mode);
1086+
#endif
1087+
}
1088+
10771089
//
10781090
// TTY utils
10791091
//

common/common.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,9 @@ struct common_file_info {
842842
};
843843
std::vector<common_file_info> fs_list(const std::string & path, bool include_directories);
844844

845+
// fs open, also handle UTF8 on Windows
846+
std::ifstream fs_open_ifstream(const std::string & fname, std::ios_base::openmode mode);
847+
845848
//
846849
// TTY utils
847850
//

ggml/src/ggml.c

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -600,18 +600,15 @@ FILE * ggml_fopen(const char * fname, const char * mode) {
600600
// convert fname (UTF-8)
601601
wchar_t * wfname = ggml_mbstowcs(fname);
602602
if (wfname) {
603-
// convert mode (ANSI)
604-
wchar_t * wmode = GGML_MALLOC((strlen(mode) + 1) * sizeof(wchar_t));
605-
wchar_t * wmode_p = wmode;
606-
do {
607-
*wmode_p++ = (wchar_t)*mode;
608-
} while (*mode++);
609-
610-
// open file
611-
file = _wfopen(wfname, wmode);
603+
// convert mode (UTF-8)
604+
wchar_t * wmode = ggml_mbstowcs(mode);
605+
if (wmode) {
606+
// open file
607+
file = _wfopen(wfname, wmode);
608+
GGML_FREE(wmode);
609+
}
612610

613611
GGML_FREE(wfname);
614-
GGML_FREE(wmode);
615612
}
616613

617614
return file;

tools/cli/cli.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ struct cli_context {
202202

203203
// TODO: support remote files in the future (http, https, etc)
204204
std::string load_input_file(const std::string & fname, bool is_media) {
205-
std::ifstream file(fname, std::ios::binary);
205+
std::ifstream file = fs_open_ifstream(fname, std::ios::binary);
206206
if (!file) {
207207
return "";
208208
}

tools/mtmd/clip-impl.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
#include <sstream>
1414
#include <vector>
1515
#include <memory>
16+
#include <fstream>
17+
18+
#ifdef _WIN32
19+
#ifndef NOMINMAX
20+
#define NOMINMAX
21+
#endif
22+
#include <windows.h>
23+
#endif
1624

1725
// Internal header for clip.cpp
1826

@@ -661,6 +669,22 @@ struct clip_image_f32_batch {
661669
// common utils
662670
//
663671

672+
#ifdef _WIN32
673+
static std::ifstream open_ifstream_binary(const std::string & fname) {
674+
int wlen = MultiByteToWideChar(CP_UTF8, 0, fname.c_str(), -1, NULL, 0);
675+
if (!wlen) {
676+
throw std::runtime_error("failed to convert filename to UTF-16: " + fname);
677+
}
678+
std::vector<wchar_t> wfname(wlen);
679+
(void)MultiByteToWideChar(CP_UTF8, 0, fname.c_str(), -1, wfname.data(), wlen);
680+
return std::ifstream(wfname.data(), std::ios::binary);
681+
}
682+
#else
683+
static std::ifstream open_ifstream_binary(const std::string & fname) {
684+
return std::ifstream(fname, std::ios::binary);
685+
}
686+
#endif
687+
664688
static std::string string_format(const char * fmt, ...) {
665689
va_list ap;
666690
va_list ap2;

tools/mtmd/clip.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,7 @@ struct clip_model_loader {
17521752
std::map<std::string, size_t> tensor_offset;
17531753
std::vector<ggml_tensor *> tensors_to_load;
17541754

1755-
auto fin = std::ifstream(fname, std::ios::binary);
1755+
auto fin = open_ifstream_binary(fname);
17561756
if (!fin) {
17571757
throw std::runtime_error(string_format("%s: failed to open %s\n", __func__, fname.c_str()));
17581758
}

tools/mtmd/mtmd-cli.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ int main(int argc, char ** argv) {
396396

397397
int n_predict = params.n_predict < 0 ? INT_MAX : params.n_predict;
398398

399+
console::init(params.simple_io, params.use_color);
400+
atexit([]() { console::cleanup(); });
401+
399402
// Ctrl+C handling
400403
{
401404
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))

tools/mtmd/mtmd-helper.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,13 +582,29 @@ mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_buf(mtmd_context * ctx,
582582
}
583583

584584
mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_file(mtmd_context * ctx, const char * fname, bool placeholder) {
585-
std::vector<unsigned char> buf;
585+
#ifdef _WIN32
586+
int wlen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
587+
if (!wlen) {
588+
LOG_ERR("Unable to convert filename to UTF-16: %s\n", fname);
589+
return {nullptr, nullptr};
590+
}
591+
std::vector<wchar_t> wfname(wlen);
592+
wlen = MultiByteToWideChar(CP_UTF8, 0, fname, -1, wfname.data(), wlen);
593+
if (!wlen) {
594+
LOG_ERR("Unable to convert filename to UTF-16: %s\n", fname);
595+
return {nullptr, nullptr};
596+
}
597+
FILE * f = _wfopen(wfname.data(), L"rb");
598+
#else
586599
FILE * f = fopen(fname, "rb");
600+
#endif
587601
if (!f) {
588602
LOG_ERR("Unable to open file %s: %s\n", fname, strerror(errno));
589603
return {nullptr, nullptr};
590604
}
591605

606+
std::vector<unsigned char> buf;
607+
592608
fseek(f, 0, SEEK_END);
593609
long file_size = ftell(f);
594610
fseek(f, 0, SEEK_SET);

0 commit comments

Comments
 (0)