/** * Init once for the path conversion code. * * @returns IPRT status code. * @param pvUser1 Unused. * @param pvUser2 Unused. */ static DECLCALLBACK(int32_t) rtPathConvInitOnce(void *pvUser) { /* * Read the environment variable, no mercy on misconfigs here except that * empty values are quietly ignored. (We use a temp buffer for stripping.) */ char *pszEnvValue = NULL; char szEnvValue[sizeof(g_szFsCodeset)]; int rc = RTEnvGetEx(RTENV_DEFAULT, RTPATH_CODESET_ENV_VAR, szEnvValue, sizeof(szEnvValue), NULL); if (rc != VERR_ENV_VAR_NOT_FOUND && RT_FAILURE(rc)) return rc; if (RT_SUCCESS(rc)) pszEnvValue = RTStrStrip(szEnvValue); if (pszEnvValue && *pszEnvValue) { g_fPassthruUtf8 = rtPathConvInitIsUtf8(pszEnvValue); g_enmFsToUtf8Idx = RTSTRICONV_FS_TO_UTF8; g_enmUtf8ToFsIdx = RTSTRICONV_UTF8_TO_FS; strcpy(g_szFsCodeset, pszEnvValue); } else { const char *pszCodeset = rtStrGetLocaleCodeset(); size_t cchCodeset = pszCodeset ? strlen(pszCodeset) : sizeof(g_szFsCodeset); if (cchCodeset >= sizeof(g_szFsCodeset)) /* This shouldn't happen, but we'll manage. */ g_szFsCodeset[0] = '\0'; else { memcpy(g_szFsCodeset, pszCodeset, cchCodeset + 1); pszCodeset = g_szFsCodeset; } g_fPassthruUtf8 = rtPathConvInitIsUtf8(pszCodeset); g_enmFsToUtf8Idx = RTSTRICONV_LOCALE_TO_UTF8; g_enmUtf8ToFsIdx = RTSTRICONV_UTF8_TO_LOCALE; } NOREF(pvUser); return VINF_SUCCESS; }
/** * Converts a string from one charset to another without using the handle cache. * * @returns IPRT status code. * * @param pvInput Pointer to intput string. * @param cbInput Size (in bytes) of input string. Excludes any terminators. * @param pszInputCS Codeset of the input string. * @param ppvOutput Pointer to pointer to output buffer if cbOutput > 0. * If cbOutput is 0 this is where the pointer to the allocated * buffer is stored. * @param cbOutput Size of the passed in buffer. * @param pszOutputCS Codeset of the input string. * @param cFactor Input vs. output size factor. */ static int rtStrConvertUncached(const void *pvInput, size_t cbInput, const char *pszInputCS, void **ppvOutput, size_t cbOutput, const char *pszOutputCS, unsigned cFactor) { /* * Allocate buffer */ bool fUcs2Term; void *pvOutput; size_t cbOutput2; if (!cbOutput) { cbOutput2 = cbInput * cFactor; pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16)); if (!pvOutput) return VERR_NO_TMP_MEMORY; fUcs2Term = true; } else { pvOutput = *ppvOutput; fUcs2Term = !strcmp(pszOutputCS, "UCS-2"); cbOutput2 = cbOutput - (fUcs2Term ? sizeof(RTUTF16) : 1); if (cbOutput2 > cbOutput) return VERR_BUFFER_OVERFLOW; } /* * Use a loop here to retry with bigger buffers. */ for (unsigned cTries = 10; cTries > 0; cTries--) { /* * Create conversion object. */ #if defined(RT_OS_SOLARIS) || defined(RT_OS_NETBSD) /* Some systems don't grok empty codeset strings, so help them find the current codeset. */ if (!*pszInputCS) pszInputCS = rtStrGetLocaleCodeset(); if (!*pszOutputCS) pszOutputCS = rtStrGetLocaleCodeset(); #endif IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc causes trouble */ iconv_t icHandle = iconv_open(pszOutputCS, pszInputCS); IPRT_ALIGNMENT_CHECKS_ENABLE(); if (icHandle != (iconv_t)-1) { /* * Do the conversion. */ size_t cbInLeft = cbInput; size_t cbOutLeft = cbOutput2; const void *pvInputLeft = pvInput; void *pvOutputLeft = pvOutput; size_t cchNonRev; #if defined(RT_OS_LINUX) || defined(RT_OS_HAIKU) || defined(RT_OS_SOLARIS) || (defined(RT_OS_DARWIN) && defined(_DARWIN_FEATURE_UNIX_CONFORMANCE)) /* there are different opinions about the constness of the input buffer. */ cchNonRev = iconv(icHandle, (char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft); #else cchNonRev = iconv(icHandle, (const char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft); #endif if (cchNonRev != (size_t)-1) { if (!cbInLeft) { /* * We're done, just add the terminator and return. * (Two terminators to support UCS-2 output, too.) */ iconv_close(icHandle); ((char *)pvOutputLeft)[0] = '\0'; if (fUcs2Term) ((char *)pvOutputLeft)[1] = '\0'; *ppvOutput = pvOutput; if (cchNonRev == 0) return VINF_SUCCESS; return VWRN_NO_TRANSLATION; } errno = E2BIG; } iconv_close(icHandle); /* * If we failed because of output buffer space we'll * increase the output buffer size and retry. */ if (errno == E2BIG) { if (!cbOutput) { RTMemTmpFree(pvOutput); cbOutput2 *= 2; pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16)); if (!pvOutput) return VERR_NO_TMP_MEMORY; continue; } return VERR_BUFFER_OVERFLOW; } } break; } /* failure */ if (!cbOutput) RTMemTmpFree(pvOutput); return VERR_NO_TRANSLATION; }
/** * Converts a string from one charset to another. * * @returns iprt status code. * @param pvInput Pointer to intput string. * @param cbInput Size (in bytes) of input string. Excludes any terminators. * @param pszInputCS Codeset of the input string. * @param ppvOutput Pointer to pointer to output buffer if cbOutput > 0. * If cbOutput is 0 this is where the pointer to the allocated * buffer is stored. * @param cbOutput Size of the passed in buffer. * @param pszOutputCS Codeset of the input string. * @param cFactor Input vs. output size factor. * @param phIconv Pointer to the cache entry. */ static int rtstrConvertCached(const void *pvInput, size_t cbInput, const char *pszInputCS, void **ppvOutput, size_t cbOutput, const char *pszOutputCS, unsigned cFactor, iconv_t *phIconv) { /* * Allocate buffer */ bool fUcs2Term; void *pvOutput; size_t cbOutput2; if (!cbOutput) { cbOutput2 = cbInput * cFactor; pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16)); if (!pvOutput) return VERR_NO_TMP_MEMORY; fUcs2Term = true; } else { pvOutput = *ppvOutput; fUcs2Term = !strcmp(pszOutputCS, "UCS-2") || !strcmp(pszOutputCS, "UTF-16") || !strcmp(pszOutputCS, "ucs-2") || !strcmp(pszOutputCS, "utf-16"); cbOutput2 = cbOutput - (fUcs2Term ? sizeof(RTUTF16) : 1); if (cbOutput2 > cbOutput) return VERR_BUFFER_OVERFLOW; } /* * Use a loop here to retry with bigger buffers. */ for (unsigned cTries = 10; cTries > 0; cTries--) { /* * Create conversion object if necessary. */ iconv_t hIconv = (iconv_t)*phIconv; if (hIconv == (iconv_t)-1) { #ifdef RT_OS_SOLARIS /* Solaris doesn't grok empty codeset strings, so help it find the current codeset. */ if (!*pszInputCS) pszInputCS = rtStrGetLocaleCodeset(); if (!*pszOutputCS) pszOutputCS = rtStrGetLocaleCodeset(); #endif IPRT_ALIGNMENT_CHECKS_DISABLE(); /* glibc causes trouble */ *phIconv = hIconv = iconv_open(pszOutputCS, pszInputCS); IPRT_ALIGNMENT_CHECKS_ENABLE(); } if (hIconv != (iconv_t)-1) { /* * Do the conversion. */ size_t cbInLeft = cbInput; size_t cbOutLeft = cbOutput2; const void *pvInputLeft = pvInput; void *pvOutputLeft = pvOutput; #if defined(RT_OS_LINUX) || defined(RT_OS_HAIKU) || defined(RT_OS_SOLARIS) || (defined(RT_OS_DARWIN) && defined(_DARWIN_FEATURE_UNIX_CONFORMANCE)) /* there are different opinions about the constness of the input buffer. */ if (iconv(hIconv, (char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft) != (size_t)-1) #else if (iconv(hIconv, (const char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft) != (size_t)-1) #endif { if (!cbInLeft) { /* * We're done, just add the terminator and return. * (Two terminators to support UCS-2 output, too.) */ ((char *)pvOutputLeft)[0] = '\0'; if (fUcs2Term) ((char *)pvOutputLeft)[1] = '\0'; *ppvOutput = pvOutput; return VINF_SUCCESS; } errno = E2BIG; } /* * If we failed because of output buffer space we'll * increase the output buffer size and retry. */ if (errno == E2BIG) { if (!cbOutput) { RTMemTmpFree(pvOutput); cbOutput2 *= 2; pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUTF16)); if (!pvOutput) return VERR_NO_TMP_MEMORY; continue; } return VERR_BUFFER_OVERFLOW; } /* * Close the handle on all other errors to make sure we won't carry * any bad state with us. */ *phIconv = (iconv_t)-1; iconv_close(hIconv); } break; } /* failure */ if (!cbOutput) RTMemTmpFree(pvOutput); return VERR_NO_TRANSLATION; }