/* Copyright © 2009-11 Michal Zelinka, 2011-17 Robert Pösel, 2017-18 Miranda NG team 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 . */ #include "std_string_utils.h" std::string utils::url::encode(const std::string &s) { return (char*)ptrA(mir_urlEncode(s.c_str())); } std::string utils::url::decode(std::string data) { std::string new_string; for (std::string::size_type i = 0; i < data.length(); i++) { if (data.at(i) == '%' && (i + 2) < data.length()) { std::string num = data.substr(i + 1, 2); unsigned long udn = strtoul(num.c_str(), nullptr, 16); utils::text::append_ordinal(udn, &new_string); i += 2; continue; } new_string += data.at(i); } return new_string; } std::string utils::time::unix_timestamp() { time_t in = ::time(0); return utils::conversion::to_string((void*)&in, UTILS_CONV_TIME_T); } std::string utils::time::mili_timestamp() { SYSTEMTIME st; std::string timestamp = utils::time::unix_timestamp(); GetSystemTime(&st); timestamp.append(utils::conversion::to_string((void*)&st.wMilliseconds, UTILS_CONV_UNSIGNED_NUMBER)); return timestamp.substr(0, 13); } time_t utils::time::from_string(const std::string &data) { long long timestamp = _strtoi64(data.c_str(), nullptr, 10); // If it is milli timestamp if (timestamp > 100000000000) timestamp /= 1000; // If conversion fails, use local time? //if (!timestamp) // timestamp = ::time(NULL); return (time_t)timestamp; } std::string utils::conversion::to_string(void* data, WORD type) { std::stringstream out; switch (type) { case UTILS_CONV_BOOLEAN: out << (data ? "true" : "false"); break; case UTILS_CONV_TIME_T: out << (*(time_t*)data); break; case UTILS_CONV_SIGNED_NUMBER: out << (*(signed int*)data); break; case UTILS_CONV_UNSIGNED_NUMBER: out << (*(unsigned int*)data); break; } return out.str(); } void utils::text::replace_first(std::string* data, const std::string &from, const std::string &to) { std::string::size_type position = data->find(from); if (position != std::string::npos) { data->replace(position, from.size(), to); } } void utils::text::replace_all(std::string* data, const std::string &from, const std::string &to) { std::string::size_type position = 0; while ((position = data->find(from, position)) != std::string::npos) { data->replace(position, from.size(), to); position += to.size(); } } void utils::text::treplace_all(std::wstring* data, const std::wstring &from, const std::wstring &to) { std::wstring::size_type position = 0; while ((position = data->find(from, position)) != std::wstring::npos) { data->replace(position, from.size(), to); position++; } } unsigned int utils::text::count_all(std::string* data, const std::string &term) { unsigned int count = 0; std::string::size_type position = 0; while ((position = data->find(term, position)) != std::string::npos) { count++; position++; } return count; } void utils::text::append_ordinal(unsigned long value, std::string* data) { if (value <= 127) { // U+0000 .. U+007F *data += (char)value; } else if (value >= 128 && value <= 2047) { // U+0080 .. U+07FF *data += (char)(192 + (value / 64)); *data += (char)(128 + (value % 64)); } else if (value >= 2048 && value <= 65535) { // U+0800 .. U+FFFF *data += (char)(224 + (value / 4096)); *data += (char)(128 + ((value / 64) % 64)); *data += (char)(128 + (value % 64)); } else { *data += (char)((value >> 24) & 0xFF); *data += (char)((value >> 16) & 0xFF); *data += (char)((value >> 8) & 0xFF); *data += (char)((value) & 0xFF); } } std::string utils::text::html_entities_decode(std::string data) { utils::text::replace_all(&data, "&", "&"); utils::text::replace_all(&data, """, "\""); utils::text::replace_all(&data, "<", "<"); utils::text::replace_all(&data, ">", ">"); utils::text::replace_all(&data, " ", " "); utils::text::replace_all(&data, "♥", "\xE2\x99\xA5"); // direct byte replacement // utils::text::replace_all(&data, "♥", "\\u2665"); // indirect slashu replacement utils::text::replace_all(&data, "\\r", "\r"); utils::text::replace_all(&data, "\\n", "\n"); utils::text::replace_all(&data, "\\\"", "\""); utils::text::replace_all(&data, "\\/", "/"); utils::text::replace_all(&data, "\\\\", "\\"); // TODO: Add more to comply general usage // http://www.utexas.edu/learn/html/spchar.html // http://www.webmonkey.com/reference/Special_Characters // http://www.degraeve.com/reference/specialcharacters.php // http://www.chami.com/tips/internet/050798i.html // http://www.w3schools.com/tags/ref_entities.asp // http://www.natural-innovations.com/wa/doc-charset.html // http://webdesign.about.com/library/bl_htmlcodes.htm std::string new_string; for (std::string::size_type i = 0; i < data.length(); i++) { if (data.at(i) == '&' && (i + 1) < data.length() && data.at(i + 1) == '#') { std::string::size_type comma = data.find(";", i); if (comma != std::string::npos) { bool hexa = false; if ((i + 2) < data.length() && data.at(i + 2) == 'x') { hexa = true; i += 3; } else { i += 2; } std::string num = data.substr(i, comma - i); if (!num.empty()) { unsigned long udn = strtoul(num.c_str(), nullptr, hexa ? 16 : 10); utils::text::append_ordinal(udn, &new_string); } i = comma; continue; } } new_string += data.at(i); } return new_string; } std::string utils::text::remove_html(const std::string &data) { std::string new_string; for (std::string::size_type i = 0; i < data.length(); i++) { if (data.at(i) == '<' && (i + 1) < data.length() && data.at(i + 1) != ' ') { i = data.find(">", i); if (i == std::string::npos) break; continue; } new_string += data.at(i); } return new_string; } std::string utils::text::slashu_to_utf8(const std::string &data) { std::string new_string; for (std::string::size_type i = 0; i < data.length(); i++) { if (data.at(i) == '\\' && (i + 1) < data.length() && data.at(i + 1) == 'u') { unsigned long udn = strtoul(data.substr(i + 2, 4).c_str(), nullptr, 16); append_ordinal(udn, &new_string); i += 5; continue; } new_string += data.at(i); } return new_string; } std::string utils::text::trim(const std::string &data, bool rtrim) { std::string spaces = " \t\r\n"; std::string::size_type begin = rtrim ? 0 : data.find_first_not_of(spaces); std::string::size_type end = data.find_last_not_of(spaces); return (end != std::string::npos) ? data.substr(begin, end + 1 - begin) : ""; } void utils::text::explode(std::string str, const std::string &separator, std::vector* results) { std::string::size_type pos; pos = str.find_first_of(separator); while (pos != std::string::npos) { if (pos > 0) { results->push_back(str.substr(0, pos)); } str = str.substr(pos + 1); pos = str.find_first_of(separator); } if (str.length() > 0) { results->push_back(str); } } std::string utils::text::source_get_value(std::string* data, unsigned int argument_count, ...) { va_list arg; std::string ret; std::string::size_type start = 0, end = 0; va_start(arg, argument_count); for (unsigned int i = argument_count; i > 0; i--) { if (i == 1) { end = data->find(va_arg(arg, char*), start); if (start == std::string::npos || end == std::string::npos) break; ret = data->substr(start, end - start); } else { std::string term = va_arg(arg, char*); start = data->find(term, start); if (start == std::string::npos) break; start += term.length(); } } va_end(arg); return ret; } std::string utils::text::source_get_value2(std::string* data, const char *term, const char *endings, bool wholeString) { std::string::size_type start = 0, end = 0; std::string ret; start = data->find(term); if (start != std::string::npos) { start += mir_strlen(term); end = data->find_first_of(endings, start); if (end != std::string::npos) { ret = data->substr(start, end - start); } else if (wholeString) { ret = data->substr(start); } } return ret; } std::string utils::text::source_get_form_data(std::string* data, bool hiddenOnly) { const char *search = hiddenOnly ? "find(search, start); while (start != std::string::npos) { start++; std::string attr, value; std::string::size_type pos = data->find("name=\"", start); if (pos != std::string::npos) { pos += 6; std::string::size_type end = data->find("\"", pos); if (end != std::string::npos) attr = data->substr(pos, end - pos); end = data->find(">", pos); pos = data->find("value=\"", pos); if (pos != std::string::npos && end != std::string::npos && pos < end) { pos += 7; end = data->find("\"", pos); if (end != std::string::npos) value = data->substr(pos, end - pos); } } if (!attr.empty()) { if (!values.empty()) values += "&"; values += attr + "=" + value; } start = data->find(search, start); } return values; } std::string utils::text::prepare_name(const std::string &name, bool withSurnameLetter) { std::string::size_type pos = name.find(" "); if (pos == std::wstring::npos) return name; std::string result = name.substr(0, pos); if (withSurnameLetter) { pos = name.rfind(" ") + 1; // we're sure there is some space in name so we can do +1 safely if (pos < name.length()) result += " " + name.substr(pos, 1) + std::string("."); } return result; } std::string utils::text::rand_string(int len, const char *chars, unsigned int *number) { std::stringstream out; int strLen = (int)mir_strlen(chars); for (int i = 0; i < len; ++i) { out << chars[utils::number::random(0, strLen, number)]; } return out.str(); } std::string utils::text::truncate_utf8(const std::string &text, size_t maxLength) { // To not split some unicode character we need to transform it to wchar_t first, then split it, and then convert it back, because we want std::string as result // TODO: Probably there is much simpler and nicer way std::wstring wtext = ptrW(mir_utf8decodeW(text.c_str())); if (wtext.length() > maxLength) { wtext = wtext.substr(0, maxLength) + L"\x2026"; // unicode ellipsis return std::string(_T2A(wtext.c_str(), CP_UTF8)); } // It's not longer, return given string return text; } int utils::number::random(int min, int max, unsigned int *number) { if (number != nullptr) { errno_t err = rand_s(number); if (!err) return (*number % (max - min)) + min; } // If called didn't specified "number" or there was error, fallback to rand() return (rand() % (max - min)) + min; }