phoenix-firestorm/indra/newview/bugsplatattributes.cpp

142 lines
4.0 KiB
C++

#include "bugsplatattributes.h"
#include "llstring.h"
#include <fstream>
#include <filesystem>
std::wstring BugSplatAttributes::mCrashContextFileName;
BugSplatAttributes& BugSplatAttributes::instance()
{
static BugSplatAttributes sInstance;
return sInstance;
}
#include <string>
#include <cctype>
std::wstring BugSplatAttributes::to_xml_token(const std::wstring& input)
{
// Example usage:
// std::wstring token = to_xml_token(L"Bandwidth (kbit/s)");
// The result should be: L"Bandwidth_kbit_per_s"
std::wstring result;
result.reserve(input.size() * 2); // Reserve some space, since we might insert more chars for "/"
for (wchar_t ch : input)
{
if (ch == L'/')
{
// Replace '/' with "_per_"
result.append(L"_per_");
}
else if (std::iswalnum(ch) || ch == L'_' || ch == L'-')
{
// Letters, digits, underscore, and hyphen are okay
result.push_back(ch);
}
else if (ch == L' ' || ch == L'(' || ch == L')')
{
// Replace spaces and parentheses with underscore
result.push_back(L'_');
}
else
{
// Other characters map to underscore
result.push_back(L'_');
}
}
// Ensure the first character is a letter or underscore:
// If not, prepend underscore.
if (result.empty() || !(std::iswalpha(result.front()) || result.front() == L'_'))
{
result.insert(result.begin(), L'_');
}
// trim trailing underscores
while (!result.empty() && result.back() == L'_')
{
result.pop_back();
}
return result;
}
bool BugSplatAttributes::writeToFile(const std::wstring& file_path)
{
std::lock_guard<std::mutex> lock(mMutex);
// Write to a temporary file first
#ifdef LL_WINDOWS
std::wstring tmp_file = file_path + L".tmp";
#else // use non-wide characters for Linux and Mac
std::string tmp_file = ll_convert_wide_to_string(file_path) + ".tmp";
#endif
{
#ifdef LL_WINDOWS
std::wofstream ofs(tmp_file, std::ios::out | std::ios::trunc);
#else // use non-wide characters for Linux and Mac
std::ofstream ofs(tmp_file, std::ios::out | std::ios::trunc);
#endif
if (!ofs.good())
{
return false;
}
ofs << L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
ofs << L"<XmlCrashContext>\n";
// First, write out attributes that have an empty category (top-level)
auto empty_category_it = mAttributes.find(L"");
if (empty_category_it != mAttributes.end())
{
for (const auto& kv : empty_category_it->second)
{
const std::wstring& key = kv.first;
const std::wstring& val = kv.second;
ofs << L" <" << key << L">" << val << L"</" << key << L">\n";
}
}
// Write out all other categories
for (const auto& cat_pair : mAttributes)
{
const std::wstring& category = cat_pair.first;
// Skip empty category since we already handled it above
if (category.empty())
{
continue;
}
ofs << L" <" << category << L">\n";
for (const auto& kv : cat_pair.second)
{
const std::wstring& key = kv.first;
const std::wstring& val = kv.second;
ofs << L" <" << key << L">" << val << L"</" << key << L">\n";
}
ofs << L" </" << category << L">\n";
}
ofs << L"</XmlCrashContext>\n";
// Close the file before renaming
ofs.close();
}
// Rename the temp file to the final file. If rename fails, leave the old file intact.
std::error_code ec;
std::filesystem::rename(tmp_file, file_path, ec);
if (ec)
{
// Rename failed, remove the temp file and return false
std::filesystem::remove(tmp_file, ec);
return false;
}
return true;
}