diff options
Diffstat (limited to 'indra/llcommon/llstring.cpp')
-rw-r--r-- | indra/llcommon/llstring.cpp | 950 |
1 files changed, 662 insertions, 288 deletions
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 7a2d42dfaa..33b55d843c 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -1,31 +1,26 @@ /** * @file llstring.cpp - * @brief String utility functions and the LLString class. + * @brief String utility functions and the std::string class. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library 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 + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,12 +36,21 @@ #include <winnls.h> // for WideCharToMultiByte #endif +LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format"); + + std::string ll_safe_string(const char* in) { if(in) return std::string(in); return std::string(); } +std::string ll_safe_string(const char* in, S32 maxlen) +{ + if(in) return std::string(in, maxlen); + return std::string(); +} + U8 hex_as_nybble(char hex) { if((hex >= '0') && (hex <= '9')) @@ -64,8 +68,26 @@ U8 hex_as_nybble(char hex) return 0; // uh - oh, not hex any more... } +bool iswindividual(llwchar elem) +{ + U32 cur_char = (U32)elem; + bool result = false; + if (0x2E80<= cur_char && cur_char <= 0x9FFF) + { + result = true; + } + else if (0xAC00<= cur_char && cur_char <= 0xD7A0 ) + { + result = true; + } + else if (0xF900<= cur_char && cur_char <= 0xFA60 ) + { + result = true; + } + return result; +} -bool _read_file_into_string(std::string& str, const char* filename) +bool _read_file_into_string(std::string& str, const std::string& filename) { llifstream ifs(filename, llifstream::binary); if (!ifs.is_open()) @@ -127,7 +149,7 @@ S32 wchar_to_utf8chars(llwchar in_char, char* outchars) *outchars++ = 0xF0 | (cur_char >> 18); *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | cur_char & 0x3F; + *outchars++ = 0x80 | (cur_char & 0x3F); } else if (cur_char < 0x4000000) { @@ -135,7 +157,7 @@ S32 wchar_to_utf8chars(llwchar in_char, char* outchars) *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F); *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | cur_char & 0x3F; + *outchars++ = 0x80 | (cur_char & 0x3F); } else if (cur_char < 0x80000000) { @@ -144,7 +166,7 @@ S32 wchar_to_utf8chars(llwchar in_char, char* outchars) *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F); *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | cur_char & 0x3F; + *outchars++ = 0x80 | (cur_char & 0x3F); } else { @@ -174,20 +196,6 @@ S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar) return inchars - base; } -S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8p) -{ - // Get 32 bit char32 - llwchar char32; - S32 nchars16 = utf16chars_to_wchar(inchars, &char32); - // Convert to utf8 - S32 nchars8 = wchar_to_utf8chars(char32, outchars); - if (nchars8p) - { - *nchars8p = nchars8; - } - return nchars16; -} - llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len) { llutf16string out; @@ -216,7 +224,7 @@ llutf16string wstring_to_utf16str(const LLWString &utf32str) return wstring_to_utf16str(utf32str, len); } -llutf16string utf8str_to_utf16str ( const LLString& utf8str ) +llutf16string utf8str_to_utf16str ( const std::string& utf8str ) { LLWString wstr = utf8str_to_wstring ( utf8str ); return wstring_to_utf16str ( wstr ); @@ -492,210 +500,10 @@ std::string utf16str_to_utf8str(const llutf16string& utf16str, S32 len) return wstring_to_utf8str(utf16str_to_wstring(utf16str, len), len); } - -//LLWString wstring_truncate(const LLWString &wstr, const S32 max_len) -//{ -// return wstr.substr(0, llmin((S32)wstr.length(), max_len)); -//} -// -// -//LLWString wstring_trim(const LLWString &wstr) -//{ -// LLWString outstr; -// outstr = wstring_trimhead(wstr); -// outstr = wstring_trimtail(outstr); -// return outstr; -//} -// -// -//LLWString wstring_trimhead(const LLWString &wstr) -//{ -// if(wstr.empty()) -// { -// return wstr; -// } -// -// S32 i = 0; -// while((i < (S32)wstr.length()) && iswspace(wstr[i])) -// { -// i++; -// } -// return wstr.substr(i, wstr.length() - i); -//} -// -// -//LLWString wstring_trimtail(const LLWString &wstr) -//{ -// if(wstr.empty()) -// { -// return wstr; -// } -// -// S32 len = (S32)wstr.length(); -// -// S32 i = len - 1; -// while (i >= 0 && iswspace(wstr[i])) -// { -// i--; -// } -// -// if (i >= 0) -// { -// return wstr.substr(0, i + 1); -// } -// return wstr; -//} -// -// -//LLWString wstring_copyinto(const LLWString &dest, const LLWString &src, const S32 insert_offset) -//{ -// llassert( insert_offset <= (S32)dest.length() ); -// -// LLWString out_str = dest.substr(0, insert_offset); -// out_str += src; -// LLWString tail = dest.substr(insert_offset); -// out_str += tail; -// -// return out_str; -//} - - -//LLWString wstring_detabify(const LLWString &wstr, const S32 num_spaces) -//{ -// LLWString out_str; -// // Replace tabs with spaces -// for (S32 i = 0; i < (S32)wstr.length(); i++) -// { -// if (wstr[i] == '\t') -// { -// for (S32 j = 0; j < num_spaces; j++) -// out_str += ' '; -// } -// else -// { -// out_str += wstr[i]; -// } -// } -// return out_str; -//} - - -//LLWString wstring_makeASCII(const LLWString &wstr) -//{ -// // Replace non-ASCII chars with replace_char -// LLWString out_str = wstr; -// for (S32 i = 0; i < (S32)out_str.length(); i++) -// { -// if (out_str[i] > 0x7f) -// { -// out_str[i] = LL_UNKNOWN_CHAR; -// } -// } -// return out_str; -//} - - -//LLWString wstring_substChar(const LLWString &wstr, const llwchar target_char, const llwchar replace_char) -//{ -// // Replace all occurences of target_char with replace_char -// LLWString out_str = wstr; -// for (S32 i = 0; i < (S32)out_str.length(); i++) -// { -// if (out_str[i] == target_char) -// { -// out_str[i] = replace_char; -// } -// } -// return out_str; -//} -// -// -//LLWString wstring_tolower(const LLWString &wstr) -//{ -// LLWString out_str = wstr; -// for (S32 i = 0; i < (S32)out_str.length(); i++) -// { -// out_str[i] = towlower(out_str[i]); -// } -// return out_str; -//} -// -// -//LLWString wstring_convert_to_lf(const LLWString &wstr) -//{ -// const llwchar CR = 13; -// // Remove carriage returns from string with CRLF -// LLWString out_str; -// -// for (S32 i = 0; i < (S32)wstr.length(); i++) -// { -// if (wstr[i] != CR) -// { -// out_str += wstr[i]; -// } -// } -// return out_str; -//} -// -// -//LLWString wstring_convert_to_crlf(const LLWString &wstr) -//{ -// const llwchar LF = 10; -// const llwchar CR = 13; -// // Remove carriage returns from string with CRLF -// LLWString out_str; -// -// for (S32 i = 0; i < (S32)wstr.length(); i++) -// { -// if (wstr[i] == LF) -// { -// out_str += CR; -// } -// out_str += wstr[i]; -// } -// return out_str; -//} - - -//S32 wstring_compare_insensitive(const LLWString &lhs, const LLWString &rhs) -//{ -// -// if (lhs == rhs) -// { -// return 0; -// } -// -// if (lhs.empty()) -// { -// return rhs.empty() ? 0 : 1; -// } -// -// if (rhs.empty()) -// { -// return -1; -// } -// -//#ifdef LL_LINUX -// // doesn't work because gcc 2.95 doesn't correctly implement c_str(). Sigh... -// llerrs << "wstring_compare_insensitive doesn't work on Linux!" << llendl; -// return 0; -//#else -// LLWString lhs_lower = lhs; -// LLWString::toLower(lhs_lower); -// std::string lhs_lower = wstring_to_utf8str(lhs_lower); -// LLWString rhs_lower = lhs; -// LLWString::toLower(rhs_lower); -// std::string rhs_lower = wstring_to_utf8str(rhs_lower); -// -// return strcmp(lhs_lower.c_str(), rhs_lower.c_str()); -//#endif -//} - - std::string utf8str_trim(const std::string& utf8str) { LLWString wstr = utf8str_to_wstring(utf8str); - LLWString::trim(wstr); + LLWStringUtil::trim(wstr); return wstring_to_utf8str(wstr); } @@ -703,7 +511,7 @@ std::string utf8str_trim(const std::string& utf8str) std::string utf8str_tolower(const std::string& utf8str) { LLWString out_str = utf8str_to_wstring(utf8str); - LLWString::toLower(out_str); + LLWStringUtil::toLower(out_str); return wstring_to_utf8str(out_str); } @@ -712,7 +520,7 @@ S32 utf8str_compare_insensitive(const std::string& lhs, const std::string& rhs) { LLWString wlhs = utf8str_to_wstring(lhs); LLWString wrhs = utf8str_to_wstring(rhs); - return LLWString::compareInsensitive(wlhs.c_str(), wrhs.c_str()); + return LLWStringUtil::compareInsensitive(wlhs, wrhs); } std::string utf8str_truncate(const std::string& utf8str, const S32 max_len) @@ -756,7 +564,7 @@ std::string utf8str_substChar( const llwchar replace_char) { LLWString wstr = utf8str_to_wstring(utf8str); - LLWString::replaceChar(wstr, target_char, replace_char); + LLWStringUtil::replaceChar(wstr, target_char, replace_char); //wstr = wstring_substChar(wstr, target_char, replace_char); return wstring_to_utf8str(wstr); } @@ -764,7 +572,7 @@ std::string utf8str_substChar( std::string utf8str_makeASCII(const std::string& utf8str) { LLWString wstr = utf8str_to_wstring(utf8str); - LLWString::_makeASCII(wstr); + LLWStringUtil::_makeASCII(wstr); return wstring_to_utf8str(wstr); } @@ -819,14 +627,14 @@ namespace snprintf_hack } } -std::string ll_convert_wide_to_string(const wchar_t* in) +std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page) { std::string out; if(in) { int len_in = wcslen(in); int len_out = WideCharToMultiByte( - CP_ACP, + code_page, 0, in, len_in, @@ -841,7 +649,7 @@ std::string ll_convert_wide_to_string(const wchar_t* in) if(pout) { WideCharToMultiByte( - CP_ACP, + code_page, 0, in, len_in, @@ -855,8 +663,56 @@ std::string ll_convert_wide_to_string(const wchar_t* in) } return out; } + +wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page) +{ + // From review: + // We can preallocate a wide char buffer that is the same length (in wchar_t elements) as the utf8 input, + // plus one for a null terminator, and be guaranteed to not overflow. + + // Normally, I'd call that sort of thing premature optimization, + // but we *are* seeing string operations taking a bunch of time, especially when constructing widgets. +// int output_str_len = MultiByteToWideChar(code_page, 0, in.c_str(), in.length(), NULL, 0); + + // reserve place to NULL terminator + int output_str_len = in.length(); + wchar_t* w_out = new wchar_t[output_str_len + 1]; + + memset(w_out, 0, output_str_len + 1); + int real_output_str_len = MultiByteToWideChar (code_page, 0, in.c_str(), in.length(), w_out, output_str_len); + + //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858. + w_out[real_output_str_len] = 0; + + return w_out; +} + +std::string ll_convert_string_to_utf8_string(const std::string& in) +{ + wchar_t* w_mesg = ll_convert_string_to_wide(in, CP_ACP); + std::string out_utf8(ll_convert_wide_to_string(w_mesg, CP_UTF8)); + delete[] w_mesg; + + return out_utf8; +} #endif // LL_WINDOWS +long LLStringOps::sPacificTimeOffset = 0; +long LLStringOps::sLocalTimeOffset = 0; +bool LLStringOps::sPacificDaylightTime = 0; +std::map<std::string, std::string> LLStringOps::datetimeToCodes; + +std::vector<std::string> LLStringOps::sWeekDayList; +std::vector<std::string> LLStringOps::sWeekDayShortList; +std::vector<std::string> LLStringOps::sMonthList; +std::vector<std::string> LLStringOps::sMonthShortList; + + +std::string LLStringOps::sDayFormat; +std::string LLStringOps::sAM; +std::string LLStringOps::sPM; + + S32 LLStringOps::collate(const llwchar* a, const llwchar* b) { #if LL_WINDOWS @@ -868,9 +724,108 @@ S32 LLStringOps::collate(const llwchar* a, const llwchar* b) #endif } +void LLStringOps::setupDatetimeInfo (bool daylight) +{ + time_t nowT, localT, gmtT; + struct tm * tmpT; + + nowT = time (NULL); + + tmpT = localtime (&nowT); + localT = mktime (tmpT); + + tmpT = gmtime (&nowT); + gmtT = mktime (tmpT); + + sLocalTimeOffset = (long) (gmtT - localT); + + + sPacificDaylightTime = daylight; + sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60; + + datetimeToCodes["wkday"] = "%a"; // Thu + datetimeToCodes["weekday"] = "%A"; // Thursday + datetimeToCodes["year4"] = "%Y"; // 2009 + datetimeToCodes["year"] = "%Y"; // 2009 + datetimeToCodes["year2"] = "%y"; // 09 + datetimeToCodes["mth"] = "%b"; // Aug + datetimeToCodes["month"] = "%B"; // August + datetimeToCodes["mthnum"] = "%m"; // 08 + datetimeToCodes["day"] = "%d"; // 31 + datetimeToCodes["sday"] = "%-d"; // 9 + datetimeToCodes["hour24"] = "%H"; // 14 + datetimeToCodes["hour"] = "%H"; // 14 + datetimeToCodes["hour12"] = "%I"; // 02 + datetimeToCodes["min"] = "%M"; // 59 + datetimeToCodes["ampm"] = "%p"; // AM + datetimeToCodes["second"] = "%S"; // 59 + datetimeToCodes["timezone"] = "%Z"; // PST +} + +void tokenizeStringToArray(const std::string& data, std::vector<std::string>& output) +{ + output.clear(); + size_t length = data.size(); + + // tokenize it and put it in the array + std::string cur_word; + for(size_t i = 0; i < length; ++i) + { + if(data[i] == ':') + { + output.push_back(cur_word); + cur_word.clear(); + } + else + { + cur_word.append(1, data[i]); + } + } + output.push_back(cur_word); +} + +void LLStringOps::setupWeekDaysNames(const std::string& data) +{ + tokenizeStringToArray(data,sWeekDayList); +} +void LLStringOps::setupWeekDaysShortNames(const std::string& data) +{ + tokenizeStringToArray(data,sWeekDayShortList); +} +void LLStringOps::setupMonthNames(const std::string& data) +{ + tokenizeStringToArray(data,sMonthList); +} +void LLStringOps::setupMonthShortNames(const std::string& data) +{ + tokenizeStringToArray(data,sMonthShortList); +} +void LLStringOps::setupDayFormat(const std::string& data) +{ + sDayFormat = data; +} + + +std::string LLStringOps::getDatetimeCode (std::string key) +{ + std::map<std::string, std::string>::iterator iter; + + iter = datetimeToCodes.find (key); + if (iter != datetimeToCodes.end()) + { + return iter->second; + } + else + { + return std::string(""); + } +} + + namespace LLStringFn { - void replace_nonprintable(std::basic_string<char>& string, char replacement) + // NOTE - this restricts output to ascii + void replace_nonprintable_in_ascii(std::basic_string<char>& string, char replacement) { const char MIN = 0x20; std::basic_string<char>::size_type len = string.size(); @@ -883,23 +838,9 @@ namespace LLStringFn } } - void replace_nonprintable( - std::basic_string<llwchar>& string, - llwchar replacement) - { - const llwchar MIN = 0x20; - const llwchar MAX = 0x7f; - std::basic_string<llwchar>::size_type len = string.size(); - for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii) - { - if((string[ii] < MIN) || (string[ii] > MAX)) - { - string[ii] = replacement; - } - } - } - void replace_nonprintable_and_pipe(std::basic_string<char>& str, + // NOTE - this restricts output to ascii + void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str, char replacement) { const char MIN = 0x20; @@ -914,23 +855,456 @@ namespace LLStringFn } } - void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str, - llwchar replacement) + // https://wiki.lindenlab.com/wiki/Unicode_Guidelines has details on + // allowable code points for XML. Specifically, they are: + // 0x09, 0x0a, 0x0d, and 0x20 on up. JC + std::string strip_invalid_xml(const std::string& instr) { - const llwchar MIN = 0x20; - const llwchar MAX = 0x7f; - const llwchar PIPE = 0x7c; - std::basic_string<llwchar>::size_type len = str.size(); - for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii) + std::string output; + output.reserve( instr.size() ); + std::string::const_iterator it = instr.begin(); + while (it != instr.end()) { - if( (str[ii] < MIN) || (str[ii] > MAX) || (str[ii] == PIPE) ) + // Must compare as unsigned for >= + // Test most likely match first + const unsigned char c = (unsigned char)*it; + if ( c >= (unsigned char)0x20 // SPACE + || c == (unsigned char)0x09 // TAB + || c == (unsigned char)0x0a // LINE_FEED + || c == (unsigned char)0x0d ) // CARRIAGE_RETURN { - str[ii] = replacement; + output.push_back(c); + } + ++it; + } + return output; + } + + /** + * @brief Replace all control characters (c < 0x20) with replacement in + * string. + */ + void replace_ascii_controlchars(std::basic_string<char>& string, char replacement) + { + const unsigned char MIN = 0x20; + std::basic_string<char>::size_type len = string.size(); + for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii) + { + const unsigned char c = (unsigned char) string[ii]; + if(c < MIN) + { + string[ii] = replacement; } } } } +//////////////////////////////////////////////////////////// + +// Forward specialization of LLStringUtil::format before use in LLStringUtil::formatDatetime. +template<> +S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions); + +//static +template<> +void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims) +{ + std::string currToken; + std::string::size_type begIdx, endIdx; + + begIdx = instr.find_first_not_of (delims); + while (begIdx != std::string::npos) + { + endIdx = instr.find_first_of (delims, begIdx); + if (endIdx == std::string::npos) + { + endIdx = instr.length(); + } + + currToken = instr.substr(begIdx, endIdx - begIdx); + LLStringUtil::trim (currToken); + tokens.push_back(currToken); + begIdx = instr.find_first_not_of (delims, endIdx); + } +} + +template<> +LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr, size_type& start, std::vector<std::string>& tokens) +{ + const std::string delims (","); + + // Find the first ] + size_type pos2 = instr.find(']', start); + if (pos2 == std::string::npos) + return std::string::npos; + + // Find the last [ before ] + size_type pos1 = instr.find_last_of('[', pos2-1); + if (pos1 == std::string::npos || pos1 < start) + return std::string::npos; + + getTokens(std::string(instr,pos1+1,pos2-pos1-1), tokens, delims); + start = pos2+1; + + return pos1; +} + +// static +template<> +bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const format_map_t& substitutions) +{ + // see if we have a replacement for the bracketed string (without the brackets) + // test first using has() because if we just look up with operator[] we get back an + // empty string even if the value is missing. We want to distinguish between + // missing replacements and deliberately empty replacement strings. + format_map_t::const_iterator iter = substitutions.find(token); + if (iter != substitutions.end()) + { + replacement = iter->second; + return true; + } + // if not, see if there's one WITH brackets + iter = substitutions.find(std::string("[" + token + "]")); + if (iter != substitutions.end()) + { + replacement = iter->second; + return true; + } + + return false; +} + +// static +template<> +bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const LLSD& substitutions) +{ + // see if we have a replacement for the bracketed string (without the brackets) + // test first using has() because if we just look up with operator[] we get back an + // empty string even if the value is missing. We want to distinguish between + // missing replacements and deliberately empty replacement strings. + if (substitutions.has(token)) + { + replacement = substitutions[token].asString(); + return true; + } + // if not, see if there's one WITH brackets + else if (substitutions.has(std::string("[" + token + "]"))) + { + replacement = substitutions[std::string("[" + token + "]")].asString(); + return true; + } + + return false; +} + +//static +template<> +void LLStringUtil::setLocale(std::string inLocale) +{ + sLocale = inLocale; +}; + +//static +template<> +std::string LLStringUtil::getLocale(void) +{ + return sLocale; +}; + +// static +template<> +void LLStringUtil::formatNumber(std::string& numStr, std::string decimals) +{ + std::stringstream strStream; + S32 intDecimals = 0; + + convertToS32 (decimals, intDecimals); + if (!sLocale.empty()) + { + // std::locale() throws if the locale is unknown! (EXT-7926) + try + { + strStream.imbue(std::locale(sLocale.c_str())); + } catch (const std::exception &) + { + LL_WARNS_ONCE("Locale") << "Cannot set locale to " << sLocale << LL_ENDL; + } + } + + if (!intDecimals) + { + S32 intStr; + + if (convertToS32(numStr, intStr)) + { + strStream << intStr; + numStr = strStream.str(); + } + } + else + { + F32 floatStr; + + if (convertToF32(numStr, floatStr)) + { + strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; + numStr = strStream.str(); + } + } +} + +// static +template<> +bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, + std::string param, S32 secFromEpoch) +{ + if (param == "local") // local + { + secFromEpoch -= LLStringOps::getLocalTimeOffset(); + } + else if (param != "utc") // slt + { + secFromEpoch -= LLStringOps::getPacificTimeOffset(); + } + + // if never fell into those two ifs above, param must be utc + if (secFromEpoch < 0) secFromEpoch = 0; + + LLDate datetime((F64)secFromEpoch); + std::string code = LLStringOps::getDatetimeCode (token); + + // special case to handle timezone + if (code == "%Z") { + if (param == "utc") + { + replacement = "GMT"; + } + else if (param == "local") + { + replacement = ""; // user knows their own timezone + } + else + { + // "slt" = Second Life Time, which is deprecated. + // If not utc or user local time, fallback to Pacific time + replacement = LLStringOps::getPacificDaylightTime() ? "PDT" : "PST"; + } + return true; + } + + //EXT-7013 + //few codes are not suppotred by strtime function (example - weekdays for Japanise) + //so use predefined ones + + //if sWeekDayList is not empty than current locale doesn't support + //weekday name. + time_t loc_seconds = (time_t) secFromEpoch; + if(LLStringOps::sWeekDayList.size() == 7 && code == "%A") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayList[gmt->tm_wday]; + } + else if(LLStringOps::sWeekDayShortList.size() == 7 && code == "%a") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayShortList[gmt->tm_wday]; + } + else if(LLStringOps::sMonthList.size() == 12 && code == "%B") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = LLStringOps::sWeekDayList[gmt->tm_mon]; + } + else if( !LLStringOps::sDayFormat.empty() && code == "%d" ) + { + struct tm * gmt = gmtime (&loc_seconds); + LLStringUtil::format_map_t args; + args["[MDAY]"] = llformat ("%d", gmt->tm_mday); + replacement = LLStringOps::sDayFormat; + LLStringUtil::format(replacement, args); + } + else if (code == "%-d") + { + struct tm * gmt = gmtime (&loc_seconds); + replacement = llformat ("%d", gmt->tm_mday); // day of the month without leading zero + } + else if( !LLStringOps::sAM.empty() && !LLStringOps::sPM.empty() && code == "%p" ) + { + struct tm * gmt = gmtime (&loc_seconds); + if(gmt->tm_hour<12) + { + replacement = LLStringOps::sAM; + } + else + { + replacement = LLStringOps::sPM; + } + } + else + { + replacement = datetime.toHTTPDateString(code); + } + + // *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format + // to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738). + // We could have used '%l' format instead, but it's not supported by Windows. + if(code == "%I" && token == "hour12" && replacement.at(0) == '0') + { + replacement = replacement.at(1); + } + + return !code.empty(); +} + +// LLStringUtil::format recogizes the following patterns. +// All substitutions *must* be encased in []'s in the input string. +// The []'s are optional in the substitution map. +// [FOO_123] +// [FOO,number,precision] +// [FOO,datetime,format] + + +// static +template<> +S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) +{ + LLFastTimer ft(FT_STRING_FORMAT); + S32 res = 0; + + std::string output; + std::vector<std::string> tokens; + + std::string::size_type start = 0; + std::string::size_type prev_start = 0; + std::string::size_type key_start = 0; + while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) + { + output += std::string(s, prev_start, key_start-prev_start); + prev_start = start; + + bool found_replacement = false; + std::string replacement; + + if (tokens.size() == 0) + { + found_replacement = false; + } + else if (tokens.size() == 1) + { + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + } + else if (tokens[1] == "number") + { + std::string param = "0"; + + if (tokens.size() > 2) param = tokens[2]; + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + if (found_replacement) formatNumber (replacement, param); + } + else if (tokens[1] == "datetime") + { + std::string param; + if (tokens.size() > 2) param = tokens[2]; + + format_map_t::const_iterator iter = substitutions.find("datetime"); + if (iter != substitutions.end()) + { + S32 secFromEpoch = 0; + BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch); + if (r) + { + found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch); + } + } + } + + if (found_replacement) + { + output += replacement; + res++; + } + else + { + // we had no replacement, use the string as is + // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" + output += std::string(s, key_start, start-key_start); + } + tokens.clear(); + } + // send the remainder of the string (with no further matches for bracketed names) + output += std::string(s, start); + s = output; + return res; +} + +//static +template<> +S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) +{ + LLFastTimer ft(FT_STRING_FORMAT); + S32 res = 0; + + if (!substitutions.isMap()) + { + return res; + } + + std::string output; + std::vector<std::string> tokens; + + std::string::size_type start = 0; + std::string::size_type prev_start = 0; + std::string::size_type key_start = 0; + while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) + { + output += std::string(s, prev_start, key_start-prev_start); + prev_start = start; + + bool found_replacement = false; + std::string replacement; + + if (tokens.size() == 0) + { + found_replacement = false; + } + else if (tokens.size() == 1) + { + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + } + else if (tokens[1] == "number") + { + std::string param = "0"; + + if (tokens.size() > 2) param = tokens[2]; + found_replacement = simpleReplacement (replacement, tokens[0], substitutions); + if (found_replacement) formatNumber (replacement, param); + } + else if (tokens[1] == "datetime") + { + std::string param; + if (tokens.size() > 2) param = tokens[2]; + + S32 secFromEpoch = (S32) substitutions["datetime"].asInteger(); + found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch); + } + + if (found_replacement) + { + output += replacement; + res++; + } + else + { + // we had no replacement, use the string as is + // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" + output += std::string(s, key_start, start-key_start); + } + tokens.clear(); + } + // send the remainder of the string (with no further matches for bracketed names) + output += std::string(s, start); + s = output; + return res; +} //////////////////////////////////////////////////////////// // Testing @@ -938,19 +1312,19 @@ namespace LLStringFn #ifdef _DEBUG template<class T> -void LLStringBase<T>::testHarness() +void LLStringUtilBase<T>::testHarness() { - LLString s1; + std::string s1; llassert( s1.c_str() == NULL ); llassert( s1.size() == 0 ); llassert( s1.empty() ); - LLString s2( "hello"); + std::string s2( "hello"); llassert( !strcmp( s2.c_str(), "hello" ) ); llassert( s2.size() == 5 ); llassert( !s2.empty() ); - LLString s3( s2 ); + std::string s3( s2 ); llassert( "hello" == s2 ); llassert( s2 == "hello" ); @@ -959,12 +1333,12 @@ void LLStringBase<T>::testHarness() llassert( "gello" != s2 ); llassert( s2 != "gello" ); - LLString s4 = s2; + std::string s4 = s2; llassert( !s4.empty() ); s4.empty(); llassert( s4.empty() ); - LLString s5(""); + std::string s5(""); llassert( s5.empty() ); llassert( isValidIndex(s5, 0) ); @@ -978,8 +1352,8 @@ void LLStringBase<T>::testHarness() llassert( s4 == "hello again!hello again!" ); - LLString s6 = s2 + " " + s2; - LLString s7 = s6; + std::string s6 = s2 + " " + s2; + std::string s7 = s6; llassert( s6 == s7 ); llassert( !( s6 != s7) ); llassert( !(s6 < s7) ); @@ -1002,10 +1376,10 @@ void LLStringBase<T>::testHarness() s2.insert( 1, "awn, don't yel"); llassert( s2 == "yawn, don't yell"); - LLString s8 = s2.substr( 6, 5 ); + std::string s8 = s2.substr( 6, 5 ); llassert( s8 == "don't" ); - LLString s9 = " \t\ntest \t\t\n "; + std::string s9 = " \t\ntest \t\t\n "; trim(s9); llassert( s9 == "test" ); @@ -1020,17 +1394,17 @@ void LLStringBase<T>::testHarness() llassert( s9 == "abc123&*(abc" ); - LLString s10( 10, 'x' ); + std::string s10( 10, 'x' ); llassert( s10 == "xxxxxxxxxx" ); - LLString s11( "monkey in the middle", 7, 2 ); + std::string s11( "monkey in the middle", 7, 2 ); llassert( s11 == "in" ); - LLString s12; //empty + std::string s12; //empty s12 += "foo"; llassert( s12 == "foo" ); - LLString s13; //empty + std::string s13; //empty s13 += 'f'; llassert( s13 == "f" ); } |