summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorBrad Payne (Vir Linden) <vir@lindenlab.com>2014-04-08 13:20:23 -0400
committerBrad Payne (Vir Linden) <vir@lindenlab.com>2014-04-08 13:20:23 -0400
commitea7f34d37ac1861fc1e48e11ca5030e424a05a85 (patch)
treeef37281a22a1daec458eb8078287aad8a3af6063 /indra/llcommon
parentd10ecef615953bfa8739282958a62d4fac2c1290 (diff)
parent7e966f28da79d2d24f93a2615c8807421300700c (diff)
merge
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-xindra/llcommon/llapp.cpp242
-rwxr-xr-xindra/llcommon/llapp.h50
-rwxr-xr-xindra/llcommon/llerrorthread.cpp71
-rwxr-xr-xindra/llcommon/llfile.cpp31
-rwxr-xr-xindra/llcommon/llfile.h2
-rwxr-xr-xindra/llcommon/llstring.h5
-rwxr-xr-xindra/llcommon/stringize.h51
-rwxr-xr-xindra/llcommon/tests/stringize_test.cpp20
8 files changed, 246 insertions, 226 deletions
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 67a98d5fb8..5c8fff051f 100755
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -46,8 +46,8 @@
#include "llstl.h" // for DeletePointer()
#include "llstring.h"
#include "lleventtimer.h"
-
#include "google_breakpad/exception_handler.h"
+#include "stringize.h"
//
// Signal handling
@@ -55,6 +55,8 @@
// Windows uses structured exceptions, so it's handled a bit differently.
//
#if LL_WINDOWS
+#include "windows.h"
+
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);
bool windows_post_minidump_callback(const wchar_t* dump_path,
@@ -71,7 +73,9 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *);
#if LL_LINUX
#include "google_breakpad/minidump_descriptor.h"
-bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, void* context, bool succeeded);
+static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc,
+ void* context,
+ bool succeeded);
#else
// Called by breakpad exception handler after the minidump has been generated.
bool unix_post_minidump_callback(const char *dump_dir,
@@ -109,11 +113,6 @@ BOOL LLApp::sLogInSignal = FALSE;
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
LLAppErrorHandler LLApp::sErrorHandler = NULL;
BOOL LLApp::sErrorThreadRunning = FALSE;
-#if !LL_WINDOWS
-LLApp::child_map LLApp::sChildMap;
-LLAtomicU32* LLApp::sSigChildCount = NULL;
-LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
-#endif
LLApp::LLApp() : mThreadErrorp(NULL)
@@ -128,11 +127,6 @@ void LLApp::commonCtor()
LLCommon::initClass();
-#if !LL_WINDOWS
- // This must be initialized before the error handler.
- sSigChildCount = new LLAtomicU32(0);
-#endif
-
// initialize the options structure. We need to make this an array
// because the structured data will not auto-allocate if we
// reference an invalid location with the [] operator.
@@ -149,12 +143,13 @@ void LLApp::commonCtor()
// Set the application to this instance.
sApplication = this;
-
+
mExceptionHandler = 0;
// initialize the buffer to write the minidump filename to
// (this is used to avoid allocating memory in the crash handler)
- memset(minidump_path, 0, MAX_MINDUMP_PATH_LENGTH);
+ memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
+ mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
}
LLApp::LLApp(LLErrorThread *error_thread) :
@@ -166,10 +161,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :
LLApp::~LLApp()
{
-#if !LL_WINDOWS
- delete sSigChildCount;
- sSigChildCount = NULL;
-#endif
// reclaim live file memory
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
@@ -244,6 +235,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
}
++ii;
value.assign(argv[ii]);
+
+#if LL_WINDOWS
+ //Windows changed command line parsing. Deal with it.
+ S32 slen = value.length() - 1;
+ S32 start = 0;
+ S32 end = slen;
+ if (argv[ii][start]=='"')start++;
+ if (argv[ii][end]=='"')end--;
+ if (start!=0 || end!=slen)
+ {
+ value = value.substr (start,end);
+ }
+#endif
+
commands[name] = value;
}
setOptionData(PRIORITY_COMMAND_LINE, commands);
@@ -288,6 +293,32 @@ void LLApp::stepFrame()
mRunner.run();
}
+#if LL_WINDOWS
+//The following code is needed for 32-bit apps on 64-bit windows to keep it from eating
+//crashes. It is a lovely undocumented 'feature' in SP1 of Windows 7. An excellent
+//in-depth article on the issue may be found here: http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesn-work/
+void EnableCrashingOnCrashes()
+{
+ typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
+ typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
+ const DWORD EXCEPTION_SWALLOWING = 0x1;
+
+ HMODULE kernel32 = LoadLibraryA("kernel32.dll");
+ tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
+ "GetProcessUserModeExceptionPolicy");
+ tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
+ "SetProcessUserModeExceptionPolicy");
+ if (pGetPolicy && pSetPolicy)
+ {
+ DWORD dwFlags;
+ if (pGetPolicy(&dwFlags))
+ {
+ // Turn off the filter
+ pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
+ }
+ }
+}
+#endif
void LLApp::setupErrorHandling()
{
@@ -295,7 +326,10 @@ void LLApp::setupErrorHandling()
// occasionally checks to see if the app is in an error state, and sees if it needs to be run.
#if LL_WINDOWS
+
#if LL_SEND_CRASH_REPORTS
+ EnableCrashingOnCrashes();
+
// This sets a callback to handle w32 signals to the console window.
// The viewer shouldn't be affected, sicne its a windowed app.
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
@@ -304,8 +338,44 @@ void LLApp::setupErrorHandling()
if(mExceptionHandler == 0)
{
llwarns << "adding breakpad exception handler" << llendl;
- mExceptionHandler = new google_breakpad::ExceptionHandler(
- L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL);
+
+ std::wstring wpipe_name;
+ wpipe_name = mCrashReportPipeStr + wstringize(getPid());
+
+ const std::wstring wdump_path(wstringize(mDumpPath));
+
+ int retries = 30;
+ for (; retries > 0; --retries)
+ {
+ if (mExceptionHandler != 0) delete mExceptionHandler;
+
+ mExceptionHandler = new google_breakpad::ExceptionHandler(
+ wdump_path,
+ NULL, //No filter
+ windows_post_minidump_callback,
+ 0,
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ MiniDumpNormal, //Generate a 'normal' minidump.
+ wpipe_name.c_str(),
+ NULL); //No custom client info.
+ if (mExceptionHandler->IsOutOfProcess())
+ {
+ LL_INFOS("CRASHREPORT") << "Successfully attached to Out of Process exception handler." << LL_ENDL;
+ break;
+ }
+ else
+ {
+ LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler. " << retries << " retries remaining." << LL_ENDL;
+ ::Sleep(100); //Wait a tick and try again.
+ }
+ }
+
+ if (retries == 0) LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler." << LL_ENDL;
+
+ if (mExceptionHandler)
+ {
+ mExceptionHandler->set_handle_debug_exceptions(true);
+ }
}
#endif
#else
@@ -355,14 +425,17 @@ void LLApp::setupErrorHandling()
if(installHandler && (mExceptionHandler == 0))
{
- std::string dumpPath = "/tmp/";
- mExceptionHandler = new google_breakpad::ExceptionHandler(dumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
+ mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
}
#elif LL_LINUX
if(installHandler && (mExceptionHandler == 0))
{
- google_breakpad::MinidumpDescriptor desc("/tmp");
- new google_breakpad::ExceptionHandler(desc, 0, &unix_minidump_callback, 0, true, 0);
+ if (mDumpPath.empty())
+ {
+ mDumpPath = "/tmp";
+ }
+ google_breakpad::MinidumpDescriptor desc(mDumpPath);
+ mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);
}
#endif
@@ -418,19 +491,35 @@ void LLApp::setError()
void LLApp::setMiniDumpDir(const std::string &path)
{
+ if (path.empty())
+ {
+ mDumpPath = "/tmp";
+ }
+ else
+ {
+ mDumpPath = path;
+ }
+
if(mExceptionHandler == 0) return;
#ifdef LL_WINDOWS
wchar_t buffer[MAX_MINDUMP_PATH_LENGTH];
- mbstowcs(buffer, path.c_str(), MAX_MINDUMP_PATH_LENGTH);
+ mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH);
mExceptionHandler->set_dump_path(std::wstring(buffer));
#elif LL_LINUX
- google_breakpad::MinidumpDescriptor desc(path);
+ //google_breakpad::MinidumpDescriptor desc("/tmp"); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
+ google_breakpad::MinidumpDescriptor desc(mDumpPath); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
mExceptionHandler->set_minidump_descriptor(desc);
#else
- mExceptionHandler->set_dump_path(path);
+ mExceptionHandler->set_dump_path(mDumpPath);
#endif
}
+void LLApp::setDebugFileNames(const std::string &path)
+{
+ mStaticDebugFileName = path + "static_debug_info.log";
+ mDynamicDebugFileName = path + "dynamic_debug_info.log";
+}
+
void LLApp::writeMiniDump()
{
if(mExceptionHandler == 0) return;
@@ -507,34 +596,11 @@ bool LLApp::isCrashloggerDisabled()
return (sDisableCrashlogger == TRUE);
}
-#if !LL_WINDOWS
-// static
-U32 LLApp::getSigChildCount()
-{
- if (sSigChildCount)
- {
- return U32(*sSigChildCount);
- }
- return 0;
-}
-
-// static
-void LLApp::incSigChildCount()
-{
- if (sSigChildCount)
- {
- (*sSigChildCount)++;
- }
-}
-
-#endif
-
-
// static
int LLApp::getPid()
{
#if LL_WINDOWS
- return 0;
+ return GetCurrentProcessId();
#else
return getpid();
#endif
@@ -610,43 +676,6 @@ BOOL ConsoleCtrlHandler(DWORD fdwCtrlType)
}
#else //!LL_WINDOWS
-void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
-{
- LLChildInfo child_info;
- child_info.mCallback = callback;
- LLApp::sChildMap[pid] = child_info;
-}
-
-void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
-{
- LLApp::sDefaultChildCallback = callback;
-}
-
-pid_t LLApp::fork()
-{
- fflush(NULL); // flush all buffers before the child inherits them
- pid_t pid = ::fork();
- if( pid < 0 )
- {
- int system_error = errno;
- llwarns << "Unable to fork! Operating system error code: "
- << system_error << llendl;
- }
- else if (pid == 0)
- {
- // Sleep a bit to allow the parent to set up child callbacks.
- ms_sleep(10);
-
- // We need to disable signal handling, because we don't have a
- // signal handling thread anymore.
- setupErrorHandling();
- }
- else
- {
- llinfos << "Forked child process " << pid << llendl;
- }
- return pid;
-}
void setup_signals()
{
@@ -747,19 +776,6 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
}
- // Check result code for all child procs for which we've
- // registered callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT
- // w/o killing the child (Go, launcher!)
- // TODO: Now that we're using SIGACTION, we can actually
- // implement the launcher behavior to determine who sent the
- // SIGCHLD even if it doesn't result in child termination
- if (LLApp::sChildMap.count(info->si_pid))
- {
- LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
- }
-
- LLApp::incSigChildCount();
-
return;
case SIGABRT:
// Abort just results in termination of the app, no funky error handling.
@@ -880,21 +896,26 @@ bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_
// heap allocations in a crash handler.
// path format: <dump_dir>/<minidump_id>.dmp
- int dirPathLength = strlen(minidump_desc.path());
+
+ //HACK: *path points to the buffer in getMiniDumpFilename which has already allocated space
+ //to avoid doing allocation during crash.
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ int dir_path_len = strlen(path);
// The path must not be truncated.
- llassert((dirPathLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH);
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH - dir_path_len;
+
+ llassert( (remaining - strlen(minidump_desc.path())) > 5);
- char * path = LLApp::instance()->getMiniDumpFilename();
- S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
- strncpy(path, minidump_desc.path(), remaining);
- remaining -= dirPathLength;
- path += dirPathLength;
- if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
+ path += dir_path_len;
+
+ if (dir_path_len > 0 && path[-1] != '/')
{
*path++ = '/';
--remaining;
}
+
+ strncpy(path, minidump_desc.path(), remaining);
llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
LLApp::runErrorHandler();
@@ -942,7 +963,7 @@ bool unix_post_minidump_callback(const char *dump_dir,
strncpy(path, ".dmp", remaining);
}
- llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;
+ llinfos << "generated minidump: " << path << llendl;
LLApp::runErrorHandler();
#ifndef LL_RELEASE_FOR_DOWNLOAD
@@ -966,6 +987,7 @@ bool windows_post_minidump_callback(const wchar_t* dump_path,
S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
size_t bytesUsed;
+ LL_INFOS("MINIDUMPCALLBACK") << "Dump file was generated." << LL_ENDL;
bytesUsed = wcstombs(path, dump_path, static_cast<size_t>(remaining));
remaining -= bytesUsed;
path += bytesUsed;
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index afa06df23e..828965b1fa 100755
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -31,7 +31,6 @@
#include "llrun.h"
#include "llsd.h"
#include "lloptioninterface.h"
-
// Forward declarations
template <typename Type> class LLAtomic32;
typedef LLAtomic32<U32> LLAtomicU32;
@@ -42,7 +41,6 @@ class LLLiveFile;
#endif
typedef void (*LLAppErrorHandler)();
-typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
#if !LL_WINDOWS
extern S32 LL_SMACKDOWN_SIGNAL;
@@ -51,13 +49,6 @@ extern S32 LL_HEARTBEAT_SIGNAL;
// Clear all of the signal handlers (which we want to do for the child process when we fork
void clear_signals();
-class LLChildInfo
-{
-public:
- LLChildInfo() : mGotSigChild(FALSE), mCallback(NULL) {}
- BOOL mGotSigChild;
- LLAppChildCallback mCallback;
-};
#endif
namespace google_breakpad {
@@ -206,10 +197,6 @@ public:
static bool isQuitting();
static bool isError();
static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
-#if !LL_WINDOWS
- static U32 getSigChildCount();
- static void incSigChildCount();
-#endif
static int getPid();
/** @name Error handling methods */
@@ -238,32 +225,16 @@ public:
// change the directory where Breakpad minidump files are written to
void setMiniDumpDir(const std::string &path);
+ void setDebugFileNames(const std::string &path);
// Return the Google Breakpad minidump filename after a crash.
- char *getMiniDumpFilename() { return minidump_path; }
+ char *getMiniDumpFilename() { return mMinidumpPath; }
+ std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
+ std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
// Write out a Google Breakpad minidump file.
void writeMiniDump();
-#if !LL_WINDOWS
- //
- // Child process handling (Unix only for now)
- //
- // Set a callback to be run on exit of a child process
- // WARNING! This callback is run from the signal handler due to
- // Linux threading requiring waitpid() to be called from the thread that spawned the process.
- // At some point I will make this more behaved, but I'm not going to fix this right now - djs
- void setChildCallback(pid_t pid, LLAppChildCallback callback);
-
- // The child callback to run if no specific handler is set
- void setDefaultChildCallback(LLAppChildCallback callback);
-
- // Fork and do the proper signal handling/error handling mojo
- // *NOTE: You need to make sure your signal handling callback is
- // correct after you fork, because not all threads are duplicated
- // when you fork!
- pid_t fork();
-#endif
/**
* @brief Get a reference to the application runner
@@ -286,13 +257,9 @@ protected:
static EAppStatus sStatus; // Reflects current application status
static BOOL sErrorThreadRunning; // Set while the error thread is running
static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
+ std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.
-#if !LL_WINDOWS
- static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
- typedef std::map<pid_t, LLChildInfo> child_map; // Map key is a PID
- static child_map sChildMap;
- static LLAppChildCallback sDefaultChildCallback;
-#endif
+ std::string mDumpPath; //output path for google breakpad. Dependency workaround.
/**
* @brief This method is called once a frame to do once a frame tasks.
@@ -303,7 +270,10 @@ private:
void startErrorThread();
// Contains the filename of the minidump file after a crash.
- char minidump_path[MAX_MINDUMP_PATH_LENGTH];
+ char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
+
+ std::string mStaticDebugFileName;
+ std::string mDynamicDebugFileName;
// *NOTE: On Windows, we need a routine to reset the structured
// exception handler when some evil driver has taken it over for
diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp
index 950fcd6e83..ebfbe19ff7 100755
--- a/indra/llcommon/llerrorthread.cpp
+++ b/indra/llcommon/llerrorthread.cpp
@@ -109,79 +109,8 @@ void LLErrorThread::run()
llinfos << "thread_error - Waiting for an error" << llendl;
S32 counter = 0;
-#if !LL_WINDOWS
- U32 last_sig_child_count = 0;
-#endif
while (! (LLApp::isError() || LLApp::isStopped()))
{
-#if !LL_WINDOWS
- // Check whether or not the main thread had a sig child we haven't handled.
- U32 current_sig_child_count = LLApp::getSigChildCount();
- if (last_sig_child_count != current_sig_child_count)
- {
- int status = 0;
- pid_t child_pid = 0;
- last_sig_child_count = current_sig_child_count;
- if (LLApp::sLogInSignal)
- {
- llinfos << "thread_error handling SIGCHLD #" << current_sig_child_count << llendl;
- }
- for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin(); iter != LLApp::sChildMap.end();)
- {
- child_pid = iter->first;
- LLChildInfo &child_info = iter->second;
- // check the status of *all* children, in case we missed a signal
- if (0 != waitpid(child_pid, &status, WNOHANG))
- {
- bool exited = false;
- int exit_status = -1;
- get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
-
- if (child_info.mCallback)
- {
- if (LLApp::sLogInSignal)
- {
- llinfos << "Signal handler - Running child callback" << llendl;
- }
- child_info.mCallback(child_pid, exited, status);
- }
- LLApp::sChildMap.erase(iter++);
- }
- else
- {
- // Child didn't terminate, yet we got a sigchild somewhere...
- if (child_info.mGotSigChild && child_info.mCallback)
- {
- child_info.mCallback(child_pid, false, 0);
- }
- child_info.mGotSigChild = FALSE;
- iter++;
- }
- }
-
- // check the status of *all* children, in case we missed a signal
- // Same as above, but use the default child callback
- while(0 < (child_pid = waitpid( -1, &status, WNOHANG )))
- {
- if (0 != waitpid(child_pid, &status, WNOHANG))
- {
- bool exited = false;
- int exit_status = -1;
- get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
- if (LLApp::sDefaultChildCallback)
- {
- if (LLApp::sLogInSignal)
- {
- llinfos << "Signal handler - Running default child callback" << llendl;
- }
- LLApp::sDefaultChildCallback(child_pid, true, status);
- }
- }
- }
- }
-
-
-#endif
ms_sleep(10);
counter++;
}
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index c3a0f0bfe0..761d7f430c 100755
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -265,6 +265,37 @@ int LLFile::rename(const std::string& filename, const std::string& newname)
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
}
+bool LLFile::copy(const std::string from, const std::string to)
+{
+ bool copied = false;
+ LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
+ if (in)
+ {
+ LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
+ if (out)
+ {
+ char buf[16384]; /* Flawfinder: ignore */
+ size_t readbytes;
+ bool write_ok = true;
+ while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
+ {
+ if (fwrite(buf, 1, readbytes, out) != readbytes)
+ {
+ LL_WARNS("LLFile") << "Short write" << LL_ENDL;
+ write_ok = false;
+ }
+ }
+ if ( write_ok )
+ {
+ copied = true;
+ }
+ fclose(out);
+ }
+ fclose(in);
+ }
+ return copied;
+}
+
int LLFile::stat(const std::string& filename, llstat* filestatus)
{
#if LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index d59e68367e..f56b22bf9a 100755
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -75,6 +75,8 @@ public:
static int rmdir(const std::string& filename);
static int remove(const std::string& filename);
static int rename(const std::string& filename,const std::string& newname);
+ static bool copy(const std::string from, const std::string to);
+
static int stat(const std::string& filename,llstat* file_status);
static bool isdir(const std::string& filename);
static bool isfile(const std::string& filename);
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index f9702868c8..16a19e7021 100755
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -518,10 +518,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
-
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
+#if LL_WINDOWS
+inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);}
+#endif
+
// Length of this UTF32 string in bytes when transformed to UTF8
LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h
index 72f2e58ce1..acae74b584 100755
--- a/indra/llcommon/stringize.h
+++ b/indra/llcommon/stringize.h
@@ -31,21 +31,64 @@
#include <sstream>
#include <boost/lambda/lambda.hpp>
+#include <llstring.h>
/**
- * stringize(item) encapsulates an idiom we use constantly, using
+ * gstringize(item) encapsulates an idiom we use constantly, using
* operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str()
+ * or their wstring equivalents
* to render a string expressing some item.
*/
-template <typename T>
-std::string stringize(const T& item)
+template <typename CHARTYPE, typename T>
+std::basic_string<CHARTYPE> gstringize(const T& item)
{
- std::ostringstream out;
+ std::basic_ostringstream<CHARTYPE> out;
out << item;
return out.str();
}
/**
+ *partial specialization of stringize for handling wstring
+ *TODO: we should have similar specializations for wchar_t[] but not until it is needed.
+ */
+inline std::string stringize(const std::wstring& item)
+{
+ llwarns << "WARNING: Possible narrowing" << llendl;
+
+ std::string s;
+
+ s = wstring_to_utf8str(item);
+ return gstringize<char>(s);
+}
+
+/**
+ * Specialization of gstringize for std::string return types
+ */
+template <typename T>
+std::string stringize(const T& item)
+{
+ return gstringize<char>(item);
+}
+
+/**
+ * Specialization for generating wstring from string.
+ * Both a convenience function and saves a miniscule amount of overhead.
+ */
+inline std::wstring wstringize(const std::string& item)
+{
+ return gstringize<wchar_t>(item.c_str());
+}
+
+/**
+ * Specialization of gstringize for std::wstring return types
+ */
+template <typename T>
+std::wstring wstringize(const T& item)
+{
+ return gstringize<wchar_t>(item);
+}
+
+/**
* stringize_f(functor)
*/
template <typename Functor>
diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp
index 3d34f23998..3e4ca548e5 100755
--- a/indra/llcommon/tests/stringize_test.cpp
+++ b/indra/llcommon/tests/stringize_test.cpp
@@ -67,6 +67,8 @@ namespace tut
llsd["i"] = i;
llsd["d"] = d;
llsd["abc"] = abc;
+ def = L"def ghi";
+
}
char c;
@@ -76,6 +78,7 @@ namespace tut
float f;
double d;
std::string abc;
+ std::wstring def;
LLSD llsd;
};
typedef test_group<stringize_data> stringize_group;
@@ -92,6 +95,7 @@ namespace tut
ensure_equals(stringize(f), "3.14159");
ensure_equals(stringize(d), "3.14159");
ensure_equals(stringize(abc), "abc def");
+ ensure_equals(stringize(def), "def ghi"); //Will generate llwarns due to narrowing.
ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");
}
@@ -101,4 +105,20 @@ namespace tut
ensure_equals(STRINGIZE("c is " << c), "c is c");
ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");
}
+
+ template<> template<>
+ void stringize_object::test<3>()
+ {
+ //Tests rely on validity of wstring_to_utf8str()
+ ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
+ ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
+ ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
+ ensure_equals(wstring_to_utf8str(wstringize(l)), wstring_to_utf8str(L"68"));
+ ensure_equals(wstring_to_utf8str(wstringize(f)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(d)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
+ ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
+ // ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
+ }
} // namespace tut