diff options
| author | Jules Blok <jules.blok@gmail.com> | 2015-06-10 00:00:12 +0200 |
|---|---|---|
| committer | Jules Blok <jules.blok@gmail.com> | 2015-06-10 00:00:12 +0200 |
| commit | 7dfced21a2e5aac7195e0e1ad76468e30306766b (patch) | |
| tree | bd671b639d3d911460b767caabad8fc8a759a6bf /Source/Core/Common/StringUtil.cpp | |
| parent | c25be031fc1031d795157d8344b87b7841145197 (diff) | |
| parent | 7b0a65e295c784f9d573f2ef52d4e2a7b9bb2889 (diff) | |
Merge branch 'master' into stable
Diffstat (limited to 'Source/Core/Common/StringUtil.cpp')
| -rw-r--r-- | Source/Core/Common/StringUtil.cpp | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/Source/Core/Common/StringUtil.cpp b/Source/Core/Common/StringUtil.cpp new file mode 100644 index 0000000000..903131a5d3 --- /dev/null +++ b/Source/Core/Common/StringUtil.cpp @@ -0,0 +1,448 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstdarg> +#include <cstddef> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <iomanip> +#include <istream> +#include <limits.h> +#include <string> +#include <vector> + +#include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" +#include "Common/StringUtil.h" + +#ifdef _WIN32 + #include <Windows.h> +#else + #include <iconv.h> + #include <locale.h> + #include <errno.h> +#endif + +#if !defined(_WIN32) && !defined(ANDROID) +static locale_t GetCLocale() +{ + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", nullptr); + return c_locale; +} +#endif + +// faster than sscanf +bool AsciiToHex(const std::string& _szValue, u32& result) +{ + // Set errno to a good state. + errno = 0; + + char *endptr = nullptr; + const u32 value = strtoul(_szValue.c_str(), &endptr, 16); + + if (!endptr || *endptr) + return false; + + if (errno == ERANGE) + return false; + + result = value; + return true; +} + +bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) +{ + int writtenCount; + +#ifdef _WIN32 + // You would think *printf are simple, right? Iterate on each character, + // if it's a format specifier handle it properly, etc. + // + // Nooooo. Not according to the C standard. + // + // According to the C99 standard (7.19.6.1 "The fprintf function") + // The format shall be a multibyte character sequence + // + // Because some character encodings might have '%' signs in the middle of + // a multibyte sequence (SJIS for example only specifies that the first + // byte of a 2 byte sequence is "high", the second byte can be anything), + // printf functions have to decode the multibyte sequences and try their + // best to not screw up. + // + // Unfortunately, on Windows, the locale for most languages is not UTF-8 + // as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the + // locale, and completely fails when trying to decode UTF-8 as EUC-CN. + // + // On the other hand, the fix is simple: because we use UTF-8, no such + // multibyte handling is required as we can simply assume that no '%' char + // will be present in the middle of a multibyte sequence. + // + // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l. + static _locale_t c_locale = nullptr; + if (!c_locale) + c_locale = _create_locale(LC_ALL, ".1252"); + writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); +#else + #if !defined(ANDROID) + locale_t previousLocale = uselocale(GetCLocale()); + #endif + writtenCount = vsnprintf(out, outsize, format, args); + #if !defined(ANDROID) + uselocale(previousLocale); + #endif +#endif + + if (writtenCount > 0 && writtenCount < outsize) + { + out[writtenCount] = '\0'; + return true; + } + else + { + out[outsize - 1] = '\0'; + return false; + } +} + +std::string StringFromFormat(const char* format, ...) +{ + va_list args; + va_start(args, format); + std::string res = StringFromFormatV(format, args); + va_end(args); + return std::move(res); +} + +std::string StringFromFormatV(const char* format, va_list args) +{ + char *buf = nullptr; +#ifdef _WIN32 + int required = _vscprintf(format, args); + buf = new char[required + 1]; + CharArrayFromFormatV(buf, required + 1, format, args); + + std::string temp = buf; + delete[] buf; +#else + #if !defined(ANDROID) + locale_t previousLocale = uselocale(GetCLocale()); + #endif + if (vasprintf(&buf, format, args) < 0) + ERROR_LOG(COMMON, "Unable to allocate memory for string"); + #if !defined(ANDROID) + uselocale(previousLocale); + #endif + + std::string temp = buf; + free(buf); +#endif + return std::move(temp); +} + +// For Debugging. Read out an u8 array. +std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces) +{ + std::ostringstream oss; + oss << std::setfill('0') << std::hex; + + for (int line = 0; size; ++data, --size) + { + oss << std::setw(2) << (int)*data; + + if (line_len == ++line) + { + oss << '\n'; + line = 0; + } + else if (spaces) + oss << ' '; + } + + return oss.str(); +} + +// Turns " hej " into "hej". Also handles tabs. +std::string StripSpaces(const std::string &str) +{ + const size_t s = str.find_first_not_of(" \t\r\n"); + + if (str.npos != s) + return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); + else + return ""; +} + +// "\"hello\"" is turned to "hello" +// This one assumes that the string has already been space stripped in both +// ends, as done by StripSpaces above, for example. +std::string StripQuotes(const std::string& s) +{ + if (s.size() && '\"' == s[0] && '\"' == *s.rbegin()) + return s.substr(1, s.size() - 2); + else + return s; +} + +bool TryParse(const std::string &str, u32 *const output) +{ + char *endptr = nullptr; + + // Reset errno to a value other than ERANGE + errno = 0; + + unsigned long value = strtoul(str.c_str(), &endptr, 0); + + if (!endptr || *endptr) + return false; + + if (errno == ERANGE) + return false; + +#if ULONG_MAX > UINT_MAX + if (value >= 0x100000000ull && + value <= 0xFFFFFFFF00000000ull) + return false; +#endif + + *output = static_cast<u32>(value); + return true; +} + +bool TryParse(const std::string &str, bool *const output) +{ + if ("1" == str || !strcasecmp("true", str.c_str())) + *output = true; + else if ("0" == str || !strcasecmp("false", str.c_str())) + *output = false; + else + return false; + + return true; +} + +std::string StringFromInt(int value) +{ + char temp[16]; + sprintf(temp, "%i", value); + return temp; +} + +std::string StringFromBool(bool value) +{ + return value ? "True" : "False"; +} + +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) +{ + if (full_path.empty()) + return false; + + size_t dir_end = full_path.find_last_of("/" + // Windows needs the : included for something like just "C:" to be considered a directory +#ifdef _WIN32 + ":" +#endif + ); + if (std::string::npos == dir_end) + dir_end = 0; + else + dir_end += 1; + + size_t fname_end = full_path.rfind('.'); + if (fname_end < dir_end || std::string::npos == fname_end) + fname_end = full_path.size(); + + if (_pPath) + *_pPath = full_path.substr(0, dir_end); + + if (_pFilename) + *_pFilename = full_path.substr(dir_end, fname_end - dir_end); + + if (_pExtension) + *_pExtension = full_path.substr(fname_end); + + return true; +} + +void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename) +{ + _CompleteFilename = _Path; + + // check for seperator + if (DIR_SEP_CHR != *_CompleteFilename.rbegin()) + _CompleteFilename += DIR_SEP_CHR; + + // add the filename + _CompleteFilename += _Filename; +} + +void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) +{ + std::istringstream iss(str); + output.resize(1); + + while (std::getline(iss, *output.rbegin(), delim)) + output.push_back(""); + + output.pop_back(); +} + +std::string TabsToSpaces(int tab_size, const std::string &in) +{ + const std::string spaces(tab_size, ' '); + std::string out(in); + + size_t i = 0; + while (out.npos != (i = out.find('\t'))) + out.replace(i, 1, spaces); + + return out; +} + +std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) +{ + size_t pos = 0; + + if (src == dest) + return result; + + while ((pos = result.find(src, pos)) != std::string::npos) + { + result.replace(pos, src.size(), dest); + pos += dest.length(); + } + + return result; +} + +#ifdef _WIN32 + +std::string UTF16ToUTF8(const std::wstring& input) +{ + auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), (int)input.size(), nullptr, 0, nullptr, nullptr); + + std::string output; + output.resize(size); + + if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), (int)input.size(), &output[0], (int)output.size(), nullptr, nullptr)) + { + output.clear(); + } + + return output; +} + +std::wstring CPToUTF16(u32 code_page, const std::string& input) +{ + auto const size = MultiByteToWideChar(code_page, 0, input.data(), (int)input.size(), nullptr, 0); + + std::wstring output; + output.resize(size); + + if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), (int)input.size(), &output[0], (int)output.size())) + { + output.clear(); + } + + return output; +} + +std::wstring UTF8ToUTF16(const std::string& input) +{ + return CPToUTF16(CP_UTF8, input); +} + +std::string SHIFTJISToUTF8(const std::string& input) +{ + return UTF16ToUTF8(CPToUTF16(932, input)); +} + +std::string CP1252ToUTF8(const std::string& input) +{ + return UTF16ToUTF8(CPToUTF16(1252, input)); +} + +#else + +template <typename T> +std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) +{ + std::string result; + + iconv_t const conv_desc = iconv_open("UTF-8", fromcode); + if ((iconv_t)-1 == conv_desc) + { + ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); + } + else + { + size_t const in_bytes = sizeof(T) * input.size(); + size_t const out_buffer_size = 4 * in_bytes; + + std::string out_buffer; + out_buffer.resize(out_buffer_size); + + auto src_buffer = &input[0]; + size_t src_bytes = in_bytes; + auto dst_buffer = &out_buffer[0]; + size_t dst_bytes = out_buffer.size(); + + while (src_bytes != 0) + { + size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, + &dst_buffer, &dst_bytes); + + if ((size_t)-1 == iconv_result) + { + if (EILSEQ == errno || EINVAL == errno) + { + // Try to skip the bad character + if (src_bytes != 0) + { + --src_bytes; + ++src_buffer; + } + } + else + { + ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); + break; + } + } + } + + out_buffer.resize(out_buffer_size - dst_bytes); + out_buffer.swap(result); + + iconv_close(conv_desc); + } + + return result; +} + +std::string CP1252ToUTF8(const std::string& input) +{ + //return CodeToUTF8("CP1252//TRANSLIT", input); + //return CodeToUTF8("CP1252//IGNORE", input); + return CodeToUTF8("CP1252", input); +} + +std::string SHIFTJISToUTF8(const std::string& input) +{ + //return CodeToUTF8("CP932", input); + return CodeToUTF8("SJIS", input); +} + +std::string UTF16ToUTF8(const std::wstring& input) +{ + std::string result = CodeToUTF8("UTF-16LE", input); + + // TODO: why is this needed? + result.erase(std::remove(result.begin(), result.end(), 0x00), result.end()); + return result; +} + +#endif |
