changeset 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 6ac59489c815
children 709d85ff7a0b
files Changelog mencoder.c mpcommon.c mpcommon.h mplayer.c stream/stream_file.c
diffstat 6 files changed, 111 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/Changelog	Thu Mar 14 19:59:35 2013 +0000
+++ b/Changelog	Thu Mar 14 20:04:24 2013 +0000
@@ -15,6 +15,10 @@
     * Support teletext and CC subtitles in WTV.
     * Support binding keys corresponding to non-ASCII characters.
 
+    Ports:
+    * Windows: support file names as UTF-8 in slave mode and passing
+      file names as wchar command line arguments.
+
   1.1: "We gave up on 1.0"
 
     Decoders:
--- a/mencoder.c	Thu Mar 14 19:59:35 2013 +0000
+++ b/mencoder.c	Thu Mar 14 20:04:24 2013 +0000
@@ -578,7 +578,7 @@
 
 user_correct_pts = 0;
 
-  common_preinit();
+  common_preinit(&argc, &argv);
 
   // Create the config context and register the options
   mconfig = m_config_new();
--- 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());
--- a/mpcommon.h	Thu Mar 14 19:59:35 2013 +0000
+++ b/mpcommon.h	Thu Mar 14 20:04:24 2013 +0000
@@ -80,7 +80,7 @@
 int cfg_inc_verbose(m_option_t *conf);
 int cfg_include(m_option_t *conf, const char *filename);
 
-void common_preinit(void);
+void common_preinit(int *argc_ptr, char **argv_ptr[]);
 int common_init(void);
 
 double calc_a_pts(struct sh_audio *sh_audio, demux_stream_t *d_audio);
--- a/mplayer.c	Thu Mar 14 19:59:35 2013 +0000
+++ b/mplayer.c	Thu Mar 14 20:04:24 2013 +0000
@@ -2769,7 +2769,7 @@
     int profile_config_loaded;
     int i;
 
-    common_preinit();
+    common_preinit(&argc, &argv);
 
     // Create the config context and register the options
     mconfig = m_config_new();
--- a/stream/stream_file.c	Thu Mar 14 19:59:35 2013 +0000
+++ b/stream/stream_file.c	Thu Mar 14 20:04:24 2013 +0000
@@ -26,6 +26,10 @@
 #if HAVE_SETMODE
 #include <io.h>
 #endif
+#ifdef __MINGW32__
+#include <windows.h>
+#include <share.h>
+#endif
 
 #include "mp_msg.h"
 #include "stream.h"
@@ -114,6 +118,28 @@
   return STREAM_UNSUPPORTED;
 }
 
+#ifdef __MINGW32__
+static int win32_open(const char *fname, int m, int omode)
+{
+    int cnt;
+    int fd = -1;
+    wchar_t fname_w[MAX_PATH];
+    int WINAPI (*mb2wc)(UINT, DWORD, LPCSTR, int, LPWSTR, int) = NULL;
+    HMODULE kernel32 = GetModuleHandle("Kernel32.dll");
+    if (!kernel32) goto fallback;
+    mb2wc = GetProcAddress(kernel32, "MultiByteToWideChar");
+    if (!mb2wc) goto fallback;
+    cnt = mb2wc(CP_UTF8, MB_ERR_INVALID_CHARS, fname, -1, fname_w, sizeof(fname_w) / sizeof(*fname_w));
+    if (cnt <= 0) goto fallback;
+    fd = _wsopen(fname_w, m, SH_DENYNO, omode);
+    if (fd != -1 || (m & O_CREAT))
+        return fd;
+
+fallback:
+    return _sopen(fname, m, SH_DENYNO, omode);
+}
+#endif
+
 static int open_f(stream_t *stream,int mode, void* opts, int* file_format) {
   int f;
   mode_t m = 0;
@@ -168,10 +194,12 @@
     }
   } else {
       mode_t openmode = S_IRUSR|S_IWUSR;
-#ifndef __MINGW32__
+#ifdef __MINGW32__
+      f = win32_open(filename, m, openmode);
+#else
       openmode |= S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+      f=open(filename,m, openmode);
 #endif
-      f=open(filename,m, openmode);
     if(f<0) {
       mp_msg(MSGT_OPEN,MSGL_ERR,MSGTR_FileNotFound,filename);
       m_struct_free(&stream_opts,opts);