Commit d5713996 authored by Mickaël Turnel's avatar Mickaël Turnel

Fix charset conversion problem on Mac and iOS

parent 405da744
Pipeline #270 passed with stage
in 0 seconds
......@@ -63,6 +63,10 @@ if(NOT CPACK_GENERATOR AND NOT CMAKE_INSTALL_RPATH AND CMAKE_INSTALL_PREFIX)
message(STATUS "Setting install rpath to ${CMAKE_INSTALL_RPATH}")
endif()
if(APPLE)
find_package(Iconv REQUIRED)
endif()
if(ENABLE_MBEDTLS)
find_package(MbedTLS)
if(MBEDTLS_FOUND)
......
......@@ -38,16 +38,13 @@ set(BCTOOLBOX_CXX_SOURCE_FILES
utils/exception.cc
)
set(BCTOOLBOX_OBJC_SOURCE_FILES)
if (APPLE)
list(APPEND BCTOOLBOX_OBJC_SOURCE_FILES conversion/charconv_apple.mm)
elseif (ANDROID)
if(ANDROID)
list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv_android.cc)
elseif (WIN32)
elseif(WIN32)
list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv_windows.cc)
elseif (UNIX)
list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv_linux.cc)
endif ()
else()
list(APPEND BCTOOLBOX_CXX_SOURCE_FILES conversion/charconv.cc)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND STRICT_OPTIONS_CXX "-x c++")
......@@ -67,19 +64,19 @@ if(ENABLE_TESTS_COMPONENT)
set(BCTOOLBOX_C_TESTER_SOURCE_FILES tester.c)
endif()
set(BCTOOLBOX_SOURCE_FILES ${BCTOOLBOX_C_SOURCE_FILES} ${BCTOOLBOX_CXX_SOURCE_FILES} ${BCTOOLBOX_OBJC_SOURCE_FILES})
set(BCTOOLBOX_SOURCE_FILES ${BCTOOLBOX_C_SOURCE_FILES} ${BCTOOLBOX_CXX_SOURCE_FILES})
set(BCTOOLBOX_TESTER_SOURCE_FILES ${BCTOOLBOX_C_TESTER_SOURCE_FILES})
bc_apply_compile_flags(BCTOOLBOX_C_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C)
bc_apply_compile_flags(BCTOOLBOX_C_TESTER_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_C)
bc_apply_compile_flags(BCTOOLBOX_CXX_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)
bc_apply_compile_flags(BCTOOLBOX_OBJC_SOURCE_FILES STRICT_OPTIONS_CPP STRICT_OPTIONS_OBJC)
if(ENABLE_STATIC)
add_library(bctoolbox-static STATIC ${BCTOOLBOX_SOURCE_FILES} ${BCTOOLBOX_HEADER_FILES})
target_link_libraries(bctoolbox-static INTERFACE ${CMAKE_THREAD_LIBS_INIT})
if(APPLE)
target_link_libraries(bctoolbox-static INTERFACE "-framework Foundation" "-framework AVFoundation")
target_include_directories(bctoolbox-static PRIVATE ${ICONV_INCLUDE_DIRS})
target_link_libraries(bctoolbox-static INTERFACE ${ICONV_LIBRARIES})
endif()
if(WIN32)
target_link_libraries(bctoolbox-static INTERFACE "Winmm" "Ws2_32")
......@@ -120,7 +117,8 @@ if(ENABLE_SHARED)
)
endif()
if(APPLE)
target_link_libraries(bctoolbox PRIVATE "-framework Foundation" "-framework AVFoundation")
target_include_directories(bctoolbox PRIVATE ${ICONV_INCLUDE_DIRS})
target_link_libraries(bctoolbox PRIVATE ${ICONV_LIBRARIES})
endif()
if(WIN32)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
......
......@@ -20,29 +20,77 @@
#include "config.h"
#endif
#ifdef __APPLE__
#include "TargetConditionals.h"
#endif
#include <errno.h>
#include <iconv.h>
#include <langinfo.h>
#include <locale.h>
#include <string.h>
#include "bctoolbox/logging.h"
#include "bctoolbox/port.h"
#include "bctoolbox/charconv.h"
namespace {
std::string defaultEncoding = "";
static char *convert_from_to (const char *str, const char *from, const char *to) {
if (!from || !to)
return NULL;
if (strcasecmp(from, to) == 0)
return bctbx_strdup(str);
char *in_buf = (char *) str;
char *out_buf, *ptr;
size_t in_left = strlen(str) + 1;
size_t out_left = in_left + in_left/10; // leave a marge of 10%
iconv_t cd;
const char* r_from = strcasecmp("locale", from) == 0 ? "" : from;
const char* r_to = strcasecmp("locale", to) == 0 ? "" : to;
if (strcasecmp(r_from, r_to) == 0) {
return bctbx_strdup(str);
}
cd = iconv_open(r_to, r_from);
if (cd != (iconv_t)-1) {
size_t ret;
size_t out_len = out_left;
out_buf = (char *) bctbx_malloc(out_left);
ptr = out_buf; // Keep a pointer to the beginning of this buffer to be able to realloc
ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left);
while (ret == (size_t)-1 && errno == E2BIG) {
ptr = (char *) bctbx_realloc(ptr, out_len*2);
out_left = out_len;
out_buf = ptr + out_left;
out_len *= 2;
ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left);
}
iconv_close(cd);
if (ret == (size_t)-1 && errno != E2BIG) {
bctbx_error("Error while converting a string from '%s' to '%s': %s", from, to, strerror(errno));
bctbx_free(ptr);
return NULL;
}
} else {
bctbx_error("Unable to open iconv content descriptor from '%s' to '%s': %s", from, to, strerror(errno));
return NULL;
}
return ptr;
}
void bctbx_set_default_encoding (const char *encoding) {
defaultEncoding = std::string(encoding);
char *bctbx_locale_to_utf8 (const char *str) {
return convert_from_to(str, "locale", "UTF-8");
}
const char *bctbx_get_default_encoding () {
if (!defaultEncoding.empty())
return defaultEncoding.c_str();
char *bctbx_utf8_to_locale (const char *str) {
return convert_from_to(str, "UTF-8", "locale");
}
#if defined(__ANDROID__) || TARGET_OS_IPHONE
return "UTF-8";
#else
return "locale";
#endif
char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) {
return convert_from_to(str, encoding, "UTF-8");
}
/*
bctoolbox
Copyright (C) 2016 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#import "config.h"
#endif
#import <Foundation/Foundation.h>
#import <Foundation/NSString.h>
#import "bctoolbox/logging.h"
#import "bctoolbox/port.h"
#import "bctoolbox/charconv.h"
static NSStringEncoding getDefaultStringEncoding (const char *encoding) {
const char *defaultEncoding = encoding;
if (defaultEncoding == NULL)
defaultEncoding = bctbx_get_default_encoding();
if (!strcmp(defaultEncoding, "UTF-8"))
return NSUTF8StringEncoding;
if (!strcmp(defaultEncoding, "locale")) {
return [NSString defaultCStringEncoding];
} else {
NSString *encodingString = [[NSString alloc] initWithCString:defaultEncoding encoding:[NSString defaultCStringEncoding]];
CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef) encodingString);
if (stringEncoding == kCFStringEncodingInvalidId) {
bctbx_error("Error invalid ID '%s': unknown charset", defaultEncoding);
return [NSString defaultCStringEncoding];
}
return CFStringConvertEncodingToNSStringEncoding(stringEncoding);
}
}
char *bctbx_locale_to_utf8 (const char *str) {
NSStringEncoding encoding = getDefaultStringEncoding(NULL);
if (encoding == NSUTF8StringEncoding)
return bctbx_strdup(str);
NSString *string = [[NSString alloc] initWithCString:str encoding:encoding];
return bctbx_strdup([string UTF8String]);
}
char *bctbx_utf8_to_locale (const char *str) {
NSStringEncoding encoding = getDefaultStringEncoding(NULL);
if (encoding == NSUTF8StringEncoding)
return bctbx_strdup(str);
NSString *string = [[NSString alloc] initWithUTF8String:str];
return bctbx_strdup([string cStringUsingEncoding:encoding]);
}
char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) {
if (!encoding)
return NULL;
if (!strcmp(encoding, "UTF-8"))
return bctbx_strdup(str);
NSString *encodingString = [[NSString alloc] initWithCString:encoding encoding:[NSString defaultCStringEncoding]];
CFStringEncoding stringEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef) encodingString);
if (stringEncoding == kCFStringEncodingInvalidId) {
bctbx_error("Error while converting a string from '%s' to 'UTF-8': unknown charset", encoding);
return NULL;
}
NSStringEncoding encodingValue = CFStringConvertEncodingToNSStringEncoding(stringEncoding);
NSString *string = [[NSString alloc] initWithCString:str encoding:encodingValue];
return bctbx_strdup([string UTF8String]);
}
/*
bctoolbox
Copyright (C) 2016 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <iconv.h>
#include <langinfo.h>
#include <locale.h>
#include <string.h>
#include "bctoolbox/logging.h"
#include "bctoolbox/port.h"
#include "bctoolbox/charconv.h"
static char *convert_from_to (const char *str, const char *from, const char *to) {
if (!from || !to)
return NULL;
if (strcasecmp(from, to) == 0)
return bctbx_strdup(str);
char *in_buf = (char *) str;
char *out_buf, *ptr;
size_t in_left = strlen(str) + 1;
size_t out_left = in_left + in_left/10; // leave a marge of 10%
iconv_t cd;
setlocale(LC_CTYPE, ""); // Retrieve environment locale before calling nl_langinfo
const char* r_from = strcasecmp("locale", from) == 0 ? nl_langinfo(CODESET) : from;
const char* r_to = strcasecmp("locale", to) == 0 ? nl_langinfo(CODESET) : to;
if (strcasecmp(r_from, r_to) == 0) {
return bctbx_strdup(str);
}
cd = iconv_open(r_to, r_from);
if (cd != (iconv_t)-1) {
size_t ret;
size_t out_len = out_left;
out_buf = (char *) bctbx_malloc(out_left);
ptr = out_buf; // Keep a pointer to the beginning of this buffer to be able to realloc
ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left);
while (ret == (size_t)-1 && errno == E2BIG) {
ptr = (char *) bctbx_realloc(ptr, out_len*2);
out_left = out_len;
out_buf = ptr + out_left;
out_len *= 2;
ret = iconv(cd, &in_buf, &in_left, &out_buf, &out_left);
}
iconv_close(cd);
if (ret == (size_t)-1 && errno != E2BIG) {
bctbx_error("Error while converting a string from '%s' to '%s': %s", from, to, strerror(errno));
bctbx_free(ptr);
return NULL;
}
} else {
bctbx_error("Unable to open iconv content descriptor from '%s' to '%s': %s", from, to, strerror(errno));
return NULL;
}
return ptr;
}
char *bctbx_locale_to_utf8 (const char *str) {
const char *default_encoding = bctbx_get_default_encoding();
if (!strcmp(default_encoding, "UTF-8"))
return bctbx_strdup(str);
return convert_from_to(str, default_encoding, "UTF-8");
}
char *bctbx_utf8_to_locale (const char *str) {
const char *default_encoding = bctbx_get_default_encoding();
if (!strcmp(default_encoding, "UTF-8"))
return bctbx_strdup(str);
return convert_from_to(str, "UTF-8", default_encoding);
}
char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) {
return convert_from_to(str, encoding, "UTF-8");
}
......@@ -158,21 +158,11 @@ static char *convertFromTo (const char *str, const char *from, const char *to) {
}
char *bctbx_locale_to_utf8 (const char *str) {
const char *defaultEncoding = bctbx_get_default_encoding();
if (!strcmp(defaultEncoding, "UTF-8"))
return bctbx_strdup(str);
return convertFromTo(str, defaultEncoding, "UTF-8");
return convertFromTo(str, "locale", "UTF-8");
}
char *bctbx_utf8_to_locale (const char *str) {
const char *defaultEncoding = bctbx_get_default_encoding();
if (!strcmp(defaultEncoding, "UTF-8"))
return bctbx_strdup(str);
return convertFromTo(str, "UTF-8", defaultEncoding);
return convertFromTo(str, "UTF-8", "locale");
}
char *bctbx_convert_any_to_utf8 (const char *str, const char *encoding) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment