From 449e36858e6f1a79d5bd1d5264cbdb6fe57c70ad Mon Sep 17 00:00:00 2001 From: Ansariel Date: Thu, 28 Jan 2016 13:37:13 +0100 Subject: [PATCH] Fixes for the chat log parser Don't make correct parsing of a chat log depend on FSSecondsinChatTimestamps. The chat log might contain different timestamp formats in random order, so try to get the correct match in either case. --- indra/newview/lllogchat.cpp | 129 +++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 23 deletions(-) diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index ccd7a61b3a..82dfef8a2c 100755 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -93,7 +93,12 @@ const static std::string MULTI_LINE_PREFIX(" "); */ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); -const static boost::regex TIMESTAMP_AND_STUFF_SEC("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}:\\d{2}\\]\\s+)?(.*)$"); +// Timestamps in chat +// Note: In contrast to the TIMESTAMP_AND_STUFF regex, for the TIMESTAMP_AND_STUFF_SEC regex the match +// on the timestamp is NOT optional. We need this to decide during parsing if we have timestamps or not. +const static boost::regex TIMESTAMP_AND_STUFF_SEC("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}:\\d{2}\\]\\s+)(.*)$"); +const static boost::regex TIMESTAMP_AND_SEC("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}:\\d{2}\\]).*"); +// /** * Regular expression suitable to match names like @@ -117,8 +122,10 @@ const static std::string NAME_TEXT_DIVIDER(": "); // is used for timestamps adjusting const static char* DATE_FORMAT("%Y/%m/%d %H:%M"); const static char* TIME_FORMAT("%H:%M"); +// Seconds in timestamps const static char* DATE_FORMAT_SEC("%Y/%m/%d %H:%M:%S"); const static char* TIME_FORMAT_SEC("%H:%M:%S"); +// const static int IDX_TIMESTAMP = 1; const static int IDX_STUFF = 2; @@ -143,18 +150,15 @@ public: LLLogChatTimeScanner() { // Note, date/time facets will be destroyed by string streams - if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) - { - mDateStream.imbue(std::locale(mDateStream.getloc(), new date_input_facet(DATE_FORMAT_SEC))); - mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_facet(TIME_FORMAT_SEC))); - mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_input_facet(DATE_FORMAT_SEC))); - } - else - { - mDateStream.imbue(std::locale(mDateStream.getloc(), new date_input_facet(DATE_FORMAT))); - mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_facet(TIME_FORMAT))); - mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_input_facet(DATE_FORMAT))); - } + mDateStream.imbue(std::locale(mDateStream.getloc(), new date_input_facet(DATE_FORMAT))); + mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_facet(TIME_FORMAT))); + mTimeStream.imbue(std::locale(mTimeStream.getloc(), new time_input_facet(DATE_FORMAT))); + + // Seconds in timestamps + mDateSecStream.imbue(std::locale(mDateSecStream.getloc(), new date_input_facet(DATE_FORMAT_SEC))); + mTimeSecStream.imbue(std::locale(mTimeSecStream.getloc(), new time_facet(TIME_FORMAT_SEC))); + mTimeSecStream.imbue(std::locale(mTimeSecStream.getloc(), new time_input_facet(DATE_FORMAT_SEC))); + // } date getTodayPacificDate() @@ -215,10 +219,61 @@ public: << LL_ENDL; } + // Seconds in timestamps + void checkAndCutOffDateWithSec(std::string& time_str) + { + // Cuts off the "%Y/%m/%d" from string for todays timestamps. + // Assume that passed string has at least "%H:%M:%S" time format. + date log_date(not_a_date_time); + date today(getTodayPacificDate()); + + // Parse the passed date + mDateSecStream.str(LLStringUtil::null); + mDateSecStream << time_str; + mDateSecStream >> log_date; + mDateSecStream.clear(); + + days zero_days(0); + days days_alive = today - log_date; + + if ( days_alive == zero_days ) + { + // Yep, today's so strip "%Y/%m/%d" info + ptime stripped_time(not_a_date_time); + + mTimeSecStream.str(LLStringUtil::null); + mTimeSecStream << time_str; + mTimeSecStream >> stripped_time; + mTimeSecStream.clear(); + + time_str.clear(); + + mTimeSecStream.str(LLStringUtil::null); + mTimeSecStream << stripped_time; + mTimeSecStream >> time_str; + mTimeSecStream.clear(); + } + + LL_DEBUGS("LLChatLogParser") + << " log_date: " + << log_date + << " today: " + << today + << " days alive: " + << days_alive + << " new time: " + << time_str + << LL_ENDL; + } + // private: std::stringstream mDateStream; std::stringstream mTimeStream; + // Seconds in timestamps + std::stringstream mDateSecStream; + std::stringstream mTimeSecStream; + // }; LLLogChat::save_history_signal_t * LLLogChat::sSaveHistorySignal = NULL; @@ -287,21 +342,23 @@ std::string LLLogChat::timestamp(bool withdate) + LLTrans::getString ("TimeDay") + "] [" + LLTrans::getString ("TimeHour") + "]:[" + LLTrans::getString ("TimeMin") + "]"; + // Seconds in timestamps if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) { - timeStr += ":[" - +LLTrans::getString ("TimeSec")+"]"; + timeStr += ":[" + LLTrans::getString ("TimeSec") + "]"; } + // } else { timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + LLTrans::getString ("TimeMin")+"]"; + // Seconds in timestamps if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) { - timeStr += ":[" - +LLTrans::getString ("TimeSec")+"]"; + timeStr += ":[" + LLTrans::getString ("TimeSec") + "]"; } + // } LLSD substitution; @@ -623,7 +680,10 @@ void LLLogChat::findTranscriptFiles(std::string pattern, std::vector matches; - if (boost::regex_match(std::string(buffer), matches, TIMESTAMP)) + // Seconds in timestamp + //if (boost::regex_match(std::string(buffer), matches, TIMESTAMP)) + if (boost::regex_match(std::string(buffer), matches, TIMESTAMP) || boost::regex_match(std::string(buffer), matches, TIMESTAMP_AND_SEC)) + // { list_of_transcriptions.push_back(gDirUtilp->add(dirname, filename)); } @@ -952,14 +1012,26 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params //matching a timestamp boost::match_results matches; - if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) + // Seconds in timestamps + // Logic here: First try to get a full match for timestamps with seconds. If we fail, + // this means we either have a timestamp without seconds or no timestamp at all. + // So we use the original regex for timestamps without seconds to find out what + // case it is. For this to work, the timestamp part in the TIMESTAMP_AND_STUFF_SEC + // regex is NOT optional! + //if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; + bool has_sec = false; + if (boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF_SEC)) { - if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF_SEC)) return false; + has_sec = true; } else { - if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) return false; + if (!boost::regex_match(raw, matches, TIMESTAMP_AND_STUFF)) + { + return false; + } } + // bool has_timestamp = matches[IDX_TIMESTAMP].matched; if (has_timestamp) @@ -972,7 +1044,17 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params if (cut_off_todays_date) { - LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + // Seconds in timestamps + //LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + if (has_sec) + { + LLLogChatTimeScanner::instance().checkAndCutOffDateWithSec(timestamp); + } + else + { + LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp); + } + // } im[LL_IM_TIME] = timestamp; @@ -997,7 +1079,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params bool has_name = name_and_text[IDX_NAME].matched; std::string name = name_and_text[IDX_NAME]; - // Ansariel: Handle the case an IM was stored in nearby chat history + // Handle the case an IM was stored in nearby chat history if (name == "IM:") { size_t divider_pos = stuff.find(NAME_TEXT_DIVIDER, 3); @@ -1008,6 +1090,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im, const LLSD& parse_params return true; } } + // //we don't need a name/text separator if (has_name && name.length() && name[name.length()-1] == ':')