summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llstring.cpp')
-rw-r--r--indra/llcommon/llstring.cpp950
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" );
}