diff mpcommon.c @ 35868:3edaed3c1d60

Win32: support file names with characters outside system code-page This is done by trying to interpret the file name as being UTF-8 encoded first. The command-line is fetched as UTF-16 and converted to UTF-8 to match with this.
author reimar
date Thu, 14 Mar 2013 20:04:24 +0000
parents 9ca9775defff
children 8587ae275646
line wrap: on
line diff
--- a/mpcommon.c	Thu Mar 14 19:59:35 2013 +0000
+++ b/mpcommon.c	Thu Mar 14 20:04:24 2013 +0000
@@ -456,6 +456,73 @@
     {NULL, NULL, 0, 0, 0, 0, NULL}
 };
 
+#ifdef __MINGW32__
+static int get_win32_cmdline(int *argc_ptr, char **argv_ptr[])
+{
+    int i;
+    int argv_size, size;
+    int argc_n;
+    char **argv_n;
+    LPWSTR *argv_w = NULL;
+    void *buffer = NULL;
+    char *strs, *strs_end;
+
+    HMODULE kernel32 = GetModuleHandle("Kernel32.dll");
+    HMODULE shell32  = GetModuleHandle("shell32.dll");
+    int WINAPI (*wc2mb)(UINT, DWORD, LPCWSTR, int, LPSTR, int, LPCSTR, LPBOOL) = NULL;
+    LPCWSTR WINAPI (*getCmdlW)(void) = NULL;
+    LPWSTR * WINAPI (*cmdl2argv)(LPCWSTR, int *) = NULL;
+
+    if (!kernel32 || !shell32)
+        goto err_out;
+    wc2mb = GetProcAddress(kernel32, "WideCharToMultiByte");
+    getCmdlW = GetProcAddress(kernel32, "GetCommandLineW");
+    cmdl2argv = GetProcAddress(shell32, "CommandLineToArgvW");
+    if (!wc2mb || !getCmdlW || !cmdl2argv)
+        goto err_out;
+
+    argv_w = cmdl2argv(getCmdlW(), &argc_n);
+    if (!argv_w || argc_n < 0 || argc_n >= INT_MAX / sizeof(char *))
+        goto err_out;
+
+    size = argv_size = (argc_n + 1) * sizeof(char *);
+    for (i = 0; i < argc_n; i++) {
+        int conv_size = wc2mb(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL);
+        if (conv_size < 0 || conv_size > INT_MAX - size)
+            goto err_out;
+        size += conv_size;
+    }
+
+    buffer = calloc(1, size);
+    if (!buffer)
+        goto err_out;
+    argv_n = buffer;
+    strs_end = strs = buffer;
+    strs += argv_size;
+    strs_end += size;
+
+    for (i = 0; i < argc_n; i++) {
+        int conv_size = wc2mb(CP_UTF8, 0, argv_w[i], -1,
+                              strs, strs_end - strs, NULL, NULL);
+        if (conv_size < 0 || conv_size > strs_end - strs)
+            goto err_out;
+        argv_n[i] = strs;
+        strs += conv_size;
+    }
+    argv_n[i] = NULL;
+
+    *argc_ptr = argc_n;
+    *argv_ptr = argv_n;
+    LocalFree(argv_w);
+    return 0;
+
+err_out:
+    free(buffer);
+    LocalFree(argv_w);
+    return -1;
+}
+#endif
+
 /**
  * Code to fix any kind of insane defaults some OS might have.
  * Currently mostly fixes for insecure-by-default Windows.
@@ -483,8 +550,14 @@
  * Initialization code to be run at the very start, must not depend
  * on option values.
  */
-void common_preinit(void)
+void common_preinit(int *argc_ptr, char **argv_ptr[])
 {
+#ifdef __MINGW32__
+    get_win32_cmdline(argc_ptr, argv_ptr);
+#else
+    (void)argc_ptr;
+    (void)argv_ptr;
+#endif
     sanitize_os();
     InitTimer();
     srand(GetTimerMS());