From 21476e0a5df91dca434c716344fe2cc0d9e7fdbc Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Tue, 20 Aug 2013 19:25:02 +0200 Subject: [PATCH] Character conversion code put into a file on its own. Character conversion relies on the setlocale(LC_TYPE, "") in main(), but maybe there are better ways of doing this. --- CMakeLists.txt | 1 + wordref/CMakeLists.txt | 1 + wordref/src/CharConv.cpp | 98 +++++++++++++++++++++++++++++++++++ wordref/src/CharConv.hpp | 21 ++++++++ wordref/src/HttpReader.cpp | 12 ++--- wordref/src/WordReference.cpp | 2 +- wordref/src/WordReference.hpp | 2 +- wordref/src/main.cpp | 22 +++++--- wordref/src/main.hpp | 2 + 9 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 wordref/src/CharConv.cpp create mode 100644 wordref/src/CharConv.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dd1ed26..9f29ce3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ project (WordReference CXX) message(STATUS "Configuring ${PROJECT_NAME} for ${CMAKE_BUILD_TYPE}") +add_definitions("-DUNICODE") set (${PROJECT_NAME}_Version_Major 0) set (${PROJECT_NAME}_Version_Minor 1) set (${PROJECT_NAME}_App_Name "\"${PROJECT_NAME}\"") diff --git a/wordref/CMakeLists.txt b/wordref/CMakeLists.txt index 63b5b0b..e6dfa82 100644 --- a/wordref/CMakeLists.txt +++ b/wordref/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(${PROJECT_NAME} src/main.cpp src/WordReference.cpp src/HttpReader.cpp + src/CharConv.cpp ) target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} diff --git a/wordref/src/CharConv.cpp b/wordref/src/CharConv.cpp new file mode 100644 index 0000000..d85e2a7 --- /dev/null +++ b/wordref/src/CharConv.cpp @@ -0,0 +1,98 @@ +#include "main.hpp" +#include "CharConv.hpp" +#include +#include +#include +#include +#include + +namespace cconv { + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + ErrCantConvert::ErrCantConvert (const std::string& parFrom, const std::string& parTo) : + std::domain_error(std::string("Can't convert from \"") + parFrom + "\" to \"" + parTo + "\"") + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + ErrCantConvert::ErrCantConvert (const char* parFrom, const char* parTo) : + std::domain_error(std::string("Can't convert from \"") + parFrom + "\" to \"" + parTo + "\"") + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + std::wstring MultibyteToWide (const std::string& parMultiByte) { + return MultibyteToWide(parMultiByte.c_str(), parMultiByte.size()); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + std::wstring MultibyteToWide (const char* parMultiByte) { + return MultibyteToWide(parMultiByte, std::strlen(parMultiByte)); + } + + ///------------------------------------------------------------------------- + ///See: http://www.lemoda.net/c/iconv-example/iconv-example.html + ///------------------------------------------------------------------------- + std::wstring MultibyteToWide (const char* parMultiByte, size_t parLen) { + const size_t maxCount = parLen + 1; + + //const std::unique_ptr iconvMem(new char[maxCount * sizeof(wchar_t)]); + //{ + // const char* const localeFrom = "UTF-8"; //std::setlocale(LC_CTYPE, nullptr); + // static const char* const localeTo = "WCHAR_T"; + // const iconv_t iconvHandle = iconv_open(localeFrom, localeTo); + // if (reinterpret_cast(-1) == iconvHandle) { + // if (EINVAL == errno) + // throw ErrCantConvert(localeFrom, localeTo); + // else + // std::runtime_error("Error initializing iconv"); + // } + + // const std::unique_ptr multibyteCopy(new char[parLen + 1]); + // std::copy(parMultiByte, parMultiByte + parLen + 1, multibyteCopy.get()); + // size_t srcLen = parLen; + // size_t dstLen = maxCount; + // char* dstMem = iconvMem.get(); + // char* srcMem = multibyteCopy.get(); + // const size_t iconvRetVal = iconv(iconvHandle, &srcMem, &srcLen, &dstMem, &dstLen); + + // const int closeRetVal = iconv_close(iconvHandle); + // if (0 != closeRetVal) + // throw std::runtime_error("iconv_close() failed"); + //} + const std::unique_ptr buff(new wchar_t[maxCount]); + const size_t charCount = std::mbstowcs(buff.get(), parMultiByte, maxCount); + if (charCount == maxCount) + buff[maxCount - 1] = L'\0'; + else if (static_cast(-1) == charCount) + throw std::runtime_error("Can't convert received string, mbstowcs() returned an error"); + return std::wstring(buff.get()); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + std::string WideToMultibyte (const std::wstring& parWide) { + return WideToMultibyte(parWide.c_str(), parWide.size()); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + std::string WideToMultibyte (const wchar_t* parWide) { + return WideToMultibyte(parWide, std::wcslen(parWide)); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + std::string WideToMultibyte (const wchar_t* parWide, size_t parLen) { + const size_t maxMultibSize = parLen * 4 + 1; + const std::unique_ptr memForMultib(new char[maxMultibSize]); + + const size_t wctombsRet = std::wcstombs(memForMultib.get(), parWide, maxMultibSize); + if (EILSEQ == wctombsRet or static_cast(-1) == wctombsRet) + throw std::runtime_error("Can't convert received string to multibyte, wcstombs() failed"); + return std::string(memForMultib.get(), wctombsRet); + } +} //namespace cconv diff --git a/wordref/src/CharConv.hpp b/wordref/src/CharConv.hpp new file mode 100644 index 0000000..f251365 --- /dev/null +++ b/wordref/src/CharConv.hpp @@ -0,0 +1,21 @@ +#ifndef idA800F41881384E6980F1D79AFCCA0338 +#define idA800F41881384E6980F1D79AFCCA0338 + +namespace cconv { + class ErrCantConvert : public std::domain_error { + public: + ErrCantConvert ( const std::string& parFrom, const std::string& parTo ); + ErrCantConvert ( const char* parFrom, const char* parTo ); + ~ErrCantConvert ( void ) noexcept = default; + }; + + std::wstring MultibyteToWide ( const std::string& parMultiByte ) pure_function; + std::wstring MultibyteToWide ( const char* parMultiByte ) pure_function; + std::wstring MultibyteToWide ( const char* parMultiByte, size_t parLen ) pure_function; + + std::string WideToMultibyte ( const std::wstring& parWide ) pure_function; + std::string WideToMultibyte ( const wchar_t* parWide ) pure_function; + std::string WideToMultibyte ( const wchar_t* parWide, size_t parLen ) pure_function; +} //namespace cconv + +#endif diff --git a/wordref/src/HttpReader.cpp b/wordref/src/HttpReader.cpp index 5a7f2a5..3e0249e 100644 --- a/wordref/src/HttpReader.cpp +++ b/wordref/src/HttpReader.cpp @@ -18,10 +18,10 @@ along with this program. If not, see . #include "main.hpp" #include "HttpReader.hpp" +#include "CharConv.hpp" #include #include #include -#include namespace { ///------------------------------------------------------------------------ @@ -47,14 +47,8 @@ namespace { }; boost::algorithm::trim_all(parWord); - const size_t maxMultibSize = parWord.size() * 4 + 1; - const std::unique_ptr memForMultib(new char[maxMultibSize]); - - const size_t wctombsRet = std::wcstombs(memForMultib.get(), parWord.c_str(), maxMultibSize); - if (EILSEQ == wctombsRet) - throw std::runtime_error("Can't convert received string to multibyte, wcstombs() failed"); - - const std::unique_ptr urlEncoded(curl_easy_escape(parCurl, memForMultib.get(), static_cast(wctombsRet))); + const std::string wordMultibyte(cconv::WideToMultibyte(parWord)); + const std::unique_ptr urlEncoded(curl_easy_escape(parCurl, wordMultibyte.c_str(), static_cast(wordMultibyte.size()))); std::string retVal(urlEncoded.get()); return retVal; } diff --git a/wordref/src/WordReference.cpp b/wordref/src/WordReference.cpp index bb52388..c639f29 100644 --- a/wordref/src/WordReference.cpp +++ b/wordref/src/WordReference.cpp @@ -99,7 +99,7 @@ ErrBadLanguage::ErrBadLanguage (std::string&& parMessage) : ///----------------------------------------------------------------------------- ///----------------------------------------------------------------------------- -WordReference::WordReference (const char* parFrom, const char* parTo, const char* parApiKey) : +WordReference::WordReference (const std::string& parFrom, const std::string& parTo, const char* parApiKey) : m_httpReader(new HttpReader), m_langFrom(parFrom), m_langTo(parTo), diff --git a/wordref/src/WordReference.hpp b/wordref/src/WordReference.hpp index 3ac396e..13fee1b 100644 --- a/wordref/src/WordReference.hpp +++ b/wordref/src/WordReference.hpp @@ -37,7 +37,7 @@ public: class WordReference { public: WordReference ( void ) = delete; - WordReference ( const char* parFrom, const char* parTo, const char* parApiKey ); + WordReference ( const std::string& parFrom, const std::string& parTo, const char* parApiKey ); ~WordReference ( void ); const std::string& GetLanguageCode ( WordReferenceLangDirection parDir ) const; diff --git a/wordref/src/main.cpp b/wordref/src/main.cpp index 4c6924a..a9d2f2c 100644 --- a/wordref/src/main.cpp +++ b/wordref/src/main.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . #include "main.hpp" #include "WordReference.hpp" +#include "CharConv.hpp" #include #include #include @@ -51,9 +52,9 @@ namespace { oss << "Usage is " << GetBaseName(parArgv[0]) << " [options] ; parameters"; boost::program_options::options_description desc(oss.str()); boost::program_options::positional_options_description positionals; - positionals.add("source-lang", 0); + positionals.add("source-lang", 1); positionals.add("dest-lang", 1); - positionals.add("word", 2); + positionals.add("word", -1); boost::program_options::options_description hidden("hidden options"); hidden.add_options() @@ -72,7 +73,8 @@ namespace { boost::program_options::store(boost::program_options::command_line_parser(parArgc, parArgv).options(commandLine).positional(positionals).run(), parVarMap); boost::program_options::notify(parVarMap); bool shownSomething = false; - if (parVarMap.count("help")) { + if (parVarMap.count("help") or parVarMap.count("source-lang") != 1 or + parVarMap.count("dest-lang") != 1 or parVarMap.count("word") == 0) { std::cout << desc << "\n"; shownSomething = true; } @@ -83,6 +85,8 @@ namespace { ///----------------------------------------------------------------------------- ///----------------------------------------------------------------------------- int main (int parArgc, const char* const parArgv[]) { + //std::setlocale(LC_CTYPE, "UTF-8"); + std::setlocale(LC_CTYPE, ""); boost::program_options::variables_map vm; if (GetCommandLine(vm, parArgc, parArgv)) return 0; @@ -101,10 +105,16 @@ int main (int parArgc, const char* const parArgv[]) { return 0; } - WordReference wref("en", "it", DefApiKey); - wref.Translate(L"house", std::wcout); + { + const std::string langFrom(vm["source-lang"].as()); + const std::string langTo(vm["dest-lang"].as()); + const std::wstring searchWord(cconv::MultibyteToWide(vm["word"].as())); - std::cout << wref.GetHttpLink(L"north face") << "\n"; + WordReference wref(langFrom, langTo, DefApiKey); + wref.Translate(searchWord, std::wcout); + + std::cout << wref.GetHttpLink(searchWord) << "\n"; + } std::wcout << L"Written by King_DuckZ; © WordReference.com" << std::endl; return 0; } diff --git a/wordref/src/main.hpp b/wordref/src/main.hpp index d300a2d..9f2fad1 100644 --- a/wordref/src/main.hpp +++ b/wordref/src/main.hpp @@ -26,4 +26,6 @@ along with this program. If not, see . #include #include +#define pure_function __attribute__((pure)) + #endif