LLStringOps, LLStringUtilBase, and LLDate updated to V2. If dates go wonky this is where it's from

This commit is contained in:
Shyotl
2011-05-15 22:50:56 -05:00
parent fe372028dc
commit 1a2d75fbe6
8 changed files with 833 additions and 117 deletions

View File

@@ -38,10 +38,13 @@
#include "apr_time.h"
#include <time.h>
#include <locale.h>
#include <string>
#include <iomanip>
#include <sstream>
#include "lltimer.h"
#include "llstring.h"
static const F64 DATE_EPOCH = 0.0;
@@ -88,47 +91,40 @@ std::string LLDate::asString() const
// is one of the standards used and the prefered format
std::string LLDate::asRFC1123() const
{
std::ostringstream stream;
toHTTPDateStream(stream);
return stream.str();
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
}
void LLDate::toHTTPDateStream(std::ostream& s) const
std::string LLDate::toHTTPDateString (std::string fmt) const
{
// http://apr.apache.org/docs/apr/0.9/group__apr__time.html
apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC);
time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * gmt = gmtime (&locSeconds);
return toHTTPDateString(gmt, fmt);
}
apr_time_exp_t exp_time ; //Apache time module
if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS)
{
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
{
// Return Epoch UTC date
s << "Thursday, 01 Jan 1970 00:00:00 GMT" ;
return;
}
s << std::dec << std::setfill('0');
#if( LL_WINDOWS || __GNUC__ > 2)
s << std::right ;
#else
s.setf(ios::right);
#endif
static char const* const weekdays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
static char const* const months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
std::string day = weekdays[exp_time.tm_wday];
std::string month = months[exp_time.tm_mon];
// avoid calling setlocale() unnecessarily - it's expensive.
static std::string prev_locale = "";
std::string this_locale = LLStringUtil::getLocale();
if (this_locale != prev_locale)
{
setlocale(LC_TIME, this_locale.c_str());
prev_locale = this_locale;
}
s << std::setw(day.length()) << (day)
<< ", " << std::setw(2) << (exp_time.tm_mday)
<< ' ' << std::setw(month.length()) << (month)
<< ' ' << std::setw(4) << (exp_time.tm_year + 1900)
<< ' ' << std::setw(2) << (exp_time.tm_hour)
<< ':' << std::setw(2) << (exp_time.tm_min)
<< ':' << std::setw(2) << (exp_time.tm_sec)
<< " GMT";
// RFC 1123 date does not use microseconds
//llinfos << "Date in RFC 1123 format is " << s << llendl;
// use strftime() as it appears to be faster than std::time_put
char buffer[128];
strftime(buffer, 128, fmt.c_str(), gmt);
std::string res(buffer);
#if LL_WINDOWS
// Convert from locale-dependant charset to UTF-8 (EXT-8524).
res = ll_convert_string_to_utf8_string(res);
#endif
return res;
}
void LLDate::toStream(std::ostream& s) const
@@ -159,7 +155,39 @@ void LLDate::toStream(std::ostream& s) const
s << '.' << std::setw(2)
<< (int)(exp_time.tm_usec / (LL_APR_USEC_PER_SEC / 100));
}
s << 'Z';
s << 'Z'
<< std::setfill(' ');
}
bool LLDate::split(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *min, S32 *sec) const
{
apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC);
apr_time_exp_t exp_time;
if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS)
{
return false;
}
if (year)
*year = exp_time.tm_year + 1900;
if (month)
*month = exp_time.tm_mon + 1;
if (day)
*day = exp_time.tm_mday;
if (hour)
*hour = exp_time.tm_hour;
if (min)
*min = exp_time.tm_min;
if (sec)
*sec = exp_time.tm_sec;
return true;
}
bool LLDate::fromString(const std::string& iso8601_date)
@@ -223,13 +251,62 @@ bool LLDate::fromStream(std::istream& s)
s >> fractional;
seconds_since_epoch += fractional;
}
c = s.get(); // skip the Z
if (c != 'Z') { return false; }
c = s.peek(); // check for offset
if (c == '+' || c == '-')
{
S32 offset_sign = (c == '+') ? 1 : -1;
S32 offset_hours = 0;
S32 offset_minutes = 0;
S32 offset_in_seconds = 0;
s >> offset_hours;
c = s.get(); // skip the colon a get the minutes if there are any
if (c == ':')
{
s >> offset_minutes;
}
offset_in_seconds = (offset_hours * 60 + offset_sign * offset_minutes) * 60;
seconds_since_epoch -= offset_in_seconds;
}
else if (c != 'Z') { return false; } // skip the Z
mSecondsSinceEpoch = seconds_since_epoch;
return true;
}
bool LLDate::fromYMDHMS(S32 year, S32 month, S32 day, S32 hour, S32 min, S32 sec)
{
struct apr_time_exp_t exp_time;
exp_time.tm_year = year - 1900;
exp_time.tm_mon = month - 1;
exp_time.tm_mday = day;
exp_time.tm_hour = hour;
exp_time.tm_min = min;
exp_time.tm_sec = sec;
// zero out the unused fields
exp_time.tm_usec = 0;
exp_time.tm_wday = 0;
exp_time.tm_yday = 0;
exp_time.tm_isdst = 0;
exp_time.tm_gmtoff = 0;
// generate a time_t from that
apr_time_t time;
if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS)
{
return false;
}
mSecondsSinceEpoch = time / LL_APR_USEC_PER_SEC;
return true;
}
F64 LLDate::secondsSinceEpoch() const
{
return mSecondsSinceEpoch;

View File

@@ -84,7 +84,9 @@ public:
std::string asString() const;
std::string asRFC1123() const;
void toStream(std::ostream&) const;
void toHTTPDateStream(std::ostream&) const;
bool split(S32 *year, S32 *month = NULL, S32 *day = NULL, S32 *hour = NULL, S32 *min = NULL, S32 *sec = NULL) const;
std::string toHTTPDateString (std::string fmt) const;
static std::string toHTTPDateString (tm * gmt, std::string fmt);
/**
* @brief Set the date from an ISO-8601 string.
*
@@ -99,6 +101,7 @@ public:
*/
bool fromString(const std::string& iso8601_date);
bool fromStream(std::istream&);
bool fromYMDHMS(S32 year, S32 month = 1, S32 day = 0, S32 hour = 0, S32 min = 0, S32 sec = 0);
/**
* @brief Return the date in seconds since epoch.

View File

@@ -51,6 +51,9 @@ public:
FTM_IDLE,
FTM_SLEEP,
// general timers
FT_STRING_FORMAT,
// common messaging components
FTM_PUMP,
FTM_CURL,

View File

@@ -34,6 +34,7 @@
#include "llstring.h"
#include "llerror.h"
#include "llfasttimer.h"
#if LL_WINDOWS
#define WIN32_LEAN_AND_MEAN
@@ -71,6 +72,24 @@ 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 std::string& filename)
{
@@ -630,14 +649,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,
@@ -652,7 +671,7 @@ std::string ll_convert_wide_to_string(const wchar_t* in)
if(pout)
{
WideCharToMultiByte(
CP_ACP,
code_page,
0,
in,
len_in,
@@ -666,8 +685,55 @@ 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
@@ -679,6 +745,107 @@ 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 = 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;
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
{
// NOTE - this restricts output to ascii
@@ -715,12 +882,12 @@ namespace LLStringFn
// 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& input)
std::string strip_invalid_xml(const std::string& instr)
{
std::string output;
output.reserve( input.size() );
std::string::const_iterator it = input.begin();
while (it != input.end())
output.reserve( instr.size() );
std::string::const_iterator it = instr.begin();
while (it != instr.end())
{
// Must compare as unsigned for >=
// Test most likely match first
@@ -756,6 +923,412 @@ 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)
{
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::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).
// 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(LLFastTimer::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(LLFastTimer::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

View File

@@ -37,6 +37,9 @@
#include <cstdio>
#include <algorithm>
#include <map>
#include <locale>
#include <iomanip>
#include "llsd.h"
#if LL_LINUX || LL_SOLARIS
#include <wctype.h>
@@ -148,7 +151,23 @@ struct char_traits<U16>
class LL_COMMON_API LLStringOps
{
private:
static long sPacificTimeOffset;
static long sLocalTimeOffset;
static bool sPacificDaylightTime;
static std::map<std::string, std::string> datetimeToCodes;
public:
static std::vector<std::string> sWeekDayList;
static std::vector<std::string> sWeekDayShortList;
static std::vector<std::string> sMonthList;
static std::vector<std::string> sMonthShortList;
static std::string sDayFormat;
static std::string sAM;
static std::string sPM;
static char toUpper(char elem) { return toupper((unsigned char)elem); }
static llwchar toUpper(llwchar elem) { return towupper(elem); }
@@ -177,14 +196,31 @@ public:
static S32 collate(const llwchar* a, const llwchar* b);
static bool isHexString(const std::string& str);
static void setupDatetimeInfo(bool pacific_daylight_time);
static void setupWeekDaysNames(const std::string& data);
static void setupWeekDaysShortNames(const std::string& data);
static void setupMonthNames(const std::string& data);
static void setupMonthShortNames(const std::string& data);
static void setupDayFormat(const std::string& data);
static long getPacificTimeOffset(void) { return sPacificTimeOffset;}
static long getLocalTimeOffset(void) { return sLocalTimeOffset;}
// Is the Pacific time zone (aka server time zone)
// currently in daylight savings time?
static bool getPacificDaylightTime(void) { return sPacificDaylightTime;}
static std::string getDatetimeCode (std::string key);
};
/**
* @brief Return a string constructed from in without crashing if the
* pointer is NULL.
*/
std::string LL_COMMON_API ll_safe_string(const char* in);
std::string LL_COMMON_API ll_safe_string(const char* in, S32 maxlen);
LL_COMMON_API std::string ll_safe_string(const char* in);
LL_COMMON_API std::string ll_safe_string(const char* in, S32 maxlen);
// Allowing assignments from non-strings into format_map_t is apparently
@@ -206,6 +242,9 @@ private:
template <class T>
class LLStringUtilBase
{
private:
static std::string sLocale;
public:
typedef typename std::basic_string<T>::size_type size_type;
@@ -213,10 +252,18 @@ public:
/////////////////////////////////////////////////////////////////////////////////////////
// Static Utility functions that operate on std::strings
static std::basic_string<T> const null;
static const std::basic_string<T> null;
typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
LL_COMMON_API static void setLocale (std::string inLocale);
LL_COMMON_API static std::string getLocale (void);
static bool isValidIndex(const std::basic_string<T>& string, size_type i)
{
@@ -233,7 +280,25 @@ public:
// True if this is the head of s.
static BOOL isHead( const std::basic_string<T>& string, const T* s );
/**
* @brief Returns true if string starts with substr
*
* If etither string or substr are empty, this method returns false.
*/
static bool startsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr);
/**
* @brief Returns true if string ends in substr
*
* If etither string or substr are empty, this method returns false.
*/
static bool endsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr);
static void addCRLF(std::basic_string<T>& string);
static void removeCRLF(std::basic_string<T>& string);
@@ -298,13 +363,19 @@ public:
// Copies src into dst at a given offset.
static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
#ifdef _DEBUG
static void testHarness();
LL_COMMON_API static void testHarness();
#endif
private:
LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
};
template<class T> std::basic_string<T> const LLStringUtilBase<T>::null;
template<class T> const std::basic_string<T> LLStringUtilBase<T>::null;
template<class T> std::string LLStringUtilBase<T>::sLocale;
typedef LLStringUtilBase<char> LLStringUtil;
typedef LLStringUtilBase<llwchar> LLWStringUtil;
@@ -366,6 +437,7 @@ LL_COMMON_API U8 hex_as_nybble(char hex);
* @return Returns true on success. If false, str is unmodified.
*/
LL_COMMON_API bool _read_file_into_string(std::string& str, const std::string& filename);
LL_COMMON_API bool iswindividual(llwchar elem);
/**
* Unicode support
@@ -495,7 +567,20 @@ using snprintf_hack::snprintf;
*
* This replaces the unsafe W2A macro from ATL.
*/
LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in);
LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page);
/**
* Converts a string to wide string.
*
* It will allocate memory for result string with "new []". Don't forget to release it with "delete []".
*/
LL_COMMON_API wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page);
/**
* Converts incoming string into urf8 string
*
*/
LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in);
//@}
#endif // LL_WINDOWS
@@ -558,63 +643,12 @@ namespace LLStringFn
}
////////////////////////////////////////////////////////////
// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
// There is no LLWStringUtil::format implementation currently.
// Calling thse for anything other than LLStringUtil will produce link errors.
// LLStringBase::format()
//
// This function takes a string 's' and a map 'fmt_map' of strings-to-strings.
// All occurances of strings in 's' from the left-hand side of 'fmt_map' are
// then replaced with the corresponding right-hand side of 'fmt_map', non-
// recursively. The function returns the number of substitutions made.
////////////////////////////////////////////////////////////
// static
template<class T>
S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map)
{
typedef typename std::basic_string<T>::size_type string_size_type_t;
string_size_type_t scanstart = 0;
S32 res = 0;
// Look for the first match of any keyword, replace that keyword,
// repeat from the end of the replacement string. This avoids
// accidentally performing substitution on a substituted string.
while (1)
{
string_size_type_t first_match_pos = scanstart;
string_size_type_t first_match_str_length = 0;
std::basic_string<T> first_match_str_replacement;
for (format_map_t::const_iterator iter = fmt_map.begin();
iter != fmt_map.end();
++iter)
{
string_size_type_t n = s.find(iter->first, scanstart);
if (n != std::basic_string<T>::npos &&
(n < first_match_pos ||
0 == first_match_str_length))
{
first_match_pos = n;
first_match_str_length = iter->first.length();
first_match_str_replacement = iter->second;
}
}
if (0 == first_match_str_length)
{
// no more keys found to substitute from this point
// in the string forward.
break;
}
else
{
s.erase(first_match_pos, first_match_str_length);
s.insert(first_match_pos, first_match_str_replacement);
scanstart = first_match_pos +
first_match_str_replacement.length();
++res;
}
}
return res;
}
// static
template<class T>
@@ -1003,14 +1037,15 @@ void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
{
return;
}
char* c_string = new char[string.size() + 1];
size_t src_size = string.size();
char* c_string = new char[src_size + 1];
if(c_string == NULL)
{
return;
}
strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/
copy(c_string, string.c_str(), src_size+1);
char* write_head = &c_string[0];
for (size_type i = 0; i < string.size(); i++)
for (size_type i = 0; i < src_size; i++)
{
char* read_head = &string[i];
write_head = &c_string[j];
@@ -1090,6 +1125,30 @@ BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s
}
}
// static
template<class T>
bool LLStringUtilBase<T>::startsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr)
{
if(string.empty() || (substr.empty())) return false;
if(0 == string.find(substr)) return true;
return false;
}
// static
template<class T>
bool LLStringUtilBase<T>::endsWith(
const std::basic_string<T>& string,
const std::basic_string<T>& substr)
{
if(string.empty() || (substr.empty())) return false;
std::string::size_type idx = string.rfind(substr);
if(std::string::npos == idx) return false;
return (idx == (string.size() - substr.size()));
}
template<class T>
BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
{

View File

@@ -81,6 +81,7 @@ static struct ft_display_info ft_display_table[] =
{ LLFastTimer::FTM_MESSAGES, " System Messages", &LLColor4::grey1, 1 },
{ LLFastTimer::FTM_MOUSEHANDLER, " Mouse", &LLColor4::grey1, 0 },
{ LLFastTimer::FTM_KEYHANDLER, " Keyboard", &LLColor4::grey1, 0 },
{ LLFastTimer::FT_STRING_FORMAT, " String Format", &LLColor4::grey3, 0 },
{ LLFastTimer::FTM_SLEEP, " Sleep", &LLColor4::grey2, 0 },
{ LLFastTimer::FTM_IDLE, " Idle", &blue0, 0 },
{ LLFastTimer::FTM_PUMP, " Pump", &LLColor4::magenta2, 1 },

View File

@@ -429,7 +429,7 @@ void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record,
if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr))
{
// Save module's address and full path.
tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name);
tmp_info["CallStack"][Ret_Addr_I]["ModuleName"] = ll_convert_wide_to_string(Module_Name,CP_ACP);
tmp_info["CallStack"][Ret_Addr_I]["ModuleAddress"] = (int)Module_Addr;
tmp_info["CallStack"][Ret_Addr_I]["CallOffset"] = (int)(Ebp->Ret_Addr - Module_Addr);
@@ -548,7 +548,7 @@ LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
Get_Version_Str(info);
GetModuleFileName(NULL, Str, MAX_PATH);
info["Process"] = ll_convert_wide_to_string(Str);
info["Process"] = ll_convert_wide_to_string(Str,CP_ACP);
info["ThreadID"] = (S32)GetCurrentThreadId();
// If exception occurred.
@@ -560,7 +560,7 @@ LLSD WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)
// If module with E.ExceptionAddress found - save its path and date.
if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))
{
info["Module"] = ll_convert_wide_to_string(Module_Name);
info["Module"] = ll_convert_wide_to_string(Module_Name,CP_ACP);
if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)

View File

@@ -145,7 +145,7 @@ void LLCrashLoggerWindows::ProcessCaption(HWND hWnd)
TCHAR header[MAX_STRING];
std::string final;
GetWindowText(hWnd, templateText, sizeof(templateText));
final = llformat(ll_convert_wide_to_string(templateText).c_str(), gProductName.c_str());
final = llformat(ll_convert_wide_to_string(templateText,CP_ACP).c_str(), gProductName.c_str());
ConvertLPCSTRToLPWSTR(final.c_str(), header);
SetWindowText(hWnd, header);
}
@@ -158,7 +158,7 @@ void LLCrashLoggerWindows::ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
TCHAR header[MAX_STRING];
std::string final;
GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
final = llformat(ll_convert_wide_to_string(templateText).c_str(), gProductName.c_str());
final = llformat(ll_convert_wide_to_string(templateText,CP_ACP).c_str(), gProductName.c_str());
ConvertLPCSTRToLPWSTR(final.c_str(), header);
SetDlgItemText(hWnd, nIDDlgItem, header);
}
@@ -201,7 +201,7 @@ bool handle_button_click(WORD button_id)
wbuffer, // pointer to buffer for text
20000 // maximum size of string
);
std::string user_text(ll_convert_wide_to_string(wbuffer));
std::string user_text(ll_convert_wide_to_string(wbuffer,CP_ACP));
// Activate and show the window.
ShowWindow(gHwndProgress, SW_SHOW);
// Try doing this second to make the progress window go frontmost.