fixes bug 326168 "Add a UTF-16 API to load a library" patch by jshin1987@gmail.com r=wtc,darin LIGHTNING_0_1_RELEASE
authordarin%meer.net
Fri, 10 Mar 2006 05:38:31 +0000
changeset 3609 924825f8a08b8f2e0d28547d9b6207ccc4b83bd2
parent 3607 130807664953f9f24e269395c3935c9c32ac1833
child 3611 016151ef344b273a59152e74ef91e42bf0a846f4
push idunknown
push userunknown
push dateunknown
reviewerswtc, darin
bugs326168
fixes bug 326168 "Add a UTF-16 API to load a library" patch by jshin1987@gmail.com r=wtc,darin
pr/include/md/_win95.h
pr/include/prlink.h
pr/include/prtypes.h
pr/src/linking/prlink.c
pr/src/md/windows/w95io.c
--- a/pr/include/md/_win95.h
+++ b/pr/include/md/_win95.h
@@ -268,18 +268,19 @@ extern PRInt32 _MD_CloseFile(PROsfd osfd
 #define _MD_DELETE                    _PR_MD_DELETE     
 #define _MD_MKDIR                     _PR_MD_MKDIR      
 #define _MD_MAKE_DIR                  _PR_MD_MAKE_DIR
 #define _MD_RMDIR                     _PR_MD_RMDIR      
 #define _MD_LOCKFILE                  _PR_MD_LOCKFILE
 #define _MD_TLOCKFILE                 _PR_MD_TLOCKFILE
 #define _MD_UNLOCKFILE                _PR_MD_UNLOCKFILE
 
+/* --- UTF16 IO stuff --- */
+extern PRBool _pr_useUnicode;
 #ifdef MOZ_UNICODE
-/* --- UTF16 IO stuff --- */
 #define _MD_OPEN_FILE_UTF16           _PR_MD_OPEN_FILE_UTF16
 #define _MD_OPEN_DIR_UTF16            _PR_MD_OPEN_DIR_UTF16
 #define _MD_READ_DIR_UTF16            _PR_MD_READ_DIR_UTF16
 #define _MD_CLOSE_DIR_UTF16           _PR_MD_CLOSE_DIR_UTF16
 #define _MD_GETFILEINFO64_UTF16       _PR_MD_GETFILEINFO64_UTF16
 #endif /* MOZ_UNICODE */
 
 /* --- Socket IO stuff --- */
--- a/pr/include/prlink.h
+++ b/pr/include/prlink.h
@@ -113,25 +113,26 @@ NSPR_API(PRLibrary*) PR_LoadLibrary(cons
 ** for a particular platform.
 **
 ** On some operating systems such as Mac OS, a shared library may
 ** contain code fragments that can be individually loaded.
 ** PRLibSpec also allows NSPR clients to identify a code fragment
 ** in a library, if code fragments are supported by the OS.
 ** A code fragment can be specified by name or by an integer index.
 **
-** Right now PRLibSpec supports three types of library specification:
-** a pathname, a Mac code fragment by name, and a Mac code fragment
-** by index.
+** Right now PRLibSpec supports four types of library specification:
+** a pathname in the native charset, a Mac code fragment by name,
+** a Mac code fragment by index, and a UTF-16 pathname.
 */
 
 typedef enum PRLibSpecType {
     PR_LibSpec_Pathname,
     PR_LibSpec_MacNamedFragment,   /* obsolete (for Mac OS Classic) */
-    PR_LibSpec_MacIndexedFragment  /* obsolete (for Mac OS Classic) */
+    PR_LibSpec_MacIndexedFragment, /* obsolete (for Mac OS Classic) */
+    PR_LibSpec_PathnameU           /* supported only on Win32 */ 
 } PRLibSpecType;
 
 struct FSSpec; /* Mac OS FSSpec */
 
 typedef struct PRLibSpec {
     PRLibSpecType type;
     union {
         /* if type is PR_LibSpec_Pathname */
@@ -143,29 +144,33 @@ typedef struct PRLibSpec {
             const char *name;
         } mac_named_fragment;      /* obsolete (for Mac OS Classic) */
 
         /* if type is PR_LibSpec_MacIndexedFragment */
         struct {
             const struct FSSpec *fsspec;
             PRUint32 index;
         } mac_indexed_fragment;    /* obsolete (for Mac OS Classic) */
+
+        /* if type is PR_LibSpec_PathnameU */
+        const PRUnichar *pathname_u; /* supported only on Win32 */
     } value;
 } PRLibSpec;
 
 /*
 ** The following bit flags may be or'd together and passed
 ** as the 'flags' argument to PR_LoadLibraryWithFlags.
 ** Flags not supported by the underlying OS are ignored.
 */
 
 #define PR_LD_LAZY   0x1  /* equivalent to RTLD_LAZY on Unix */
 #define PR_LD_NOW    0x2  /* equivalent to RTLD_NOW on Unix */
 #define PR_LD_GLOBAL 0x4  /* equivalent to RTLD_GLOBAL on Unix */
 #define PR_LD_LOCAL  0x8  /* equivalent to RTLD_LOCAL on Unix */
+/*                 0x400     reserved for NSPR internal use */
 
 /*
 ** Load the specified library, in the manner specified by 'flags'.
 */
 
 NSPR_API(PRLibrary *)
 PR_LoadLibraryWithFlags(
     PRLibSpec libSpec,    /* the shared library */
--- a/pr/include/prtypes.h
+++ b/pr/include/prtypes.h
@@ -469,29 +469,24 @@ typedef PRIntn PRBool;
 typedef PRUint8 PRPackedBool;
 
 /*
 ** Status code used by some routines that have a single point of failure or 
 ** special status return.
 */
 typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
 
-#ifdef MOZ_UNICODE
-/*
- * EXPERIMENTAL: This type may be removed in a future release.
- */
 #ifndef __PRUNICHAR__
 #define __PRUNICHAR__
-#if defined(WIN32) || defined(XP_MAC)
+#if (defined(__MWERKS__) || defined(_MSC_VER)) && defined(WIN32)
 typedef wchar_t PRUnichar;
 #else
 typedef PRUint16 PRUnichar;
 #endif
 #endif
-#endif /* MOZ_UNICODE */
 
 /*
 ** WARNING: The undocumented data types PRWord and PRUword are
 ** only used in the garbage collection and arena code.  Do not
 ** use PRWord and PRUword in new code.
 **
 ** A PRWord is an integer that is the same size as a void*.
 ** It implements the notion of a "word" in the Java Virtual
--- a/pr/src/linking/prlink.c
+++ b/pr/src/linking/prlink.c
@@ -152,16 +152,20 @@ struct _imcb *IAC$GL_IMAGE_LIST = NULL;
  * On these platforms, symbols have a leading '_'.
  */
 #if defined(SUNOS4) || defined(DARWIN) || defined(NEXTSTEP) \
     || defined(WIN16) || defined(XP_OS2) \
     || ((defined(OPENBSD) || defined(NETBSD)) && !defined(__ELF__))
 #define NEED_LEADING_UNDERSCORE
 #endif
 
+#ifdef WIN32
+#define PR_LD_PATHW  0x400  /* for PR_LibSpec_PathnameU */
+#endif
+
 /************************************************************************/
 
 struct PRLibrary {
     char*                       name;  /* Our own copy of the name string */
     PRLibrary*                  next;
     int                         refCount;
     const PRStaticLinkTable*    staticTable;
 
@@ -199,16 +203,26 @@ struct PRLibrary {
 
 static PRLibrary *pr_loadmap;
 static PRLibrary *pr_exe_loadmap;
 static PRMonitor *pr_linker_lock;
 static char* _pr_currentLibPath = NULL;
 
 static PRLibrary *pr_LoadLibraryByPathname(const char *name, PRIntn flags);
 
+#ifdef WIN95
+typedef HMODULE (WINAPI *LoadLibraryWFn)(LPCWSTR);
+static HMODULE WINAPI EmulateLoadLibraryW(LPCWSTR);
+static LoadLibraryWFn loadLibraryW = LoadLibraryW;
+#endif
+
+#ifdef WIN32
+static int pr_ConvertUTF16toUTF8(LPCWSTR wname, LPSTR name, int len);
+#endif
+
 /************************************************************************/
 
 #if !defined(USE_DLFCN) && !defined(HAVE_STRERROR)
 static char* errStrBuf = NULL;
 #define ERR_STR_BUF_LENGTH    20
 static char* errno_string(PRIntn oserr)
 {
     if (errStrBuf == NULL)
@@ -239,16 +253,22 @@ static void DLLErrorInternal(PRIntn oser
 
 void _PR_InitLinker(void)
 {
     PRLibrary *lm = NULL;
 #if defined(XP_UNIX)
     void *h;
 #endif
 
+#ifdef WIN95
+    if (!_pr_useUnicode) {
+        loadLibraryW = EmulateLoadLibraryW;
+    }
+#endif
+
     if (!pr_linker_lock) {
         pr_linker_lock = PR_NewNamedMonitor("linker-lock");
     }
     PR_EnterMonitor(pr_linker_lock);
 
 #if defined(XP_PC)
     lm = PR_NEWZAP(PRLibrary);
     lm->name = strdup("Executable");
@@ -551,16 +571,26 @@ PR_IMPLEMENT(PRLibrary*)
 PR_LoadLibraryWithFlags(PRLibSpec libSpec, PRIntn flags)
 {
     if (flags == 0) {
         flags = _PR_DEFAULT_LD_FLAGS;
     }
     switch (libSpec.type) {
         case PR_LibSpec_Pathname:
             return pr_LoadLibraryByPathname(libSpec.value.pathname, flags);
+#ifdef WIN32
+        case PR_LibSpec_PathnameU:
+            /*
+             * cast to |char *| and set PR_LD_PATHW flag so that
+             * it can be cast back to PRUnichar* in the callee.
+             */
+            return pr_LoadLibraryByPathname((const char*) 
+                                            libSpec.value.pathname_u, 
+                                            flags | PR_LD_PATHW);
+#endif
         default:
             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
             return NULL;
     }
 }
             
 PR_IMPLEMENT(PRLibrary*) 
 PR_LoadLibrary(const char *name)
@@ -747,33 +777,87 @@ pr_LoadViaDyld(const char *name, PRLibra
          * error information.
          */
     }
     return (lm->dlh != NULL || lm->image != NULL) ? PR_SUCCESS : PR_FAILURE;
 }
 
 #endif /* XP_MACOSX */
 
+#ifdef WIN95
+static HMODULE WINAPI
+EmulateLoadLibraryW(LPCWSTR lpLibFileName)
+{
+    HMODULE h;
+    char nameA[MAX_PATH];
+
+    if (!WideCharToMultiByte(CP_ACP, 0, lpLibFileName, -1,
+                             nameA, sizeof nameA, NULL, NULL)) {
+        return NULL;
+    }
+    /* Perhaps it's better to add a check for characters 
+     * not representable in CP_ACP.
+     */
+    h = LoadLibraryA(nameA);
+    return h;
+}
+#endif /* WIN95 */
+
 /*
 ** Dynamically load a library. Only load libraries once, so scan the load
 ** map first.
 */
 static PRLibrary*
 pr_LoadLibraryByPathname(const char *name, PRIntn flags)
 {
     PRLibrary *lm;
     PRLibrary* result;
     PRInt32 oserr;
+#ifdef WIN32
+    char utf8name_stack[MAX_PATH];
+    PRUnichar wname_stack[MAX_PATH];
+    char *utf8name = utf8name_stack;
+    PRUnichar *wname = wname_stack;
+    int len;
+#endif
 
     if (!_pr_initialized) _PR_ImplicitInitialization();
 
     /* See if library is already loaded */
     PR_EnterMonitor(pr_linker_lock);
 
+#ifdef WIN32
+    if (flags & PR_LD_PATHW) {
+        /* cast back what's cast to |char *| for the argument passing. */
+        wname = (LPWSTR) name;
+    } else {
+        int wlen = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
+        if (wlen > MAX_PATH)
+            wname = PR_Malloc(wlen);
+        if (wname == NULL ||
+            !MultiByteToWideChar(CP_ACP, 0,  name, -1, wname, wlen)) {
+            oserr = _MD_ERRNO();
+            goto unlock;
+        }
+    }
+    len = pr_ConvertUTF16toUTF8(wname, NULL, 0);
+    if (len > MAX_PATH)
+        utf8name = PR_Malloc(len);
+    if (utf8name == NULL ||
+        !pr_ConvertUTF16toUTF8(wname, utf8name, len)) {
+        oserr = _MD_ERRNO();
+        goto unlock;
+    }
+    /* the list of loaded library names are always kept in UTF-8 
+     * on Win32 platforms */
+    result = pr_UnlockedFindLibrary(utf8name);
+#else
     result = pr_UnlockedFindLibrary(name);
+#endif
+
     if (result != NULL) goto unlock;
 
     lm = PR_NEWZAP(PRLibrary);
     if (lm == NULL) {
         oserr = _MD_ERRNO();
         goto unlock;
     }
     lm->staticTable = NULL;
@@ -796,23 +880,41 @@ pr_LoadLibraryByPathname(const char *nam
           pr_loadmap = lm;
     }
 #endif /* XP_OS2 */
 
 #if defined(WIN32) || defined(WIN16)
     {
     HINSTANCE h;
 
+#ifdef WIN32
+#ifdef WIN95
+    if (flags & PR_LD_PATHW)
+        h = loadLibraryW(wname);
+    else
+        h = LoadLibraryA(name);
+#else
+    if (flags & PR_LD_PATHW)
+        h = LoadLibraryW(wname);
+    else
+        h = LoadLibraryA(name);
+#endif /* WIN95 */
+#else 
     h = LoadLibrary(name);
+#endif
     if (h < (HINSTANCE)HINSTANCE_ERROR) {
         oserr = _MD_ERRNO();
         PR_DELETE(lm);
         goto unlock;
     }
+#ifdef WIN32
+    lm->name = strdup(utf8name);
+#else
     lm->name = strdup(name);
+#endif
     lm->dlh = h;
     lm->next = pr_loadmap;
     pr_loadmap = lm;
     }
 #endif /* WIN32 || WIN16 */
 
 #ifdef XP_MACOSX
     {
@@ -1003,20 +1105,142 @@ pr_LoadLibraryByPathname(const char *nam
     result = lm;    /* success */
     PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name));
 
   unlock:
     if (result == NULL) {
         PR_SetError(PR_LOAD_LIBRARY_ERROR, oserr);
         DLLErrorInternal(oserr);  /* sets error text */
     }
+#ifdef WIN32
+    if (utf8name && utf8name != utf8name_stack) 
+        PR_Free(utf8name);
+    if (!(flags & PR_LD_PATHW) && wname && wname != wname_stack)
+        PR_Free(wname);
+#endif
     PR_ExitMonitor(pr_linker_lock);
     return result;
 }
 
+#ifdef WIN32
+#ifdef WIN95
+/*
+ * CP_UTF8 is not supported by WideCharToMultiByte on Windows 95 so that 
+ * we have to emulate it
+ */
+static PRStatus 
+pr_ConvertSingleCharToUTF8(PRUint32 usv, PRUint16 offset, int bufLen,
+                           int *utf8Len, char * *buf)
+{
+    /* XXX No error checking. Add it for debug build */
+    char* p = *buf;
+    /*if (!bufLen || !*buf) { */
+    if (!bufLen) {
+        *utf8Len += offset;
+        return PR_SUCCESS;
+    }
+
+    if (*utf8Len + offset >= bufLen)
+        return PR_FAILURE;
+
+    *utf8Len += offset;
+    if (offset == 1) {
+        *p++ = (char) usv;
+    } else if (offset == 2) {
+        *p++ = (char)0xc0 | (usv >> 6);
+        *p++ = (char)0x80 | (usv & 0x003f);
+    } else if (offset == 3) {
+        *p++ = (char)0xe0 | (usv >> 12);
+        *p++ = (char)0x80 | ((usv >> 6) & 0x003f);
+        *p++ = (char)0x80 | (usv & 0x003f);
+    } else { /* offset = 4 */
+        *p++ = (char)0xf0 | (usv >> 18);
+        *p++ = (char)0x80 | ((usv >> 12) & 0x003f);
+        *p++ = (char)0x80 | ((usv >> 6) & 0x003f);
+        *p++ = (char)0x80 | (usv & 0x003f);
+    }
+
+    *buf = p;
+    return PR_SUCCESS;
+}
+
+static int pr_ConvertUTF16toUTF8(LPCWSTR wname, LPSTR name, int len)
+{
+    LPCWSTR pw = wname;
+    LPSTR p = name;
+    int utf8Len = 0;
+    PRBool highSurrogate = PR_FALSE;
+
+    /* Windows NT4/2k/XP supports CP_UTF8. So do Win 98/ME, but
+     * we don't bother to optimize for them. */
+    if (_pr_useUnicode) 
+        return WideCharToMultiByte(CP_UTF8, 0, wname, -1, name, len, 
+                                   NULL, NULL);
+
+    if (!wname || len < 0 || (len > 0 && !name)) {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return 0;
+    }
+
+    while (*pw) {
+        PRStatus status = PR_SUCCESS;
+        if (highSurrogate) {
+            if (*pw >= (PRUnichar) 0xDC00 && *pw < (PRUnichar) 0xE000) {
+                /* found a matching low surrogate */
+                /* convert a surrogate pair to UCS4 */
+                PRUint32 usv = ((*(pw-1) - (PRUnichar)0xD800) << 10) + 
+                               (*pw - (PRUnichar)0xDC00) + (PRUint32)0x10000;
+                if (pr_ConvertSingleCharToUTF8(usv, 4, len, &utf8Len, &p) ==
+                    PR_FAILURE)
+                    return 0;
+                highSurrogate = PR_FALSE;
+                ++pw;
+                continue;
+            } else {
+                /*
+                 * silently ignore a lone high surrogate
+                 * as is done by WideCharToMultiByte by default
+                 */
+                highSurrogate = PR_FALSE;
+            }
+        }
+        if (*pw <= 0x7f) 
+            status = pr_ConvertSingleCharToUTF8(*pw, 1, len, &utf8Len, &p);
+        else if (*pw <= 0x07ff)
+            status = pr_ConvertSingleCharToUTF8(*pw, 2, len, &utf8Len, &p);
+        else if (*pw < (PRUnichar) 0xD800 || *pw >= (PRUnichar) 0xE000)
+            status = pr_ConvertSingleCharToUTF8(*pw, 3, len, &utf8Len, &p);
+        else if (*pw < (PRUnichar) 0xDC00)
+            highSurrogate = PR_TRUE;
+        /* else */
+        /* silently ignore a lone low surrogate as is done by 
+         * WideCharToMultiByte by default */
+
+        if (status == PR_FAILURE) {
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+            return 0;
+        }
+        ++pw;
+    }
+
+    /* if we're concerned with a lone high surrogate,
+     * we have to take care of it here, but we just drop it 
+     */
+    if (len > 0)
+        *p = '\0';
+    return utf8Len + 1;
+}
+#else
+static int pr_ConvertUTF16toUTF8(LPCWSTR wname, LPSTR name, int len)
+{
+    return WideCharToMultiByte(CP_UTF8, 0, wname, -1, name, len, NULL, NULL);
+}
+#endif /* WIN95 */
+#endif /* WIN32 */
+
 /*
 ** Unload a shared library which was loaded via PR_LoadLibrary
 */
 PR_IMPLEMENT(PRStatus) 
 PR_UnloadLibrary(PRLibrary *lib)
 {
     int result = 0;
     PRStatus status = PR_SUCCESS;
--- a/pr/src/md/windows/w95io.c
+++ b/pr/src/md/windows/w95io.c
@@ -76,19 +76,17 @@ static DWORD dirAccessTable[] = {
  * This constant is used by _PR_FileTimeToPRTime().
  */
 #if defined(__MINGW32__)
 static const PRTime _pr_filetime_offset = 116444736000000000LL;
 #else
 static const PRTime _pr_filetime_offset = 116444736000000000i64;
 #endif
 
-#ifdef MOZ_UNICODE
 static void InitUnicodeSupport(void);
-#endif
 
 static PRBool IsPrevCharSlash(const char *str, const char *current);
 
 void
 _PR_MD_INIT_IO()
 {
     WORD WSAVersion = 0x0101;
     WSADATA WSAData;
@@ -119,19 +117,17 @@ void
         rv = SystemTimeToFileTime(&systime, &filetime.ft);
         PR_ASSERT(0 != rv);
         PR_ASSERT(filetime.prt == _pr_filetime_offset);
     }
 #endif /* DEBUG */
 
     _PR_NT_InitSids();
 
-#ifdef MOZ_UNICODE
     InitUnicodeSupport();
-#endif
 }
 
 PRStatus
 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
 {
     DWORD rv;
 
     PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
@@ -1125,25 +1121,46 @@ typedef HANDLE (WINAPI *FindFirstFileWFn
 static FindFirstFileWFn findFirstFileW = FindFirstFileW;
 typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW);
 static FindNextFileWFn findNextFileW = FindNextFileW;
 typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *);
 static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW;
 typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR);
 static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW;
 
+#endif /* MOZ_UNICODE */
+
+PRBool _pr_useUnicode = PR_FALSE;
+
 static void InitUnicodeSupport(void)
 {
     /*
      * The W functions exist on Win9x as stubs that fail with the
      * ERROR_CALL_NOT_IMPLEMENTED error.  We plan to emulate the
      * MSLU W functions on Win9x in the future.
      */
+
+    /* Find out if we are running on a Unicode enabled version of Windows */
+    OSVERSIONINFOA osvi = {0};
+
+    osvi.dwOSVersionInfoSize = sizeof(osvi);
+    if (GetVersionExA(&osvi)) {
+        _pr_useUnicode = (osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT);
+    } else {
+        _pr_useUnicode = PR_FALSE;
+    }
+#ifdef DEBUG
+    /* In debug builds, allow explicit use of ANSI methods for testing. */
+    if (getenv("WINAPI_USE_ANSI"))
+        _pr_useUnicode = PR_FALSE;
+#endif
 }
 
+#ifdef MOZ_UNICODE
+
 /* ================ UTF16 Interfaces ================================ */
 void FlipSlashesW(PRUnichar *cp, size_t len)
 {
     while (len-- > 0) {
         if (cp[0] == L'/') {
             cp[0] = L'\\';
         }
         cp++;