summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstring.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llstring.h')
-rw-r--r--indra/llcommon/llstring.h261
1 files changed, 224 insertions, 37 deletions
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 6ba665b8d2..813e2656ae 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -34,6 +34,10 @@
#define LL_LLSTRING_H
#include <string>
+#include <locale>
+#include <iomanip>
+#include <boost/regex.hpp>
+#include "llsd.h"
#if LL_LINUX || LL_SOLARIS
#include <wctype.h>
@@ -145,6 +149,12 @@ struct char_traits<U16>
class LLStringOps
{
+private:
+ static long sltOffset;
+ static long localTimeOffset;
+ static bool daylightSavings;
+ static std::map<std::string, std::string> datetimeToCodes;
+
public:
static char toUpper(char elem) { return toupper((unsigned char)elem); }
static llwchar toUpper(llwchar elem) { return towupper(elem); }
@@ -172,6 +182,12 @@ public:
static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
static S32 collate(const llwchar* a, const llwchar* b);
+
+ static void setupDatetimeInfo (bool daylight);
+ static long getSltOffset (void) {return sltOffset;}
+ static long getLocalTimeOffset (void) {return localTimeOffset;}
+ static bool getDaylightSavings (void) {return daylightSavings;}
+ static std::string getDatetimeCode (std::string key);
};
/**
@@ -201,6 +217,9 @@ private:
template <class T>
class LLStringUtilBase
{
+private:
+ static std::string sLocale;
+
public:
typedef typename std::basic_string<T>::size_type size_type;
@@ -211,7 +230,14 @@ public:
static std::basic_string<T> null;
typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
+ static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens);
+ static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
+ static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, const LLSD& substitutions);
+ static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
+ static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
+ static void setLocale (std::string inLocale) {sLocale = inLocale;};
+ static std::string getLocale (void) {return sLocale;};
static bool isValidIndex(const std::basic_string<T>& string, size_type i)
{
@@ -317,6 +343,7 @@ public:
};
template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
+template<class T> std::string LLStringUtilBase<T>::sLocale;
typedef LLStringUtilBase<char> LLStringUtil;
typedef LLStringUtilBase<llwchar> LLWStringUtil;
@@ -378,6 +405,7 @@ U8 hex_as_nybble(char hex);
* @return Returns true on success. If false, str is unmodified.
*/
bool _read_file_into_string(std::string& str, const std::string& filename);
+bool iswindividual(llwchar elem);
/**
* Unicode support
@@ -493,7 +521,14 @@ std::string utf8str_removeCRLF(const std::string& utf8str);
* formatted string.
*
*/
-int safe_snprintf(char* str, size_t size, const char* format, ...);
+
+// Deal with the differeneces on Windows
+namespace snprintf_hack
+{
+ LL_COMMON_API int snprintf(char *str, size_t size, const char *format, ...);
+}
+
+using snprintf_hack::snprintf;
/**
* @brief Convert a wide string to std::string
@@ -564,64 +599,216 @@ namespace LLStringFn
////////////////////////////////////////////////////////////
-// 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>
+void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens)
+{
+ const std::basic_string<T> delims (",");
+ std::basic_string<T> currToken;
+ size_type begIdx, endIdx;
+
+ begIdx = input.find_first_not_of (delims);
+ while (begIdx != std::basic_string<T>::npos)
+ {
+ endIdx = input.find_first_of (delims, begIdx);
+ if (endIdx == std::basic_string<T>::npos)
+ {
+ endIdx = input.length();
+ }
+
+ currToken = input.substr(begIdx, endIdx - begIdx);
+ trim (currToken);
+ tokens.push_back(currToken);
+ begIdx = input.find_first_not_of (delims, endIdx);
+ }
+}
// 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;
+ LLSD llsdMap;
+
+ for (format_map_t::const_iterator iter = fmt_map.begin();
+ iter != fmt_map.end();
+ ++iter)
+ {
+ llsdMap[iter->first] = iter->second;
+ }
+
+ return format (s, llsdMap);
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions)
+{
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)
+ if (!substitutions.isMap())
+ {
+ return res;
+ }
+
+ std::basic_ostringstream<T> output;
+ // match strings like [NAME,number,3]
+ const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
+
+
+ typename std::basic_string<T>::const_iterator start = s.begin();
+ typename std::basic_string<T>::const_iterator end = s.end();
+ boost::smatch match;
+
+
+ while (boost::regex_search(start, end, match, key, boost::match_default))
{
- 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;
+ bool found_replacement = false;
+ std::vector<std::basic_string<T> > tokens;
+ std::basic_string<T> replacement;
- for (format_map_t::const_iterator iter = fmt_map.begin();
- iter != fmt_map.end();
- ++iter)
+ getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
+
+ if (tokens.size() == 1)
{
- 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;
- }
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
}
+ else if (tokens[1] == "number")
+ {
+ std::basic_string<T> param = "0";
- if (0 == first_match_str_length)
+ 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")
{
- // no more keys found to substitute from this point
- // in the string forward.
- break;
+ std::basic_string<T> param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ found_replacement = formatDatetime (replacement, tokens[0], param, substitutions);
+ }
+
+ if (found_replacement)
+ {
+ output << std::basic_string<T>(start, match[0].first) << replacement;
+ res++;
}
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;
+ // we had no replacement, so leave the string we searched for so that it gets noticed by QA
+ // "hello [NAME_NOT_FOUND]" is output
+ output << std::basic_string<T>(start, match[0].second);
}
+
+ // update search position
+ start = match[0].second;
}
+ // send the remainder of the string (with no further matches for bracketed names)
+ output << std::basic_string<T>(start, end);
+ s = output.str();
return res;
}
// static
+template<class T>
+bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> 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::basic_string<T>("[" + token + "]")))
+ {
+ replacement = substitutions[std::basic_string<T>("[" + token + "]")].asString();
+ return true;
+ }
+
+ return false;
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals)
+{
+ typedef typename std::basic_string<T>::size_type string_size_type_t;
+ std::basic_stringstream<T> strStream;
+ S32 intDecimals = 0;
+
+ convertToS32 (decimals, intDecimals);
+ if (!sLocale.empty())
+ {
+ strStream.imbue (std::locale(sLocale.c_str()));
+ }
+
+ 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<class T>
+bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token,
+ std::basic_string<T> param, const LLSD& substitutions)
+{
+ S32 secFromEpoch = (long) substitutions["datetime"].asInteger();
+
+ if (param == "local") // local
+ {
+ secFromEpoch -= LLStringOps::getLocalTimeOffset();
+ }
+ else if (param != "utc") // slt
+ {
+ secFromEpoch -= LLStringOps::getSltOffset();
+ }
+
+ // if never fell into those two ifs above, param must be utc
+ if (secFromEpoch < 0) secFromEpoch = 0;
+
+ LLDate * datetime = new LLDate((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 = LLStringOps::getDaylightSavings()? "PDT" : "PST";
+ return true;
+ }
+
+ replacement = datetime->toHTTPDateString(code);
+ if (code.empty())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+// static
template<class T>
S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
{