summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llstring.cpp')
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstring.cpp290
1 files changed, 238 insertions, 52 deletions
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 5f3d9d6582..f3b8999883 100644..100755
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -2,31 +2,25 @@
* @file llstring.cpp
* @brief String utility functions and the std::string class.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, 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://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * 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://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * 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.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,15 +28,15 @@
#include "llstring.h"
#include "llerror.h"
+#include "llfasttimer.h"
+#include "llsd.h"
#if LL_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#include <winsock2.h>
-#include <windows.h>
+#include "llwin32headerslean.h"
#include <winnls.h> // for WideCharToMultiByte
#endif
-LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
+LLTrace::BlockTimerStatHandle FT_STRING_FORMAT("String Format");
std::string ll_safe_string(const char* in)
@@ -53,10 +47,28 @@ std::string ll_safe_string(const char* in)
std::string ll_safe_string(const char* in, S32 maxlen)
{
- if(in) return std::string(in, maxlen);
+ if(in && maxlen > 0 ) return std::string(in, maxlen);
+
return std::string();
}
+bool is_char_hex(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return true;
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return true;
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return true;
+ }
+ return false; // uh - oh, not hex any more...
+}
+
U8 hex_as_nybble(char hex)
{
if((hex >= '0') && (hex <= '9'))
@@ -95,10 +107,10 @@ bool iswindividual(llwchar elem)
bool _read_file_into_string(std::string& str, const std::string& filename)
{
- llifstream ifs(filename, llifstream::binary);
+ llifstream ifs(filename.c_str(), llifstream::binary);
if (!ifs.is_open())
{
- llinfos << "Unable to open file " << filename << llendl;
+ LL_INFOS() << "Unable to open file " << filename << LL_ENDL;
return false;
}
@@ -176,7 +188,7 @@ S32 wchar_to_utf8chars(llwchar in_char, char* outchars)
}
else
{
- llwarns << "Invalid Unicode character " << cur_char << "!" << llendl;
+ LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL;
*outchars++ = LL_UNKNOWN_CHAR;
}
return outchars - base;
@@ -633,14 +645,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,
@@ -655,7 +667,7 @@ std::string ll_convert_wide_to_string(const wchar_t* in)
if(pout)
{
WideCharToMultiByte(
- CP_ACP,
+ code_page,
0,
in,
len_in,
@@ -669,6 +681,38 @@ 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;
@@ -676,6 +720,17 @@ 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
@@ -694,14 +749,17 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
nowT = time (NULL);
- tmpT = localtime (&nowT);
- localT = mktime (tmpT);
-
tmpT = gmtime (&nowT);
gmtT = mktime (tmpT);
+ tmpT = localtime (&nowT);
+ localT = mktime (tmpT);
+
sLocalTimeOffset = (long) (gmtT - localT);
-
+ if (tmpT->tm_isdst)
+ {
+ sLocalTimeOffset -= 60 * 60; // 1 hour
+ }
sPacificDaylightTime = daylight;
sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;
@@ -715,6 +773,7 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
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
@@ -724,6 +783,50 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
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;
@@ -819,26 +922,32 @@ namespace LLStringFn
////////////////////////////////////////////////////////////
+// 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)
+ // Starting at offset 0, scan forward for the next non-delimiter. We're
+ // done when the only characters left in 'instr' are delimiters.
+ for (std::string::size_type begIdx, endIdx = 0;
+ (begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; )
{
+ // Found a non-delimiter. After that, find the next delimiter.
endIdx = instr.find_first_of (delims, begIdx);
if (endIdx == std::string::npos)
{
+ // No more delimiters: this token extends to the end of the string.
endIdx = instr.length();
}
- currToken = instr.substr(begIdx, endIdx - begIdx);
+ // extract the token between begIdx and endIdx; substr() needs length
+ std::string currToken(instr.substr(begIdx, endIdx - begIdx));
LLStringUtil::trim (currToken);
tokens.push_back(currToken);
- begIdx = instr.find_first_not_of (delims, endIdx);
+ // next scan past delimiters starts at endIdx
}
}
@@ -847,13 +956,18 @@ LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr,
{
const std::string delims (",");
- // Find the first ]
- size_type pos2 = instr.find(']', start);
+ // Find the first [
+ size_type pos1 = instr.find('[', start);
+ if (pos1 == std::string::npos)
+ return std::string::npos;
+
+ //Find the first ] after the initial [
+ size_type pos2 = instr.find(']', pos1);
if (pos2 == std::string::npos)
return std::string::npos;
- // Find the last [ before ]
- size_type pos1 = instr.find_last_of('[', pos2-1);
+ // Find the last [ before ] in case of nested [[]]
+ pos1 = instr.find_last_of('[', pos2-1);
if (pos1 == std::string::npos || pos1 < start)
return std::string::npos;
@@ -911,6 +1025,20 @@ bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token
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)
@@ -921,7 +1049,14 @@ void LLStringUtil::formatNumber(std::string& numStr, std::string decimals)
convertToS32 (decimals, intDecimals);
if (!sLocale.empty())
{
- strStream.imbue (std::locale(sLocale.c_str()));
+ // 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)
@@ -984,7 +1119,58 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
}
return true;
}
- replacement = datetime.toHTTPDateString(code);
+
+ //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::sMonthList[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).
@@ -1009,7 +1195,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
template<>
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
{
- LLFastTimer ft(FT_STRING_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
S32 res = 0;
std::string output;
@@ -1082,7 +1268,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
template<>
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
{
- LLFastTimer ft(FT_STRING_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
S32 res = 0;
if (!substitutions.isMap())
@@ -1211,7 +1397,7 @@ void LLStringUtilBase<T>::testHarness()
s2.erase( 4, 1 );
llassert( s2 == "hell");
- s2.insert( 0, 'y' );
+ s2.insert( 0, "y" );
llassert( s2 == "yhell");
s2.erase( 1, 3 );
llassert( s2 == "yl");