diff --git a/.hgtags b/.hgtags
index 629164589877eb62515d8847c912ccfe429f13b8..e050331fdf54127912fe0c61245823541128d5f7 100644
--- a/.hgtags
+++ b/.hgtags
@@ -129,6 +129,8 @@ dac76a711da5f1489a01c1fa62ec97d99c25736d 2.6.6-release
 54fd44ac92e4c61435ea33effe093a3527e18d98 2.7.1-start
 0c4d0c24278074f219e5a32e72b449e78301d11b DRTVWR-61_2.7.1-beta1
 0c4d0c24278074f219e5a32e72b449e78301d11b 2.7.1-beta1
+a9abb9633a266c8d2fe62411cfd1c86d32da72bf DRTVWR-60_2.7.1-release
+a9abb9633a266c8d2fe62411cfd1c86d32da72bf 2.7.1-release
 9f79a6ed8fdcd2f3dac33ea6b3236eeb278dccfe 2.7.2-start
 e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb DRTVWR-63_2.7.2-beta1
 e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb 2.7.2-beta1
@@ -136,6 +138,8 @@ e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb 2.7.2-beta1
 6af10678de4736222b2c3f7e010e984fb5b327de 2.7.4-start
 be963a4eef635542f9617d7f5fd22ba48fb71958 DRTVWR-67_2.7.4-beta1
 be963a4eef635542f9617d7f5fd22ba48fb71958 2.7.4-beta1
+057f319dd8eccdf63a54d99686c68cdcb31b6abc DRTVWR-66_2.7.4-release
+057f319dd8eccdf63a54d99686c68cdcb31b6abc 2.7.4-release
 a9abb9633a266c8d2fe62411cfd1c86d32da72bf DRTVWR-60_2.7.1-release
 be963a4eef635542f9617d7f5fd22ba48fb71958 DRTVWR-67_2.7.4-beta1
 be963a4eef635542f9617d7f5fd22ba48fb71958 2.7.4-beta1
@@ -143,5 +147,7 @@ a9abb9633a266c8d2fe62411cfd1c86d32da72bf 2.7.1-release
 19a498fa62570f352d7d246f17e3c81cc1d82d8b 2.7.5-start
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc DRTVWR-69_2.7.5-beta1
 09984bfa6cae17e0f72d02b75c1b7393c65eecfc 2.7.5-beta1
+e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-start
+502f6a5deca9365ddae57db4f1e30172668e171e 2.8.1-start
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c DRTVWR-71_2.8.0-beta1
 e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-beta1
diff --git a/doc/contributions.txt b/doc/contributions.txt
index bad78dcd5f2eeeb8be05d5550c45391b7daa874e..68b0a4279f009de4473124bb1ff48296007ee323 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -20,6 +20,7 @@ Aimee Trescothick
 	SNOW-570
 	SNOW-572
 	SNOW-575
+	STORM-1315
 	VWR-3321
 	VWR-3336
 	VWR-3903
@@ -103,6 +104,7 @@ Ales Beaumont
 Alexandrea Fride
     STORM-255
 	STORM-960
+	STORM-1459
 Alissa Sabre
 	VWR-81
 	VWR-83
@@ -201,6 +203,7 @@ Boroondas Gupte
 	OPEN-29
 	OPEN-39
 	OPEN-39
+	OPEN-99
 	SNOW-278
 	SNOW-503
 	SNOW-510
@@ -449,6 +452,7 @@ Jonathan Yap
 	STORM-899
 	STORM-1273
 	STORM-1462
+	STORM-1459
 Kage Pixel
 	VWR-11
 Ken March
@@ -690,6 +694,7 @@ Robin Cornelius
 	STORM-1019
 	STORM-1095
 	STORM-1128
+	STORM-1459
 	VWR-2488
 	VWR-9557
 	VWR-10579
@@ -831,6 +836,7 @@ Thickbrick Sleaford
 	VWR-24420
 	STORM-956
 	STORM-1147
+	STORM-1325
 Thraxis Epsilon
 	SVC-371
 	VWR-383
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 2c974fb4ff27ba5edab1aa868354e29972eeea3f..0266239454c0f8e7feaf76c1fd85695a79661631 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -171,7 +171,10 @@ if (LINUX)
     add_definitions(-fvisibility=hidden)
     # don't catch SIGCHLD in our base application class for the viewer - some of our 3rd party libs may need their *own* SIGCHLD handler to work.  Sigh!  The viewer doesn't need to catch SIGCHLD anyway.
     add_definitions(-DLL_IGNORE_SIGCHLD)
-    add_definitions(-march=pentium4 -mfpmath=sse)
+    if (WORD_SIZE EQUAL 32)
+      add_definitions(-march=pentium4)
+    endif (WORD_SIZE EQUAL 32)
+    add_definitions(-mfpmath=sse)
     #add_definitions(-ftree-vectorize) # THIS CRASHES GCC 3.1-3.2
     if (NOT STANDALONE)
       # this stops us requiring a really recent glibc at runtime
diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
index 8beae555fb4418e571eaefc0902609c823c19037..99d0ad7e14060786688490ba0ba577cc61e8521b 100644
--- a/indra/linux_crash_logger/linux_crash_logger.cpp
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -24,16 +24,24 @@
  * $/LicenseInfo$
  */
 
+#include "linden_common.h"
 #include "llcrashloggerlinux.h"
 
 int main(int argc, char **argv)
 {
+	llinfos << "Starting crash reporter." << llendl;
+
 	LLCrashLoggerLinux app;
 	app.parseCommandOptions(argc, argv);
-	app.init();
+
+	if (! app.init())
+	{
+		llwarns << "Unable to initialize application." << llendl;
+		return 1;
+	}
+
 	app.mainLoop();
 	app.cleanup();
+	llinfos << "Crash reporter finished normally." << llendl;
 	return 0;
 }
-
-
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
index 7449c6426f8a763be0d807723e0133678a015526..7316717193bbc1aa2b8f2ffaefdf90cbb01c76c1 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -30,8 +30,6 @@
 
 #include "linden_common.h"
 
-#include "boost/tokenizer.hpp"
-
 #include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
 #include "llerror.h"
 #include "llfile.h"
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index d0f287657ed0b46d3a0743f9cc3af910640ffed9..0745696ef3e70d801725e7609dfe86e701e2c1c7 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -387,8 +387,6 @@ const S32 MAP_SIM_RETURN_NULL_SIMS 	= 0x00010000;
 const S32 MAP_SIM_PRELUDE 			= 0x00020000;
 
 // Crash reporter behavior
-const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
-const char* const CRASH_BEHAVIOR_SETTING = "CrashSubmitBehavior";
 const S32 CRASH_BEHAVIOR_ASK = 0;
 const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1;
 const S32 CRASH_BEHAVIOR_NEVER_SEND = 2;
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index e8616a9be6bd8f0455bb0b9229448ef6646e8f25..99e61433c65c748cd48286f9b09c7e4dc7e86499 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -1,6 +1,6 @@
 /** 
  * @file llsys.cpp
- * @brief Impelementation of the basic system query functions.
+ * @brief Implementation of the basic system query functions.
  *
  * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  * Second Life Viewer Source Code
@@ -24,6 +24,10 @@
  * $/LicenseInfo$
  */
 
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
 #include "linden_common.h"
 
 #include "llsys.h"
@@ -36,22 +40,43 @@
 #endif
 
 #include "llprocessor.h"
+#include "llerrorcontrol.h"
+#include "llevents.h"
+#include "lltimer.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include <boost/bind.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/regex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/range.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/type_traits/is_integral.hpp>
+#include <boost/type_traits/is_float.hpp>
+
+using namespace llsd;
 
 #if LL_WINDOWS
 #	define WIN32_LEAN_AND_MEAN
 #	include <winsock2.h>
 #	include <windows.h>
+#   include <psapi.h>               // GetPerformanceInfo() et al.
 #elif LL_DARWIN
 #	include <errno.h>
 #	include <sys/sysctl.h>
 #	include <sys/utsname.h>
 #	include <stdint.h>
 #	include <Carbon/Carbon.h>
+#   include <sys/wait.h>
+#   include <string.h>
+#   include <stdexcept>
 #elif LL_LINUX
 #	include <errno.h>
 #	include <sys/utsname.h>
 #	include <unistd.h>
 #	include <sys/sysinfo.h>
+#   include <stdexcept>
 const char MEMINFO_FILE[] = "/proc/meminfo";
 #elif LL_SOLARIS
 #	include <stdio.h>
@@ -70,6 +95,15 @@ extern int errno;
 static const S32 CPUINFO_BUFFER_SIZE = 16383;
 LLCPUInfo gSysCPU;
 
+// Don't log memory info any more often than this. It also serves as our
+// framerate sample size.
+static const F32 MEM_INFO_THROTTLE = 20;
+// Sliding window of samples. We intentionally limit the length of time we
+// remember "the slowest" framerate because framerate is very slow at login.
+// If we only triggered FrameWatcher logging when the session framerate
+// dropped below the login framerate, we'd have very little additional data.
+static const F32 MEM_INFO_WINDOW = 10*60;
+
 #if LL_WINDOWS
 #ifndef DLLVERSIONINFO
 typedef struct _DllVersionInfo
@@ -613,8 +647,78 @@ void LLCPUInfo::stream(std::ostream& s) const
 	s << "->mCPUString:  " << mCPUString << std::endl;
 }
 
+// Helper class for LLMemoryInfo: accumulate stats in the form we store for
+// LLMemoryInfo::getStatsMap().
+class Stats
+{
+public:
+	Stats():
+		mStats(LLSD::emptyMap())
+	{}
+
+	// Store every integer type as LLSD::Integer.
+	template <class T>
+	void add(const LLSD::String& name, const T& value,
+			 typename boost::enable_if<boost::is_integral<T> >::type* = 0)
+	{
+		mStats[name] = LLSD::Integer(value);
+	}
+
+	// Store every floating-point type as LLSD::Real.
+	template <class T>
+	void add(const LLSD::String& name, const T& value,
+			 typename boost::enable_if<boost::is_float<T> >::type* = 0)
+	{
+		mStats[name] = LLSD::Real(value);
+	}
+
+	// Hope that LLSD::Date values are sufficiently unambiguous.
+	void add(const LLSD::String& name, const LLSD::Date& value)
+	{
+		mStats[name] = value;
+	}
+
+	LLSD get() const { return mStats; }
+
+private:
+	LLSD mStats;
+};
+
+// Wrap boost::regex_match() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_match_no_exc(const S& string, M& match, const R& regex)
+{
+    try
+    {
+        return boost::regex_match(string, match, regex);
+    }
+    catch (const std::runtime_error& e)
+    {
+        LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': "
+                                 << e.what() << ":\n'" << string << "'" << LL_ENDL;
+        return false;
+    }
+}
+
+// Wrap boost::regex_search() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_search_no_exc(const S& string, M& match, const R& regex)
+{
+    try
+    {
+        return boost::regex_search(string, match, regex);
+    }
+    catch (const std::runtime_error& e)
+    {
+        LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': "
+                                 << e.what() << ":\n'" << string << "'" << LL_ENDL;
+        return false;
+    }
+}
+
 LLMemoryInfo::LLMemoryInfo()
 {
+	refresh();
 }
 
 #if LL_WINDOWS
@@ -638,11 +742,7 @@ static U32 LLMemoryAdjustKBResult(U32 inKB)
 U32 LLMemoryInfo::getPhysicalMemoryKB() const
 {
 #if LL_WINDOWS
-	MEMORYSTATUSEX state;
-	state.dwLength = sizeof(state);
-	GlobalMemoryStatusEx(&state);
-
-	return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10));
+	return LLMemoryAdjustKBResult(mStatsMap["Total Physical KB"].asInteger());
 
 #elif LL_DARWIN
 	// This might work on Linux as well.  Someone check...
@@ -690,12 +790,82 @@ U32 LLMemoryInfo::getPhysicalMemoryClamped() const
 void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb)
 {
 #if LL_WINDOWS
-	MEMORYSTATUSEX state;
-	state.dwLength = sizeof(state);
-	GlobalMemoryStatusEx(&state);
+	// Sigh, this shouldn't be a static method, then we wouldn't have to
+	// reload this data separately from refresh()
+	LLSD statsMap(loadStatsMap());
+
+	avail_physical_mem_kb = statsMap["Avail Physical KB"].asInteger();
+	avail_virtual_mem_kb  = statsMap["Avail Virtual KB"].asInteger();
+
+#elif LL_DARWIN
+	// mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
+	// $ vm_stat
+	// Mach Virtual Memory Statistics: (page size of 4096 bytes)
+	// Pages free:                   462078.
+	// Pages active:                 142010.
+	// Pages inactive:               220007.
+	// Pages wired down:             159552.
+	// "Translation faults":      220825184.
+	// Pages copy-on-write:         2104153.
+	// Pages zero filled:         167034876.
+	// Pages reactivated:             65153.
+	// Pageins:                     2097212.
+	// Pageouts:                      41759.
+	// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+	avail_physical_mem_kb = -1 ;
+	avail_virtual_mem_kb = -1 ;
 
-	avail_physical_mem_kb = (U32)(state.ullAvailPhys/1024) ;
-	avail_virtual_mem_kb = (U32)(state.ullAvailVirtual/1024) ;
+#elif LL_LINUX
+	// mStatsMap is derived from MEMINFO_FILE:
+	// $ cat /proc/meminfo
+	// MemTotal:        4108424 kB
+	// MemFree:         1244064 kB
+	// Buffers:           85164 kB
+	// Cached:          1990264 kB
+	// SwapCached:            0 kB
+	// Active:          1176648 kB
+	// Inactive:        1427532 kB
+	// Active(anon):     529152 kB
+	// Inactive(anon):    15924 kB
+	// Active(file):     647496 kB
+	// Inactive(file):  1411608 kB
+	// Unevictable:          16 kB
+	// Mlocked:              16 kB
+	// HighTotal:       3266316 kB
+	// HighFree:         721308 kB
+	// LowTotal:         842108 kB
+	// LowFree:          522756 kB
+	// SwapTotal:       6384632 kB
+	// SwapFree:        6384632 kB
+	// Dirty:                28 kB
+	// Writeback:             0 kB
+	// AnonPages:        528820 kB
+	// Mapped:            89472 kB
+	// Shmem:             16324 kB
+	// Slab:             159624 kB
+	// SReclaimable:     145168 kB
+	// SUnreclaim:        14456 kB
+	// KernelStack:        2560 kB
+	// PageTables:         5560 kB
+	// NFS_Unstable:          0 kB
+	// Bounce:                0 kB
+	// WritebackTmp:          0 kB
+	// CommitLimit:     8438844 kB
+	// Committed_AS:    1271596 kB
+	// VmallocTotal:     122880 kB
+	// VmallocUsed:       65252 kB
+	// VmallocChunk:      52356 kB
+	// HardwareCorrupted:     0 kB
+	// HugePages_Total:       0
+	// HugePages_Free:        0
+	// HugePages_Rsvd:        0
+	// HugePages_Surp:        0
+	// Hugepagesize:       2048 kB
+	// DirectMap4k:      434168 kB
+	// DirectMap2M:      477184 kB
+	// (could also run 'free', but easier to read a file than run a program)
+	avail_physical_mem_kb = -1 ;
+	avail_virtual_mem_kb = -1 ;
 
 #else
 	//do not know how to collect available memory info for other systems.
@@ -708,56 +878,389 @@ void LLMemoryInfo::getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_v
 
 void LLMemoryInfo::stream(std::ostream& s) const
 {
+	// We want these memory stats to be easy to grep from the log, along with
+	// the timestamp. So preface each line with the timestamp and a
+	// distinctive marker. Without that, we'd have to search the log for the
+	// introducer line, then read subsequent lines, etc...
+	std::string pfx(LLError::utcTime() + " <mem> ");
+
+	// Max key length
+	size_t key_width(0);
+	BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+	{
+		size_t len(pair.first.length());
+		if (len > key_width)
+		{
+			key_width = len;
+		}
+	}
+
+	// Now stream stats
+	BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+	{
+		s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' ';
+		LLSD value(pair.second);
+		if (value.isInteger())
+			s << std::setw(12) << value.asInteger();
+		else if (value.isReal())
+			s << std::fixed << std::setprecision(1) << value.asReal();
+		else if (value.isDate())
+			value.asDate().toStream(s);
+		else
+			s << value;           // just use default LLSD formatting
+		s << std::endl;
+	}
+}
+
+LLSD LLMemoryInfo::getStatsMap() const
+{
+	return mStatsMap;
+}
+
+LLMemoryInfo& LLMemoryInfo::refresh()
+{
+	mStatsMap = loadStatsMap();
+
+	LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
+	LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT);
+	LL_ENDL;
+
+	return *this;
+}
+
+LLSD LLMemoryInfo::loadStatsMap()
+{
+	// This implementation is derived from stream() code (as of 2011-06-29).
+	Stats stats;
+
+	// associate timestamp for analysis over time
+	stats.add("timestamp", LLDate::now());
+
 #if LL_WINDOWS
 	MEMORYSTATUSEX state;
 	state.dwLength = sizeof(state);
 	GlobalMemoryStatusEx(&state);
 
-	s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl;
-	s << "Total Physical KB:  " << (U32)(state.ullTotalPhys/1024) << std::endl;
-	s << "Avail Physical KB:  " << (U32)(state.ullAvailPhys/1024) << std::endl;
-	s << "Total page KB:      " << (U32)(state.ullTotalPageFile/1024) << std::endl;
-	s << "Avail page KB:      " << (U32)(state.ullAvailPageFile/1024) << std::endl;
-	s << "Total Virtual KB:   " << (U32)(state.ullTotalVirtual/1024) << std::endl;
-	s << "Avail Virtual KB:   " << (U32)(state.ullAvailVirtual/1024) << std::endl;
+	stats.add("Percent Memory use", state.dwMemoryLoad);
+	stats.add("Total Physical KB",  state.ullTotalPhys/1024);
+	stats.add("Avail Physical KB",  state.ullAvailPhys/1024);
+	stats.add("Total page KB",      state.ullTotalPageFile/1024);
+	stats.add("Avail page KB",      state.ullAvailPageFile/1024);
+	stats.add("Total Virtual KB",   state.ullTotalVirtual/1024);
+	stats.add("Avail Virtual KB",   state.ullAvailVirtual/1024);
+
+	PERFORMANCE_INFORMATION perf;
+	perf.cb = sizeof(perf);
+	GetPerformanceInfo(&perf, sizeof(perf));
+
+	SIZE_T pagekb(perf.PageSize/1024);
+	stats.add("CommitTotal KB",     perf.CommitTotal * pagekb);
+	stats.add("CommitLimit KB",     perf.CommitLimit * pagekb);
+	stats.add("CommitPeak KB",      perf.CommitPeak * pagekb);
+	stats.add("PhysicalTotal KB",   perf.PhysicalTotal * pagekb);
+	stats.add("PhysicalAvail KB",   perf.PhysicalAvailable * pagekb);
+	stats.add("SystemCache KB",     perf.SystemCache * pagekb);
+	stats.add("KernelTotal KB",     perf.KernelTotal * pagekb);
+	stats.add("KernelPaged KB",     perf.KernelPaged * pagekb);
+	stats.add("KernelNonpaged KB",  perf.KernelNonpaged * pagekb);
+	stats.add("PageSize KB",        pagekb);
+	stats.add("HandleCount",        perf.HandleCount);
+	stats.add("ProcessCount",       perf.ProcessCount);
+	stats.add("ThreadCount",        perf.ThreadCount);
+
+	PROCESS_MEMORY_COUNTERS_EX pmem;
+	pmem.cb = sizeof(pmem);
+	// GetProcessMemoryInfo() is documented to accept either
+	// PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
+	// using the redundant size info to distinguish. But its prototype
+	// specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
+	// classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
+	// pointer.
+	GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
+
+	stats.add("Page Fault Count",              pmem.PageFaultCount);
+	stats.add("PeakWorkingSetSize KB",         pmem.PeakWorkingSetSize/1024);
+	stats.add("WorkingSetSize KB",             pmem.WorkingSetSize/1024);
+	stats.add("QutaPeakPagedPoolUsage KB",     pmem.QuotaPeakPagedPoolUsage/1024);
+	stats.add("QuotaPagedPoolUsage KB",        pmem.QuotaPagedPoolUsage/1024);
+	stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/1024);
+	stats.add("QuotaNonPagedPoolUsage KB",     pmem.QuotaNonPagedPoolUsage/1024);
+	stats.add("PagefileUsage KB",              pmem.PagefileUsage/1024);
+	stats.add("PeakPagefileUsage KB",          pmem.PeakPagefileUsage/1024);
+	stats.add("PrivateUsage KB",               pmem.PrivateUsage/1024);
+
 #elif LL_DARWIN
 	uint64_t phys = 0;
 
 	size_t len = sizeof(phys);	
 	
-	if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
+	if (sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
 	{
-		s << "Total Physical KB:  " << phys/1024 << std::endl;
+		stats.add("Total Physical KB", phys/1024);
 	}
 	else
 	{
-		s << "Unable to collect memory information";
+		LL_WARNS("LLMemoryInfo") << "Unable to collect hw.memsize memory information" << LL_ENDL;
+	}
+
+	FILE* pout = popen("vm_stat 2>&1", "r");
+	if (! pout)                     // popen() couldn't run vm_stat
+	{
+		// Save errno right away.
+		int popen_errno(errno);
+		LL_WARNS("LLMemoryInfo") << "Unable to collect vm_stat memory information: ";
+		char buffer[256];
+		if (0 == strerror_r(popen_errno, buffer, sizeof(buffer)))
+		{
+			LL_CONT << buffer;
+		}
+		else
+		{
+			LL_CONT << "errno " << popen_errno;
+		}
+		LL_CONT << LL_ENDL;
+	}
+	else                            // popen() launched vm_stat
+	{
+		// Mach Virtual Memory Statistics: (page size of 4096 bytes)
+		// Pages free:					 462078.
+		// Pages active:				 142010.
+		// Pages inactive:				 220007.
+		// Pages wired down:			 159552.
+		// "Translation faults":	  220825184.
+		// Pages copy-on-write:			2104153.
+		// Pages zero filled:		  167034876.
+		// Pages reactivated:			  65153.
+		// Pageins:						2097212.
+		// Pageouts:					  41759.
+		// Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+
+		// Intentionally don't pass the boost::no_except flag. These
+		// boost::regex objects are constructed with string literals, so they
+		// should be valid every time. If they become invalid, we WANT an
+		// exception, hopefully even before the dev checks in.
+		boost::regex pagesize_rx("\\(page size of ([0-9]+) bytes\\)");
+		boost::regex stat_rx("(.+): +([0-9]+)\\.");
+		boost::regex cache_rx("Object cache: ([0-9]+) hits of ([0-9]+) lookups "
+							  "\\(([0-9]+)% hit rate\\)");
+		boost::cmatch matched;
+		LLSD::Integer pagesizekb(4096/1024);
+
+		// Here 'pout' is vm_stat's stdout. Search it for relevant data.
+		char line[100];
+		line[sizeof(line)-1] = '\0';
+		while (fgets(line, sizeof(line)-1, pout))
+		{
+			size_t linelen(strlen(line));
+			// Truncate any trailing newline
+			if (line[linelen - 1] == '\n')
+			{
+				line[--linelen] = '\0';
+			}
+			LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+			if (regex_search_no_exc(line, matched, pagesize_rx))
+			{
+				// "Mach Virtual Memory Statistics: (page size of 4096 bytes)"
+				std::string pagesize_str(matched[1].first, matched[1].second);
+				try
+				{
+					// Reasonable to assume that pagesize will always be a
+					// multiple of 1Kb?
+					pagesizekb = boost::lexical_cast<LLSD::Integer>(pagesize_str)/1024;
+				}
+				catch (const boost::bad_lexical_cast&)
+				{
+					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << pagesize_str
+											 << "' in vm_stat line: " << line << LL_ENDL;
+					continue;
+				}
+				stats.add("page size", pagesizekb);
+			}
+			else if (regex_match_no_exc(line, matched, stat_rx))
+			{
+				// e.g. "Pages free:					 462078."
+				// Strip double-quotes off certain statistic names
+				const char *key_begin(matched[1].first), *key_end(matched[1].second);
+				if (key_begin[0] == '"' && key_end[-1] == '"')
+				{
+					++key_begin;
+					--key_end;
+				}
+				LLSD::String key(key_begin, key_end);
+				LLSD::String value_str(matched[2].first, matched[2].second);
+				LLSD::Integer value(0);
+				try
+				{
+					value = boost::lexical_cast<LLSD::Integer>(value_str);
+				}
+				catch (const boost::bad_lexical_cast&)
+				{
+					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+											 << "' in vm_stat line: " << line << LL_ENDL;
+					continue;
+				}
+				// Store this statistic.
+				stats.add(key, value);
+				// Is this in units of pages? If so, convert to Kb.
+				static const LLSD::String pages("Pages ");
+				if (key.substr(0, pages.length()) == pages)
+				{
+					// Synthesize a new key with kb in place of Pages
+					LLSD::String kbkey("kb ");
+					kbkey.append(key.substr(pages.length()));
+					stats.add(kbkey, value * pagesizekb);
+				}
+			}
+			else if (regex_match_no_exc(line, matched, cache_rx))
+			{
+				// e.g. "Object cache: 841598 hits of 7629869 lookups (11% hit rate)"
+				static const char* cache_keys[] = { "cache hits", "cache lookups", "cache hit%" };
+				std::vector<LLSD::Integer> cache_values;
+				for (size_t i = 0; i < (sizeof(cache_keys)/sizeof(cache_keys[0])); ++i)
+				{
+					LLSD::String value_str(matched[i+1].first, matched[i+1].second);
+					LLSD::Integer value(0);
+					try
+					{
+						value = boost::lexical_cast<LLSD::Integer>(value_str);
+					}
+					catch (boost::bad_lexical_cast&)
+					{
+						LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+												 << "' in vm_stat line: " << line << LL_ENDL;
+						continue;
+					}
+					stats.add(cache_keys[i], value);
+				}
+			}
+			else
+			{
+				LL_WARNS("LLMemoryInfo") << "unrecognized vm_stat line: " << line << LL_ENDL;
+			}
+		}
+		int status(pclose(pout));
+		if (status == -1)           // pclose() couldn't retrieve rc
+		{
+			// Save errno right away.
+			int pclose_errno(errno);
+			// The ECHILD error happens so frequently that unless filtered,
+			// the warning below spams the log file. This is too bad, because
+			// sometimes the logic above fails to produce any output derived
+			// from vm_stat, but we've been unable to observe any specific
+			// error indicating the problem.
+			if (pclose_errno != ECHILD)
+			{
+				LL_WARNS("LLMemoryInfo") << "Unable to obtain vm_stat termination code: ";
+				char buffer[256];
+				if (0 == strerror_r(pclose_errno, buffer, sizeof(buffer)))
+				{
+					LL_CONT << buffer;
+				}
+				else
+				{
+					LL_CONT << "errno " << pclose_errno;
+				}
+				LL_CONT << LL_ENDL;
+			}
+		}
+		else                        // pclose() retrieved rc; analyze
+		{
+			if (WIFEXITED(status))
+			{
+				int rc(WEXITSTATUS(status));
+				if (rc != 0)
+				{
+					LL_WARNS("LLMemoryInfo") << "vm_stat terminated with rc " << rc << LL_ENDL;
+				}
+			}
+			else if (WIFSIGNALED(status))
+			{
+				LL_WARNS("LLMemoryInfo") << "vm_stat terminated by signal " << WTERMSIG(status)
+										 << LL_ENDL;
+			}
+		}
 	}
+
 #elif LL_SOLARIS
-        U64 phys = 0;
+	U64 phys = 0;
 
-        phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
+	phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
 
-        s << "Total Physical KB:  " << phys << std::endl;
-#else
-	// *NOTE: This works on linux. What will it do on other systems?
-	LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb");
-	if(meminfo)
+	stats.add("Total Physical KB", phys);
+
+#elif LL_LINUX
+	std::ifstream meminfo(MEMINFO_FILE);
+	if (meminfo.is_open())
 	{
-		char line[MAX_STRING];		/* Flawfinder: ignore */
-		memset(line, 0, MAX_STRING);
-		while(fgets(line, MAX_STRING, meminfo))
+		// MemTotal:		4108424 kB
+		// MemFree:			1244064 kB
+		// Buffers:			  85164 kB
+		// Cached:			1990264 kB
+		// SwapCached:			  0 kB
+		// Active:			1176648 kB
+		// Inactive:		1427532 kB
+		// ...
+		// VmallocTotal:	 122880 kB
+		// VmallocUsed:		  65252 kB
+		// VmallocChunk:	  52356 kB
+		// HardwareCorrupted:	  0 kB
+		// HugePages_Total:		  0
+		// HugePages_Free:		  0
+		// HugePages_Rsvd:		  0
+		// HugePages_Surp:		  0
+		// Hugepagesize:	   2048 kB
+		// DirectMap4k:		 434168 kB
+		// DirectMap2M:		 477184 kB
+
+		// Intentionally don't pass the boost::no_except flag. This
+		// boost::regex object is constructed with a string literal, so it
+		// should be valid every time. If it becomes invalid, we WANT an
+		// exception, hopefully even before the dev checks in.
+		boost::regex stat_rx("(.+): +([0-9]+)( kB)?");
+		boost::smatch matched;
+
+		std::string line;
+		while (std::getline(meminfo, line))
 		{
-			line[strlen(line)-1] = ' ';		 /*Flawfinder: ignore*/
-			s << line;
+			LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+			if (regex_match_no_exc(line, matched, stat_rx))
+			{
+				// e.g. "MemTotal:		4108424 kB"
+				LLSD::String key(matched[1].first, matched[1].second);
+				LLSD::String value_str(matched[2].first, matched[2].second);
+				LLSD::Integer value(0);
+				try
+				{
+					value = boost::lexical_cast<LLSD::Integer>(value_str);
+				}
+				catch (const boost::bad_lexical_cast&)
+				{
+					LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+											 << "' in " << MEMINFO_FILE << " line: "
+											 << line << LL_ENDL;
+					continue;
+				}
+				// Store this statistic.
+				stats.add(key, value);
+			}
+			else
+			{
+				LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: "
+										 << line << LL_ENDL;
+			}
 		}
-		fclose(meminfo);
 	}
 	else
 	{
-		s << "Unable to collect memory information";
+		LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
 	}
+
+#else
+	LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL;
+
 #endif
+
+	return stats.get();
 }
 
 std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
@@ -778,6 +1281,143 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
 	return s;
 }
 
+class FrameWatcher
+{
+public:
+    FrameWatcher():
+        // Hooking onto the "mainloop" event pump gets us one call per frame.
+        mConnection(LLEventPumps::instance()
+                    .obtain("mainloop")
+                    .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))),
+        // Initializing mSampleStart to an invalid timestamp alerts us to skip
+        // trying to compute framerate on the first call.
+        mSampleStart(-1),
+        // Initializing mSampleEnd to 0 ensures that we treat the first call
+        // as the completion of a sample window.
+        mSampleEnd(0),
+        mFrames(0),
+        // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
+        // the number of integer MEM_INFO_THROTTLE sample slots that will fit
+        // in MEM_INFO_WINDOW. Round up.
+        mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)),
+        // Initializing to F32_MAX means that the first real frame will become
+        // the slowest ever, which sounds like a good idea.
+        mSlowest(F32_MAX)
+    {}
+
+    bool tick(const LLSD&)
+    {
+        F32 timestamp(mTimer.getElapsedTimeF32());
+
+        // Count this frame in the interval just completed.
+        ++mFrames;
+
+        // Have we finished a sample window yet?
+        if (timestamp < mSampleEnd)
+        {
+            // no, just keep waiting
+            return false;
+        }
+
+        // Set up for next sample window. Capture values for previous frame in
+        // local variables and reset data members.
+        U32 frames(mFrames);
+        F32 sampleStart(mSampleStart);
+        // No frames yet in next window
+        mFrames = 0;
+        // which starts right now
+        mSampleStart = timestamp;
+        // and ends MEM_INFO_THROTTLE seconds in the future
+        mSampleEnd = mSampleStart + MEM_INFO_THROTTLE;
+
+        // On the very first call, that's all we can do, no framerate
+        // computation is possible.
+        if (sampleStart < 0)
+        {
+            return false;
+        }
+
+        // How long did this actually take? As framerate slows, the duration
+        // of the frame we just finished could push us WELL beyond our desired
+        // sample window size.
+        F32 elapsed(timestamp - sampleStart);
+        F32 framerate(frames/elapsed);
+
+        // Remember previous slowest framerate because we're just about to
+        // update it.
+        F32 slowest(mSlowest);
+        // Remember previous number of samples.
+        boost::circular_buffer<F32>::size_type prevSize(mSamples.size());
+
+        // Capture new framerate in our samples buffer. Once the buffer is
+        // full (after MEM_INFO_WINDOW seconds), this will displace the oldest
+        // sample. ("So they all rolled over, and one fell out...")
+        mSamples.push_back(framerate);
+
+        // Calculate the new minimum framerate. I know of no way to update a
+        // rolling minimum without ever rescanning the buffer. But since there
+        // are only a few tens of items in this buffer, rescanning it is
+        // probably cheaper (and certainly easier to reason about) than
+        // attempting to optimize away some of the scans.
+        mSlowest = framerate;       // pick an arbitrary entry to start
+        for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end());
+             si != send; ++si)
+        {
+            if (*si < mSlowest)
+            {
+                mSlowest = *si;
+            }
+        }
+
+        // We're especially interested in memory as framerate drops. Only log
+        // when framerate drops below the slowest framerate we remember.
+        // (Should always be true for the end of the very first sample
+        // window.)
+        if (framerate >= slowest)
+        {
+            return false;
+        }
+        // Congratulations, we've hit a new low.  :-P
+
+        LL_INFOS("FrameWatcher") << ' ';
+        if (! prevSize)
+        {
+            LL_CONT << "initial framerate ";
+        }
+        else
+        {
+            LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE)
+                    << " seconds ";
+        }
+        LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n'
+                << LLMemoryInfo() << LL_ENDL;
+
+        return false;
+    }
+
+private:
+    // Storing the connection in an LLTempBoundListener ensures it will be
+    // disconnected when we're destroyed.
+    LLTempBoundListener mConnection;
+    // Track elapsed time
+    LLTimer mTimer;
+    // Some of what you see here is in fact redundant with functionality you
+    // can get from LLTimer. Unfortunately the LLTimer API is missing the
+    // feature we need: has at least the stated interval elapsed, and if so,
+    // exactly how long has passed? So we have to do it by hand, sigh.
+    // Time at start, end of sample window
+    F32 mSampleStart, mSampleEnd;
+    // Frames this sample window
+    U32 mFrames;
+    // Sliding window of framerate samples
+    boost::circular_buffer<F32> mSamples;
+    // Slowest framerate in mSamples
+    F32 mSlowest;
+};
+
+// Need an instance of FrameWatcher before it does any good
+static FrameWatcher sFrameWatcher;
+
 BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile)
 {
 	std::string tmpfile;
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index 41a4f2500064eed2cda8e289b1d986f8d202b7c5..739e795d3af69bf4b8d691053a7ecf9987d87540 100644
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -36,6 +36,7 @@
 //  llinfos << info << llendl;
 //
 
+#include "llsd.h"
 #include <iosfwd>
 #include <string>
 
@@ -117,6 +118,27 @@ class LL_COMMON_API LLMemoryInfo
 
 	//get the available memory infomation in KiloBytes.
 	static void getAvailableMemoryKB(U32& avail_physical_mem_kb, U32& avail_virtual_mem_kb);
+
+	// Retrieve a map of memory statistics. The keys of the map are platform-
+	// dependent. The values are in kilobytes to try to avoid integer overflow.
+	LLSD getStatsMap() const;
+
+	// Re-fetch memory data (as reported by stream() and getStatsMap()) from the
+	// system. Normally this is fetched at construction time. Return (*this)
+	// to permit usage of the form:
+	// @code
+	// LLMemoryInfo info;
+	// ...
+	// info.refresh().getStatsMap();
+	// @endcode
+	LLMemoryInfo& refresh();
+
+private:
+	// set mStatsMap
+	static LLSD loadStatsMap();
+
+	// Memory stats for getStatsMap().
+	LLSD mStatsMap;
 };
 
 
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 3dcaca4f16c3d116fc0ee622df698b269096e3ac..0018b8e844a18b7941a65594f222dbe42f9e8c1b 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -29,7 +29,7 @@
 
 const S32 LL_VERSION_MAJOR = 2;
 const S32 LL_VERSION_MINOR = 8;
-const S32 LL_VERSION_PATCH = 0;
+const S32 LL_VERSION_PATCH = 1;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp
index 68e45f36e4f65b7e8a8a5663177f87c7a9dd1397..93f3c910bd70f8fce1af0a7c9564605ad405bc17 100644
--- a/indra/llcrashlogger/llcrashlogger.cpp
+++ b/indra/llcrashlogger/llcrashlogger.cpp
@@ -31,10 +31,12 @@
 #include "llcrashlogger.h"
 #include "linden_common.h"
 #include "llstring.h"
-#include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "indra_constants.h"	// CRASH_BEHAVIOR_...
 #include "llerror.h"
+#include "llerrorcontrol.h"
 #include "lltimer.h"
 #include "lldir.h"
+#include "llfile.h"
 #include "llsdserialize.h"
 #include "lliopipe.h"
 #include "llpumpio.h"
@@ -54,7 +56,7 @@ class LLCrashLoggerResponder : public LLHTTPClient::Responder
 
 	virtual void error(U32 status, const std::string& reason)
 	{
-		gBreak = true;		
+		gBreak = true;
 	}
 
 	virtual void result(const LLSD& content)
@@ -64,21 +66,8 @@ class LLCrashLoggerResponder : public LLHTTPClient::Responder
 	}
 };
 
-bool LLCrashLoggerText::mainLoop()
-{
-	std::cout << "Entering main loop" << std::endl;
-	sendCrashLogs();
-	return true;	
-}
-
-void LLCrashLoggerText::updateApplication(const std::string& message)
-{
-	LLCrashLogger::updateApplication(message);
-	std::cout << message << std::endl;
-}
-
 LLCrashLogger::LLCrashLogger() :
-	mCrashBehavior(CRASH_BEHAVIOR_ASK),
+	mCrashBehavior(CRASH_BEHAVIOR_ALWAYS_SEND),
 	mCrashInPreviousExec(false),
 	mCrashSettings("CrashSettings"),
 	mSentCrashLogs(false),
@@ -281,26 +270,48 @@ LLSD LLCrashLogger::constructPostData()
 	return mCrashInfo;
 }
 
+const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
+
 S32 LLCrashLogger::loadCrashBehaviorSetting()
 {
+	// First check user_settings (in the user's home dir)
 	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+	if (! mCrashSettings.loadFromFile(filename))
+	{
+		// Next check app_settings (in the SL program dir)
+		std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, CRASH_SETTINGS_FILE);
+		mCrashSettings.loadFromFile(filename);
+	}
 
-	mCrashSettings.loadFromFile(filename);
-		
-	S32 value = mCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-	
-	if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+	// If we didn't load any files above, this will return the default
+	S32 value = mCrashSettings.getS32("CrashSubmitBehavior");
 
-	return value;
+	// Whatever value we got, make sure it's valid
+	switch (value)
+	{
+	case CRASH_BEHAVIOR_NEVER_SEND:
+		return CRASH_BEHAVIOR_NEVER_SEND;
+	case CRASH_BEHAVIOR_ALWAYS_SEND:
+		return CRASH_BEHAVIOR_ALWAYS_SEND;
+	}
+
+	return CRASH_BEHAVIOR_ASK;
 }
 
 bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
 {
-	if (crash_behavior != CRASH_BEHAVIOR_ASK && crash_behavior != CRASH_BEHAVIOR_ALWAYS_SEND) return false;
+	switch (crash_behavior)
+	{
+	case CRASH_BEHAVIOR_ASK:
+	case CRASH_BEHAVIOR_NEVER_SEND:
+	case CRASH_BEHAVIOR_ALWAYS_SEND:
+		break;
+	default:
+		return false;
+	}
 
-	mCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+	mCrashSettings.setS32("CrashSubmitBehavior", crash_behavior);
 	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-
 	mCrashSettings.saveToFile(filename, FALSE);
 
 	return true;
@@ -309,14 +320,13 @@ bool LLCrashLogger::saveCrashBehaviorSetting(S32 crash_behavior)
 bool LLCrashLogger::runCrashLogPost(std::string host, LLSD data, std::string msg, int retries, int timeout)
 {
 	gBreak = false;
-	std::string status_message;
 	for(int i = 0; i < retries; ++i)
 	{
-		status_message = llformat("%s, try %d...", msg.c_str(), i+1);
+		updateApplication(llformat("%s, try %d...", msg.c_str(), i+1));
 		LLHTTPClient::post(host, data, new LLCrashLoggerResponder(), timeout);
 		while(!gBreak)
 		{
-			updateApplication(status_message);
+			updateApplication(); // No new message, just pump the IO
 		}
 		if(gSent)
 		{
@@ -336,7 +346,7 @@ bool LLCrashLogger::sendCrashLogs()
 	updateApplication("Sending reports...");
 
 	std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
-															   "SecondLifeCrashReport");
+														   "SecondLifeCrashReport");
 	std::string report_file = dump_path + ".log";
 
 	std::ofstream out_file(report_file.c_str());
@@ -365,6 +375,7 @@ void LLCrashLogger::updateApplication(const std::string& message)
 {
 	gServicePump->pump();
     gServicePump->callback();
+	if (!message.empty()) llinfos << message << llendl;
 }
 
 bool LLCrashLogger::init()
@@ -374,14 +385,27 @@ bool LLCrashLogger::init()
 	// We assume that all the logs we're looking for reside on the current drive
 	gDirUtilp->initAppDirs("SecondLife");
 
+	LLError::initForApplication(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+
 	// Default to the product name "Second Life" (this is overridden by the -name argument)
 	mProductName = "Second Life";
+
+	// Rename current log file to ".old"
+	std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
+	std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
+	LLFile::rename(log_file.c_str(), old_log_file.c_str());
+
+	// Set the log file to crashreport.log
+	LLError::logToFile(log_file);
 	
-	mCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
-		"(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
+	mCrashSettings.declareS32("CrashSubmitBehavior", CRASH_BEHAVIOR_ALWAYS_SEND,
+							  "Controls behavior when viewer crashes "
+							  "(0 = ask before sending crash report, "
+							  "1 = always send crash report, "
+							  "2 = never send crash report)");
 
-	llinfos << "Loading crash behavior setting" << llendl;
-	mCrashBehavior = loadCrashBehaviorSetting();
+	// llinfos << "Loading crash behavior setting" << llendl;
+	// mCrashBehavior = loadCrashBehaviorSetting();
 
 	// If user doesn't want to send, bail out
 	if (mCrashBehavior == CRASH_BEHAVIOR_NEVER_SEND)
@@ -394,10 +418,11 @@ bool LLCrashLogger::init()
 	gServicePump->prime(gAPRPoolp);
 	LLHTTPClient::setPump(*gServicePump);
 
-	//If we've opened the crash logger, assume we can delete the marker file if it exists	
+	//If we've opened the crash logger, assume we can delete the marker file if it exists
 	if( gDirUtilp )
 	{
-		std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.exec_marker");
+		std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+																 "SecondLife.exec_marker");
 		LLAPRFile::remove( marker_file );
 	}
 	
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
index a5daa742471dbe4e8ec1b09b335f7cd8e095d118..5d0cb5931ce2875f40a5b790e7a17392b525b4e9 100644
--- a/indra/llcrashlogger/llcrashlogger.h
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -66,15 +66,4 @@ class LLCrashLogger : public LLApp
 	bool mSentCrashLogs;
 };
 
-class LLCrashLoggerText : public LLCrashLogger
-{
-public:
-	LLCrashLoggerText(void) {}
-	~LLCrashLoggerText(void) {}
-
-	virtual bool mainLoop();
-	virtual void updateApplication(const std::string& message = LLStringUtil::null);
-};
-
-
 #endif //LLCRASHLOGGER_H
diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt
index 9dadad7dd34836f0f107afaed854973ddb85d7cd..cd100cdf9fd38a0147ddb33a91da54de57f9bf28 100644
--- a/indra/llmath/CMakeLists.txt
+++ b/indra/llmath/CMakeLists.txt
@@ -12,6 +12,8 @@ include_directories(
 set(llmath_SOURCE_FILES
     llbbox.cpp
     llbboxlocal.cpp
+    llcalc.cpp
+    llcalcparser.cpp
     llcamera.cpp
     llcoordframe.cpp
     llline.cpp
@@ -46,6 +48,8 @@ set(llmath_HEADER_FILES
     coordframe.h
     llbbox.h
     llbboxlocal.h
+    llcalc.h
+    llcalcparser.h
     llcamera.h
     llcoord.h
     llcoordframe.h
diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..597d0815fb527295369dbd0aa7517af540532bfd
--- /dev/null
+++ b/indra/llmath/llcalc.cpp
@@ -0,0 +1,145 @@
+/*
+ *  LLCalc.cpp
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalc.h"
+
+#include "llcalcparser.h"
+#include "llmath.h"
+
+
+// Variable names for use in the build floater
+const char* LLCalc::X_POS = "PX";
+const char* LLCalc::Y_POS = "PY";
+const char* LLCalc::Z_POS = "PZ";
+const char* LLCalc::X_SCALE = "SX";
+const char* LLCalc::Y_SCALE = "SY";
+const char* LLCalc::Z_SCALE = "SZ";
+const char* LLCalc::X_ROT = "RX";
+const char* LLCalc::Y_ROT = "RY";
+const char* LLCalc::Z_ROT = "RZ";
+const char* LLCalc::HOLLOW = "HLW";
+const char* LLCalc::CUT_BEGIN = "CB";
+const char* LLCalc::CUT_END = "CE";
+const char* LLCalc::PATH_BEGIN = "PB";
+const char* LLCalc::PATH_END = "PE";
+const char* LLCalc::TWIST_BEGIN = "TB";
+const char* LLCalc::TWIST_END = "TE";
+const char* LLCalc::X_SHEAR = "SHX";
+const char* LLCalc::Y_SHEAR = "SHY";
+const char* LLCalc::X_TAPER = "TPX";
+const char* LLCalc::Y_TAPER = "TPY";
+const char* LLCalc::RADIUS_OFFSET = "ROF";
+const char* LLCalc::REVOLUTIONS = "REV";
+const char* LLCalc::SKEW = "SKW";
+const char* LLCalc::X_HOLE = "HLX";
+const char* LLCalc::Y_HOLE = "HLY";
+const char* LLCalc::TEX_U_SCALE = "TSU";
+const char* LLCalc::TEX_V_SCALE = "TSV";
+const char* LLCalc::TEX_U_OFFSET = "TOU";
+const char* LLCalc::TEX_V_OFFSET = "TOV";
+const char* LLCalc::TEX_ROTATION = "TROT";
+const char* LLCalc::TEX_TRANSPARENCY = "TRNS";
+const char* LLCalc::TEX_GLOW = "GLOW";
+
+
+LLCalc* LLCalc::sInstance = NULL;
+
+LLCalc::LLCalc() : mLastErrorPos(0)
+{
+	// Init table of constants
+	mConstants["PI"] = F_PI;
+	mConstants["TWO_PI"] = F_TWO_PI;
+	mConstants["PI_BY_TWO"] = F_PI_BY_TWO;
+	mConstants["SQRT_TWO_PI"] = F_SQRT_TWO_PI;
+	mConstants["SQRT2"] = F_SQRT2;
+	mConstants["SQRT3"] = F_SQRT3;
+	mConstants["DEG_TO_RAD"] = DEG_TO_RAD;
+	mConstants["RAD_TO_DEG"] = RAD_TO_DEG;
+	mConstants["GRAVITY"] = GRAVITY;
+}
+
+LLCalc::~LLCalc()
+{
+}
+
+//static
+void LLCalc::cleanUp()
+{
+	delete sInstance;
+	sInstance = NULL;
+}
+
+//static
+LLCalc* LLCalc::getInstance()
+{
+    if (!sInstance)	sInstance = new LLCalc();
+	return sInstance;
+}
+
+void LLCalc::setVar(const std::string& name, const F32& value)
+{
+	mVariables[name] = value;
+}
+
+void LLCalc::clearVar(const std::string& name)
+{
+	mVariables.erase(name);
+}
+
+void LLCalc::clearAllVariables()
+{
+	mVariables.clear();
+}
+
+/*
+void LLCalc::updateVariables(LLSD& vars)
+{
+	LLSD::map_iterator cIt = vars.beginMap();
+	for(; cIt != vars.endMap(); cIt++)
+	{
+		setVar(cIt->first, (F32)(LLSD::Real)cIt->second);
+	}
+}
+*/
+
+bool LLCalc::evalString(const std::string& expression, F32& result)
+{
+	std::string expr_upper = expression;
+	LLStringUtil::toUpper(expr_upper);
+	
+	LLCalcParser calc(result, &mConstants, &mVariables);
+
+	mLastErrorPos = 0;
+	std::string::iterator start = expr_upper.begin();
+ 	parse_info<std::string::iterator> info;
+	
+	try
+	{
+		info = parse(start, expr_upper.end(), calc, space_p);
+		lldebugs << "Math expression: " << expression << " = " << result << llendl;
+	}
+	catch(parser_error<std::string, std::string::iterator> &e)
+	{
+		mLastErrorPos = e.where - expr_upper.begin();
+		
+		llinfos << "Calc parser exception: " << e.descriptor << " at " << mLastErrorPos << " in expression: " << expression << llendl;
+		return false;
+	}
+	
+	if (!info.full)
+	{
+		mLastErrorPos = info.stop - expr_upper.begin();
+		llinfos << "Unhandled syntax error at " << mLastErrorPos << " in expression: " << expression << llendl;
+		return false;
+	}
+	
+	return true;
+}
diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc31950cb681001ba65b9232249be9161ec173f4
--- /dev/null
+++ b/indra/llmath/llcalc.h
@@ -0,0 +1,83 @@
+/*
+ *  LLCalc.h
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALC_H
+#define LL_CALC_H
+
+#include <map>
+#include <string>
+
+class LLCalc
+{
+public:
+	LLCalc();
+	~LLCalc();
+
+	// Variable name constants
+	static const char* X_POS;
+	static const char* Y_POS;
+	static const char* Z_POS;
+	static const char* X_SCALE;
+	static const char* Y_SCALE;
+	static const char* Z_SCALE;
+	static const char* X_ROT;
+	static const char* Y_ROT;
+	static const char* Z_ROT;
+	static const char* HOLLOW;
+	static const char* CUT_BEGIN;
+	static const char* CUT_END;
+	static const char* PATH_BEGIN;
+	static const char* PATH_END;
+	static const char* TWIST_BEGIN;
+	static const char* TWIST_END;
+	static const char* X_SHEAR;
+	static const char* Y_SHEAR;
+	static const char* X_TAPER;
+	static const char* Y_TAPER;
+	static const char* RADIUS_OFFSET;
+	static const char* REVOLUTIONS;
+	static const char* SKEW;
+	static const char* X_HOLE;
+	static const char* Y_HOLE;
+	static const char* TEX_U_SCALE;
+	static const char* TEX_V_SCALE;
+	static const char* TEX_U_OFFSET;
+	static const char* TEX_V_OFFSET;
+	static const char* TEX_ROTATION;
+	static const char* TEX_TRANSPARENCY;
+	static const char* TEX_GLOW;
+
+	void	setVar(const std::string& name, const F32& value);
+	void	clearVar(const std::string& name);
+	void	clearAllVariables();
+//	void	updateVariables(LLSD& vars);
+
+	bool	evalString(const std::string& expression, F32& result);
+	std::string::size_type	getLastErrorPos()	{ return mLastErrorPos; }
+	
+	static LLCalc* getInstance();
+	static void cleanUp();
+
+	typedef	std::map<std::string, F32> calc_map_t;
+	
+private:
+	std::string::size_type	mLastErrorPos;
+	
+	calc_map_t	mConstants;
+	calc_map_t	mVariables;
+	
+	// *TODO: Add support for storing user defined variables, and stored functions.
+	//	Will need UI work, and a means to save them between sessions.
+//	calc_map_t mUserVariables;
+	
+	// "There shall be only one"
+	static LLCalc*	sInstance;
+};
+
+#endif // LL_CALC_H
diff --git a/indra/llmath/llcalcparser.cpp b/indra/llmath/llcalcparser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd55376fa9086173be5712dab2ebc415c1e99609
--- /dev/null
+++ b/indra/llmath/llcalcparser.cpp
@@ -0,0 +1,46 @@
+/*
+ *  LLCalcParser.cpp
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#include "linden_common.h"
+
+#include "llcalcparser.h"
+using namespace boost::spirit::classic;
+
+F32 LLCalcParser::lookup(const std::string::iterator& start, const std::string::iterator& end) const
+{
+	LLCalc::calc_map_t::iterator iter;
+
+	std::string name(start, end);
+	
+	if (mConstants)
+	{
+		iter = mConstants->find(name);
+		if (iter != mConstants->end())
+		{
+			return (*iter).second;
+		}
+	}
+	else
+	{
+		// This should never happen!
+		throw_(end, std::string("Missing constants table"));
+	}
+	
+	if (mVariables)
+	{
+		iter = mVariables->find(name);
+		if (iter != mVariables->end())
+		{
+			return (*iter).second;
+		}
+	}
+	
+	throw_(end, std::string("Unknown symbol " + name));
+	return 0.f;
+}
diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h
new file mode 100644
index 0000000000000000000000000000000000000000..600e17366107c9279badc2001341424dc506e07e
--- /dev/null
+++ b/indra/llmath/llcalcparser.h
@@ -0,0 +1,174 @@
+/*
+ *  LLCalcParser.h
+ *  SecondLife
+ *
+ *  Created by Aimee Walton on 28/09/2008.
+ *  Copyright 2008 Aimee Walton.
+ *
+ */
+
+#ifndef LL_CALCPARSER_H
+#define LL_CALCPARSER_H
+
+#include <boost/spirit/include/classic_attribute.hpp>
+#include <boost/spirit/include/classic_core.hpp>
+#include <boost/spirit/include/classic_error_handling.hpp>
+#include <boost/spirit/include/classic_position_iterator.hpp>
+#include <boost/spirit/include/phoenix1_binders.hpp>
+#include <boost/spirit/include/classic_symbols.hpp>
+using namespace boost::spirit::classic;
+
+#include "llcalc.h"
+#include "llmath.h"
+
+struct LLCalcParser : grammar<LLCalcParser>
+{
+	LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) :
+		mResult(result), mConstants(constants), mVariables(vars) {};
+	
+	struct value_closure : closure<value_closure, F32>
+	{
+		member1 value;
+	};
+	
+	template <typename ScannerT>
+	struct definition
+	{
+		// Rule declarations
+		rule<ScannerT> statement, identifier;
+		rule<ScannerT, value_closure::context_t> expression, term,
+			power, 
+			unary_expr, 
+			factor, 
+			unary_func, 
+			binary_func,
+			group;
+
+		// start() should return the starting symbol
+		rule<ScannerT> const& start() const { return statement; }
+		
+		definition(LLCalcParser const& self)
+		{
+			using namespace phoenix;
+			
+			assertion<std::string> assert_domain("Domain error");
+//			assertion<std::string> assert_symbol("Unknown symbol");
+			assertion<std::string> assert_syntax("Syntax error");
+			
+			identifier =
+				lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')]
+			;
+			
+			group =
+				'(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')'))
+			;
+
+			unary_func =
+				((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sin)(self,arg1)]) |
+				 (str_p("COS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_cos)(self,arg1)]) |
+				 (str_p("TAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_tan)(self,arg1)]) |
+				 (str_p("ASIN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_asin)(self,arg1)]) |
+				 (str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_acos)(self,arg1)]) |
+				 (str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_atan)(self,arg1)]) |
+				 (str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_sqrt)(self,arg1)]) |
+				 (str_p("LOG") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_log)(self,arg1)]) |
+				 (str_p("EXP") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_exp)(self,arg1)]) |
+				 (str_p("ABS") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_fabs)(self,arg1)]) |
+				 (str_p("FLR") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_floor)(self,arg1)]) |
+				 (str_p("CEIL") >> '(' >> expression[unary_func.value = bind(&LLCalcParser::_ceil)(self,arg1)])
+				) >> assert_syntax(ch_p(')'))
+			;
+			
+			binary_func =
+				((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
+				  expression[binary_func.value = bind(&LLCalcParser::_atan2)(self, binary_func.value, arg1)]) |
+				 (str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >> 
+				  expression[binary_func.value = bind(&LLCalcParser::_min)(self, binary_func.value, arg1)]) |
+				 (str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >> 
+				  expression[binary_func.value = bind(&LLCalcParser::_max)(self, binary_func.value, arg1)])
+				) >> assert_syntax(ch_p(')'))
+			;
+			
+			// *TODO: Localisation of the decimal point?
+			// Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate
+			// for the current locale. However to do that here could clash with using
+			// the comma as a separator when passing arguments to functions.
+			factor =
+				(ureal_p[factor.value = arg1] |
+				 group[factor.value = arg1] |
+				 unary_func[factor.value = arg1] |
+				 binary_func[factor.value = arg1] |
+				 // Lookup throws an Unknown Symbol error if it is unknown, while this works fine,
+				 // would be "neater" to handle symbol lookup from here with an assertive parser.
+//				 constants_p[factor.value = arg1]|
+				 identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)]
+				) >>
+				// Detect and throw math errors.
+				assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value)))
+			;
+
+			unary_expr =
+				!ch_p('+') >> factor[unary_expr.value = arg1] |
+				'-' >> factor[unary_expr.value = -arg1]
+			;
+			
+			power =
+				unary_expr[power.value = arg1] >>
+				*('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)]))
+			;
+			
+			term =
+				power[term.value = arg1] >>
+				*(('*' >> assert_syntax(power[term.value *= arg1])) |
+				  ('/' >> assert_syntax(power[term.value /= arg1])) |
+				  ('%' >> assert_syntax(power[term.value = bind(&fmodf)(term.value, arg1)]))
+				)
+			;
+			
+			expression =
+				assert_syntax(term[expression.value = arg1]) >>
+				*(('+' >> assert_syntax(term[expression.value += arg1])) |
+				  ('-' >> assert_syntax(term[expression.value -= arg1]))
+				)
+			;
+
+			statement =
+				!ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p)
+			;
+		}
+	};
+	
+private:
+	// Member functions for semantic actions
+	F32	lookup(const std::string::iterator&, const std::string::iterator&) const;
+	F32 _min(const F32& a, const F32& b) const { return llmin(a, b); }
+	F32 _max(const F32& a, const F32& b) const { return llmax(a, b); }
+	
+	bool checkNaN(const F32& a) const { return !llisnan(a); }
+	
+	//FIX* non ambigious function fix making SIN() work for calc -Cryogenic Blitz
+	F32 _sin(const F32& a) const { return sin(DEG_TO_RAD * a); }
+	F32 _cos(const F32& a) const { return cos(DEG_TO_RAD * a); }
+	F32 _tan(const F32& a) const { return tan(DEG_TO_RAD * a); }
+	F32 _asin(const F32& a) const { return asin(a * RAD_TO_DEG); }
+	F32 _acos(const F32& a) const { return acos(a * RAD_TO_DEG); }
+	F32 _atan(const F32& a) const { return atan(a * RAD_TO_DEG); }
+	F32 _sqrt(const F32& a) const { return sqrt(a); }
+	F32 _log(const F32& a) const { return log(a); }
+	F32 _exp(const F32& a) const { return exp(a); }
+	F32 _fabs(const F32& a) const { return fabs(a); }
+	F32 _floor(const F32& a) const { return llfloor(a); }
+	F32 _ceil(const F32& a) const { return llceil(a); }
+
+	F32 _atan2(const F32& a,const F32& b) const { return atan2(a,b); }
+
+
+
+	LLCalc::calc_map_t* mConstants;
+	LLCalc::calc_map_t* mVariables;
+//	LLCalc::calc_map_t* mUserVariables;
+	
+	F32&		mResult;
+};
+
+#endif // LL_CALCPARSER_H
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 123997e5e918273e1d8f24e94cd82f821b6c05d4..06fbc0f234850f5c1eb063407bd3d3c94cf4f7fa 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -37,6 +37,7 @@
 #include "llgl.h"
 #include "lltimer.h"
 
+#include "llcalc.h"
 //#include "llclipboard.h"
 #include "llcontrol.h"
 #include "llbutton.h"
@@ -133,6 +134,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	mIgnoreTab( p.ignore_tab ),
 	mDrawAsterixes( p.is_password ),
 	mSelectAllonFocusReceived( p.select_on_focus ),
+	mSelectAllonCommit( TRUE ),
 	mPassDelete(FALSE),
 	mReadOnly(FALSE),
 	mBgImage( p.background_image ),
@@ -230,7 +232,10 @@ void LLLineEditor::onCommit()
 
 	setControlValue(getValue());
 	LLUICtrl::onCommit();
-	selectAll();
+
+	// Selection on commit needs to be turned off when evaluating maths
+	// expressions, to allow indication of the error position
+	if (mSelectAllonCommit) selectAll();
 }
 
 // Returns TRUE if user changed value at all
@@ -2072,6 +2077,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str)
 	return success;
 }
 
+BOOL LLLineEditor::evaluateFloat()
+{
+	bool success;
+	F32 result = 0.f;
+	std::string expr = getText();
+	LLStringUtil::toUpper(expr);
+
+	success = LLCalc::getInstance()->evalString(expr, result);
+
+	if (!success)
+	{
+		// Move the cursor to near the error on failure
+		setCursor(LLCalc::getInstance()->getLastErrorPos());
+		// *TODO: Translated error message indicating the type of error? Select error text?
+	}
+	else
+	{
+		// Replace the expression with the result
+		std::string result_str = llformat("%f",result);
+		setText(result_str);
+		selectAll();
+	}
+
+	return success;
+}
+
 void LLLineEditor::onMouseCaptureLost()
 {
 	endSelection();
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 4b7770859037fdb6b0dffda6ef02456fcabc1b8d..583bde360a3181d15d9a1813a119b1841c7b5d5e 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -221,6 +221,7 @@ class LLLineEditor
 	void			deleteSelection();
 
 	void			setSelectAllonFocusReceived(BOOL b);
+	void			setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; }
 	
 	typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t;
 	void			setKeystrokeCallback(callback_t callback, void* user_data);
@@ -241,6 +242,7 @@ class LLLineEditor
 	static BOOL		postvalidateFloat(const std::string &str);
 
 	bool			prevalidateInput(const LLWString& wstr);
+	BOOL			evaluateFloat();
 
 	// line history support:
 	void			setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off 
@@ -340,6 +342,7 @@ class LLLineEditor
 	BOOL		mDrawAsterixes;
 
 	BOOL		mSelectAllonFocusReceived;
+	BOOL		mSelectAllonCommit;
 	BOOL		mPassDelete;
 
 	BOOL		mReadOnly;
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
index 15a7438ec956796567cfcbcfe427c6e8e009d70d..934879cdfd1dbd573b2ede3a8275c13be48b4001 100644
--- a/indra/llui/llspinctrl.cpp
+++ b/indra/llui/llspinctrl.cpp
@@ -44,7 +44,7 @@
 #include "llresmgr.h"
 #include "lluictrlfactory.h"
 
-const U32 MAX_STRING_LENGTH = 32;
+const U32 MAX_STRING_LENGTH = 255;
 
 static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner");
 
@@ -124,14 +124,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
 	params.max_length.bytes(MAX_STRING_LENGTH);
 	params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2)));
 	
-	if( mPrecision>0 )//should accept float numbers
-	{
-		params.prevalidate_callback(&LLTextValidate::validateFloat);
-	}
-	else //should accept int numbers
-	{
-		params.prevalidate_callback(&LLTextValidate::validateInt);
-	}
+	//*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit
 	
 	params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
 	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
@@ -140,6 +133,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p)
 	// than when it doesn't.  Instead, if you always have to double click to select all the text, 
 	// it's easier to understand
 	//mEditor->setSelectAllonFocusReceived(TRUE);
+	mEditor->setSelectAllonCommit(FALSE);
 	addChild(mEditor);
 
 	updateEditor();
@@ -304,9 +298,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
 {
 	BOOL success = FALSE;
 	
-	std::string text = mEditor->getText();
-	if( LLLineEditor::postvalidateFloat( text ) )
+	if( mEditor->evaluateFloat() )
 	{
+		std::string text = mEditor->getText();
+
 		LLLocale locale(LLLocale::USER_LOCALE);
 		F32 val = (F32) atof(text.c_str());
 
@@ -327,7 +322,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data )
 	}
 	updateEditor();
 
-	if( !success )
+	if( success )
+	{
+		updateEditor();
+	}
+	else
 	{
 		reportInvalidData();		
 	}
diff --git a/indra/mac_crash_logger/CrashReporter.nib/objects.xib b/indra/mac_crash_logger/CrashReporter.nib/objects.xib
index 634d1c5321df91765438eda0a9e70d02386204af..32647391b6cc8a91bb96d20ed7d8a95f5ca84bae 100644
--- a/indra/mac_crash_logger/CrashReporter.nib/objects.xib
+++ b/indra/mac_crash_logger/CrashReporter.nib/objects.xib
@@ -15,7 +15,7 @@
       <string name="bounds">414 390 434 487 </string>
     </object>
     <object class="IBCarbonStaticText" id="181">
-      <string name="title">Second Life appears to have crashed or frozen the last time it ran.&#10;&#10;This crash reporter collects information about your computer&apos;s hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only.&#10;&#10;In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!&#10;&#10;This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/&#10;&#10;If you don&apos;t wish to send Linden Lab a crash report, press Cancel.&#10;</string>
+      <string name="title">Second Life appears to have crashed or frozen the last time it ran.&#10;&#10;This crash reporter collects information about your computer&apos;s hardware configuration, operating system, and some Second Life logs, all of which are used for debugging purposes only.&#10;&#10;In the space below, please briefly describe what you were doing or trying to do just prior to the crash. Thank you for your help!&#10;&#10;This report is NOT read by Customer Support. If you have billing or other questions, please go to: http://www.secondlife.com/support/&#10;&#10;If you don&apos;t wish to send Linden Lab a crash report, press Don&apos;t Send.&#10;</string>
       <string name="bounds">20 20 231 487 </string>
     </object>
     <object class="IBCarbonWindow" id="166">
diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp
index bec8cce04e2b6fe9e88c5aeab620dc17d67ac8ee..b555e92b96e563f42e167d1c3585aca610418338 100644
--- a/indra/mac_crash_logger/llcrashloggermac.cpp
+++ b/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -29,9 +29,6 @@
 
 #include <Carbon/Carbon.h>
 #include <iostream>
-#include <sstream>
-
-#include "boost/tokenizer.hpp"
 
 #include "indra_constants.h"	// CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
 #include "llerror.h"
@@ -247,7 +244,7 @@ bool LLCrashLoggerMac::mainLoop()
 
 void LLCrashLoggerMac::updateApplication(const std::string& message)
 {
-	LLCrashLogger::updateApplication();
+	LLCrashLogger::updateApplication(message);
 }
 
 bool LLCrashLoggerMac::cleanup()
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
index 20b491c4014374c38779f6402acdaaa1b9f512b1..6571b352417da0fca755d3db0d60ed8caa2171aa 100644
--- a/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -25,22 +25,23 @@
  */
 
 #include "linden_common.h"
-
 #include "llcrashloggermac.h"
 
 int main(int argc, char **argv)
 {
-	//time(&gLaunchTime);
-	
-	llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
+	llinfos << "Starting crash reporter." << llendl;
 
 	LLCrashLoggerMac app;
 	app.parseCommandOptions(argc, argv);
-	if(!app.init())
+
+	if (! app.init())
 	{
-		return 0;
+		llwarns << "Unable to initialize application." << llendl;
+		return 1;
 	}
+
 	app.mainLoop();
-		
+	app.cleanup();
+	llinfos << "Crash reporter finished normally." << llendl;
 	return 0;
 }
diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml
index 89e5949fbe93ba3f8b6644d79b9c0e9804c4bccc..15434f2b8f743f921c0f5555e47b4c0f039f3604 100644
--- a/indra/newview/app_settings/cmd_line.xml
+++ b/indra/newview/app_settings/cmd_line.xml
@@ -220,8 +220,7 @@
     <map>
       <key>desc</key>
       <string>Set the detail level. 
-              0 - low, 1 - medium, 2 - high, 3 - ultra
-       </string>
+0 - low, 1 - medium, 2 - high, 3 - ultra</string>
       <key>count</key>
       <integer>1</integer>
     </map>
@@ -229,10 +228,7 @@
     <key>setdefault</key>
     <map>
       <key>desc</key>
-      <string> specify the value of a particular
-               configuration variable which can be
-               overridden by settings.xml
-      </string>
+      <string>specify the value of a particular configuration variable which can be overridden by settings.xml.</string>
       <key>count</key>
       <integer>2</integer>
       <!-- Special case. Mapped to settings procedurally. -->
@@ -241,10 +237,7 @@
     <key>set</key>
     <map>
       <key>desc</key>
-      <string> specify the value of a particular
-               configuration variable that
-               overrides all other settings
-      </string>
+      <string>specify the value of a particular configuration variable that overrides all other settings.</string>
       <key>count</key>
       <integer>2</integer>
       <key>compose</key>
diff --git a/indra/newview/app_settings/settings_files.xml b/indra/newview/app_settings/settings_files.xml
index 079a54f95780d88d88b78f3287233c8721592f3d..bfc09286e39abd63cadb21d20bf4928230fa690b 100644
--- a/indra/newview/app_settings/settings_files.xml
+++ b/indra/newview/app_settings/settings_files.xml
@@ -20,7 +20,8 @@
           file_name="settings.xml"
           file_name_setting="ClientSettingsFile"/>
     <file name="CrashSettings"
-          file_name="settings_crash_behavior"/>
+          file_name="settings_crash_behavior.xml"
+          file_name_setting="CrashSettingsFile"/>
     <file name="Warnings"
           file_name="ignorable_dialogs.xml"
           file_name_setting="WarningSettingsFile"/>
@@ -61,4 +62,4 @@
           file_name="colors.xml"
           file_name_setting="SkinningSettingsFile"/>
   </group>
-</settings_files>
\ No newline at end of file
+</settings_files>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
old mode 100644
new mode 100755
index 8954937f695f6a246de1b53c625e78d5ded869f3..492cfe7c1ba8cde93ca0be2c85d29a8a76030e4b
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -577,7 +577,10 @@ void LLAgent::setFlying(BOOL fly)
 // static
 void LLAgent::toggleFlying()
 {
-	LLToolPie::instance().stopClickToWalk();
+	if ( gAgent.mAutoPilot )
+	{
+		LLToolPie::instance().stopClickToWalk();
+	}
 
 	BOOL fly = !gAgent.getFlying();
 
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 1d9519d675c71c4ff62c53bb8b249ed6ce39e9a2..92e0513464ddb69c76277ac3b4570cdfe0974d66 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -56,6 +56,7 @@
 #include "llallocator.h"
 #include "llares.h" 
 #include "llcurl.h"
+#include "llcalc.h"
 #include "lltexturestats.h"
 #include "lltexturestats.h"
 #include "llviewerwindow.h"
@@ -468,18 +469,6 @@ void request_initial_instant_messages()
 	}
 }
 
-// A settings system callback for CrashSubmitBehavior
-bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue)
-{
-	S32 cb = newvalue.asInteger();
-	const S32 NEVER_SUBMIT_REPORT = 2;
-	if(cb == NEVER_SUBMIT_REPORT)
-	{
-		LLAppViewer::instance()->destroyMainloopTimeout();
-	}
-	return true;
-}
-
 // Use these strictly for things that are constructed at startup,
 // or for things that are performance critical.  JC
 static void settings_to_globals()
@@ -611,9 +600,6 @@ bool LLAppViewer::sendURLToOtherInstance(const std::string& url)
 // Static members.
 // The single viewer app.
 LLAppViewer* LLAppViewer::sInstance = NULL;
-
-const std::string LLAppViewer::sGlobalSettingsName = "Global"; 
-
 LLTextureCache* LLAppViewer::sTextureCache = NULL; 
 LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; 
 LLTextureFetch* LLAppViewer::sTextureFetch = NULL; 
@@ -771,16 +757,6 @@ bool LLAppViewer::init()
 	LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL;
 	LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL;
 
-	// Get the single value from the crash settings file, if it exists
-	std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-	gCrashSettings.loadFromFile(crash_settings_filename);
-	if(gSavedSettings.getBOOL("IgnoreAllNotifications"))
-	{
-		gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ALWAYS_SEND);
-		gCrashSettings.saveToFile(crash_settings_filename, FALSE);
-	}
-	LL_INFOS("InitInfo") << "Crash settings done." << LL_ENDL ;
-
 	/////////////////////////////////////////////////
 	// OS-specific login dialogs
 	/////////////////////////////////////////////////
@@ -1055,7 +1031,7 @@ bool LLAppViewer::init()
 	//EXT-7013 - On windows for some locale (Japanese) standard 
 	//datetime formatting functions didn't support some parameters such as "weekday".
 	//Names for days and months localized in xml are also useful for Polish locale(STORM-107).
-	std::string language = LLControlGroup::getInstance(sGlobalSettingsName)->getString("Language");
+	std::string language = gSavedSettings.getString("Language");
 	if(language == "ja" || language == "pl")
 	{
 		LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames"));
@@ -1543,7 +1519,9 @@ bool LLAppViewer::cleanup()
 	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
 
 	LLWorldMap::getInstance()->reset(); // release any images
-	
+
+	LLCalc::cleanUp();
+
 	llinfos << "Global stuff deleted" << llendflush;
 
 	if (gAudiop)
@@ -1706,10 +1684,6 @@ bool LLAppViewer::cleanup()
 		llinfos << "Saved settings" << llendflush;
 	}
 
-	std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-	// save all settings, even if equals defaults
-	gCrashSettings.saveToFile(crash_settings_filename, FALSE);
-
 	std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings"));
 	gWarningSettings.saveToFile(warnings_settings_filename, TRUE);
 
@@ -1839,7 +1813,6 @@ bool LLAppViewer::cleanup()
 	
 	gSavedSettings.cleanup();
 	LLUIColorTable::instance().clear();
-	gCrashSettings.cleanup();
 
 	LLWatchdog::getInstance()->cleanup();
 
@@ -1982,7 +1955,6 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
 		llerrs << "Invalid settings location list" << llendl;
 	}
 
-	LLControlGroup* global_settings = LLControlGroup::getInstance(sGlobalSettingsName);  
 	for(LLInitParam::ParamIterator<SettingsGroup>::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end();
 		it != end_it;
 		++it)
@@ -2015,11 +1987,15 @@ bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key,
 			std::string full_settings_path;
 
 			if (file_it->file_name_setting.isProvided() 
-				&& global_settings->controlExists(file_it->file_name_setting))
+				&& gSavedSettings.controlExists(file_it->file_name_setting))
 			{
 				// try to find filename stored in file_name_setting control
-				full_settings_path = global_settings->getString(file_it->file_name_setting);
-				if (!gDirUtilp->fileExists(full_settings_path))
+				full_settings_path = gSavedSettings.getString(file_it->file_name_setting);
+				if (full_settings_path.empty())
+				{
+					continue;
+				}
+				else if (!gDirUtilp->fileExists(full_settings_path))
 				{
 					// search in default path
 					full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path);
@@ -2165,8 +2141,6 @@ bool LLAppViewer::initConfiguration()
 	gSavedSettings.setS32("WatchdogEnabled", 0);
 #endif
 	
-	gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2));	
-
 	// These are warnings that appear on the first experience of that condition.
 	// They are already set in the settings_default.xml file, but still need to be added to LLFirstUse
 	// for disable/reset ability
@@ -2297,15 +2271,33 @@ bool LLAppViewer::initConfiguration()
             {
                 const std::string& name = *itr;
                 const std::string& value = *(++itr);
-				LLControlVariable* c = LLControlGroup::getInstance(sGlobalSettingsName)->getControl(name);
-                if(c)
+                std::string name_part;
+                std::string group_part;
+				LLControlVariable* control = NULL;
+
+				// Name can be further split into ControlGroup.Name, with the default control group being Global
+				size_t pos = name.find('.');
+				if (pos != std::string::npos)
+				{
+					group_part = name.substr(0, pos);
+					name_part = name.substr(pos+1);
+					llinfos << "Setting " << group_part << "." << name_part << " to " << value << llendl;
+					LLControlGroup* g = LLControlGroup::getInstance(group_part);
+					if (g) control = g->getControl(name_part);
+				}
+				else
+				{
+					llinfos << "Setting Global." << name << " to " << value << llendl;
+					control = gSavedSettings.getControl(name);
+				}
+
+                if (control)
                 {
-                    c->setValue(value, false);
+                    control->setValue(value, false);
                 }
                 else
                 {
-                    llwarns << "'--set' specified with unknown setting: '"
-                        << name << "'." << llendl;
+					llwarns << "Failed --set " << name << ": setting name unknown." << llendl;
                 }
             }
         }
@@ -2762,7 +2754,8 @@ void LLAppViewer::checkForCrash(void)
         // Pop up a freeze or crash warning dialog
         //
         S32 choice;
-        if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_ASK)
+	const S32 cb = gCrashSettings.getS32("CrashSubmitBehavior");
+        if(cb == CRASH_BEHAVIOR_ASK)
         {
             std::ostringstream msg;
 			msg << LLTrans::getString("MBFrozenCrashed");
@@ -2771,7 +2764,7 @@ void LLAppViewer::checkForCrash(void)
                                   alert,
                                   OSMB_YESNO);
         } 
-        else if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_NEVER_SEND)
+        else if(cb == CRASH_BEHAVIOR_NEVER_SEND)
         {
             choice = OSBTN_NO;
         }
@@ -2828,10 +2821,9 @@ bool LLAppViewer::initWindow()
 	LL_INFOS("AppInit") << "gViewerwindow created." << LL_ENDL;
 
 	// Need to load feature table before cheking to start watchdog.
-	const S32 NEVER_SUBMIT_REPORT = 2;
 	bool use_watchdog = false;
 	int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled");
-	if(watchdog_enabled_setting == -1)
+	if (watchdog_enabled_setting == -1)
 	{
 		use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled");
 	}
@@ -2841,8 +2833,7 @@ bool LLAppViewer::initWindow()
 		use_watchdog = bool(watchdog_enabled_setting);
 	}
 
-	bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT;
-	if(use_watchdog && send_reports)
+	if (use_watchdog)
 	{
 		LLWatchdog::getInstance()->init(watchdog_killer_callback);
 	}
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index 714e0e6163b8a939ac3f9603cafc80a17e0f5d8f..48d02dfeaade77a3b13729edbb1ab2925af61897 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -361,46 +361,35 @@ void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
 	}
 	else
 	{
-		const S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-
-		// Always generate the report, have the logger do the asking, and
-		// don't wait for the logger before exiting (-> total cleanup).
-		if (CRASH_BEHAVIOR_NEVER_SEND != cb)
-		{	
-			// launch the actual crash logger
-			const char* ask_dialog = "-dialog";
-			if (CRASH_BEHAVIOR_ASK != cb)
-				ask_dialog = ""; // omit '-dialog' option
-			const char * cmdargv[] =
-				{cmd.c_str(),
-				 ask_dialog,
-				 "-user",
-				 (char*)LLGridManager::getInstance()->getGridLabel().c_str(),
-				 "-name",
-				 LLAppViewer::instance()->getSecondLifeTitle().c_str(),
-				 NULL};
-			fflush(NULL);
-			pid_t pid = fork();
-			if (pid == 0)
-			{ // child
-				execv(cmd.c_str(), (char* const*) cmdargv);		/* Flawfinder: ignore */
-				llwarns << "execv failure when trying to start " << cmd << llendl;
-				_exit(1); // avoid atexit()
+		// launch the actual crash logger
+		const char * cmdargv[] =
+			{cmd.c_str(),
+			 "-user",
+			 (char*)LLGridManager::getInstance()->getGridLabel().c_str(),
+			 "-name",
+			 LLAppViewer::instance()->getSecondLifeTitle().c_str(),
+			 NULL};
+		fflush(NULL);
+		pid_t pid = fork();
+		if (pid == 0)
+		{ // child
+			execv(cmd.c_str(), (char* const*) cmdargv);		/* Flawfinder: ignore */
+			llwarns << "execv failure when trying to start " << cmd << llendl;
+			_exit(1); // avoid atexit()
+		} 
+		else
+		{
+			if (pid > 0)
+			{
+				// DO NOT wait for child proc to die; we want
+				// the logger to outlive us while we quit to
+				// free up the screen/keyboard/etc.
+				////int childExitStatus;
+				////waitpid(pid, &childExitStatus, 0);
 			} 
 			else
 			{
-				if (pid > 0)
-				{
-					// DO NOT wait for child proc to die; we want
-					// the logger to outlive us while we quit to
-					// free up the screen/keyboard/etc.
-					////int childExitStatus;
-					////waitpid(pid, &childExitStatus, 0);
-				} 
-				else
-				{
-					llwarns << "fork failure." << llendl;
-				}
+				llwarns << "fork failure." << llendl;
 			}
 		}
 		// Sometimes signals don't seem to quit the viewer.  Also, we may
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 445bd208efeced91c75e93087f63ee0793558640..f94c843ad9b5ee1d85f96c103486c3d768f00fa7 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -518,11 +518,7 @@ void LLAppViewerWin32::handleCrashReporting(bool reportFreeze)
 	}
 	else
 	{
-		S32 cb = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
-		if(cb != CRASH_BEHAVIOR_NEVER_SEND)
-		{
-			_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
-		}
+		_spawnl(_P_NOWAIT, exe_path.c_str(), arg_str, NULL);
 	}
 }
 
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
index d7ba4ea4706b89604fdbe19faa44acd5d8fb0d17..5b9a449be15bd7a02f12c314e27b2b0a062fa309 100644
--- a/indra/newview/llassetuploadresponders.cpp
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -449,7 +449,7 @@ void LLSendTexLayerResponder::uploadComplete(const LLSD& content)
 	std::string result = content["state"];
 	LLUUID new_id = content["new_asset"];
 
-	llinfos << "result: " << result << "new_id:" << new_id << llendl;
+	llinfos << "result: " << result << " new_id: " << new_id << llendl;
 	if (result == "complete"
 		&& mBakedUploadData != NULL)
 	{	// Invoke 
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
index 70871b62e2f0fe4b53c455010dbd5a6854cb4ddc..381b919c4a4c15587d0227d22bedfb89a167a43b 100644
--- a/indra/newview/llassetuploadresponders.h
+++ b/indra/newview/llassetuploadresponders.h
@@ -112,6 +112,7 @@ class LLNewAgentInventoryVariablePriceResponder :
 struct LLBakedUploadData;
 class LLSendTexLayerResponder : public LLAssetUploadResponder
 {
+	LOG_CLASS(LLSendTexLayerResponder);
 public:
 	LLSendTexLayerResponder(const LLSD& post_data,
 							const LLUUID& vfile_id,
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 7848484ac6d34b28cc33727d041f4f643cf146f8..5fd262a72019b550b898b38145b23c191b9215db 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -751,10 +751,7 @@ void LLFloaterPreference::onBtnOK()
 		closeFloater(false);
 
 		LLUIColorTable::instance().saveUserSettings();
-		gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE );
-		std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
-		// save all settings, even if equals defaults
-		gCrashSettings.saveToFile(crash_settings_filename, FALSE);
+		gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);
 	}
 	else
 	{
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index 07c7f3598965cf24c01619675e81cf0841b536bb..a4f6921f9879ee4c4d73bc1347808c58d05b2540 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -30,6 +30,7 @@
 #include "llpanelface.h"
  
 // library includes
+#include "llcalc.h"
 #include "llerror.h"
 #include "llfocusmgr.h"
 #include "llrect.h"
@@ -926,6 +927,16 @@ void LLPanelFace::getState()
 				getChildView("button apply")->setEnabled(enabled);
 			}
 		}
+
+		// Set variable values for numeric expressions
+		LLCalc* calcp = LLCalc::getInstance();
+		calcp->setVar(LLCalc::TEX_U_SCALE, childGetValue("TexScaleU").asReal());
+		calcp->setVar(LLCalc::TEX_V_SCALE, childGetValue("TexScaleV").asReal());
+		calcp->setVar(LLCalc::TEX_U_OFFSET, childGetValue("TexOffsetU").asReal());
+		calcp->setVar(LLCalc::TEX_V_OFFSET, childGetValue("TexOffsetV").asReal());
+		calcp->setVar(LLCalc::TEX_ROTATION, childGetValue("TexRot").asReal());
+		calcp->setVar(LLCalc::TEX_TRANSPARENCY, childGetValue("ColorTrans").asReal());
+		calcp->setVar(LLCalc::TEX_GLOW, childGetValue("glow").asReal());
 	}
 	else
 	{
@@ -961,6 +972,16 @@ void LLPanelFace::getState()
 		//getChildView("has media")->setEnabled(FALSE);
 		//getChildView("media info set")->setEnabled(FALSE);
 		
+
+		// Set variable values for numeric expressions
+		LLCalc* calcp = LLCalc::getInstance();
+		calcp->clearVar(LLCalc::TEX_U_SCALE);
+		calcp->clearVar(LLCalc::TEX_V_SCALE);
+		calcp->clearVar(LLCalc::TEX_U_OFFSET);
+		calcp->clearVar(LLCalc::TEX_V_OFFSET);
+		calcp->clearVar(LLCalc::TEX_ROTATION);
+		calcp->clearVar(LLCalc::TEX_TRANSPARENCY);
+		calcp->clearVar(LLCalc::TEX_GLOW);		
 	}
 }
 
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 52917ff20b3dfbe5e30fca4c53c52a47cf8496be..c222bbb191245ff0bfb83522b0fdac126dbf014b 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -41,6 +41,7 @@
 // project includes
 #include "llagent.h"
 #include "llbutton.h"
+#include "llcalc.h"
 #include "llcheckboxctrl.h"
 #include "llcolorswatch.h"
 #include "llcombobox.h"
@@ -318,6 +319,8 @@ void LLPanelObject::getState( )
 		}
 	}
 
+	LLCalc* calcp = LLCalc::getInstance();
+
 	LLVOVolume *volobjp = NULL;
 	if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
 	{
@@ -334,6 +337,7 @@ void LLPanelObject::getState( )
 
 		// Disable all text input fields
 		clearCtrls();
+		calcp->clearAllVariables();
 		return;
 	}
 
@@ -360,12 +364,18 @@ void LLPanelObject::getState( )
 		mCtrlPosX->set( vec.mV[VX] );
 		mCtrlPosY->set( vec.mV[VY] );
 		mCtrlPosZ->set( vec.mV[VZ] );
+		calcp->setVar(LLCalc::X_POS, vec.mV[VX]);
+		calcp->setVar(LLCalc::Y_POS, vec.mV[VY]);
+		calcp->setVar(LLCalc::Z_POS, vec.mV[VZ]);
 	}
 	else
 	{
 		mCtrlPosX->clear();
 		mCtrlPosY->clear();
 		mCtrlPosZ->clear();
+		calcp->clearVar(LLCalc::X_POS);
+		calcp->clearVar(LLCalc::Y_POS);
+		calcp->clearVar(LLCalc::Z_POS);
 	}
 
 
@@ -380,12 +390,18 @@ void LLPanelObject::getState( )
 		mCtrlScaleX->set( vec.mV[VX] );
 		mCtrlScaleY->set( vec.mV[VY] );
 		mCtrlScaleZ->set( vec.mV[VZ] );
+		calcp->setVar(LLCalc::X_SCALE, vec.mV[VX]);
+		calcp->setVar(LLCalc::Y_SCALE, vec.mV[VY]);
+		calcp->setVar(LLCalc::Z_SCALE, vec.mV[VZ]);
 	}
 	else
 	{
 		mCtrlScaleX->clear();
 		mCtrlScaleY->clear();
 		mCtrlScaleZ->clear();
+		calcp->setVar(LLCalc::X_SCALE, 0.f);
+		calcp->setVar(LLCalc::Y_SCALE, 0.f);
+		calcp->setVar(LLCalc::Z_SCALE, 0.f);
 	}
 
 	mLabelSize->setEnabled( enable_scale );
@@ -405,12 +421,18 @@ void LLPanelObject::getState( )
 		mCtrlRotX->set( mCurEulerDegrees.mV[VX] );
 		mCtrlRotY->set( mCurEulerDegrees.mV[VY] );
 		mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] );
+		calcp->setVar(LLCalc::X_ROT, mCurEulerDegrees.mV[VX]);
+		calcp->setVar(LLCalc::Y_ROT, mCurEulerDegrees.mV[VY]);
+		calcp->setVar(LLCalc::Z_ROT, mCurEulerDegrees.mV[VZ]);
 	}
 	else
 	{
 		mCtrlRotX->clear();
 		mCtrlRotY->clear();
 		mCtrlRotZ->clear();
+		calcp->clearVar(LLCalc::X_ROT);
+		calcp->clearVar(LLCalc::Y_ROT);
+		calcp->clearVar(LLCalc::Z_ROT);
 	}
 
 	mLabelRotation->setEnabled( enable_rotate );
@@ -625,9 +647,9 @@ void LLPanelObject::getState( )
 		F32 end_t	= volume_params.getEndT();
 
 		// Hollowness
-		F32 hollow = volume_params.getHollow();
-		mSpinHollow->set( 100.f * hollow );
-
+		F32 hollow = 100.f * volume_params.getHollow();
+		mSpinHollow->set( hollow );
+		calcp->setVar(LLCalc::HOLLOW, hollow);
 		// All hollow objects allow a shape to be selected.
 		if (hollow > 0.f)
 		{
@@ -679,6 +701,10 @@ void LLPanelObject::getState( )
 		mSpinCutEnd		->set( cut_end );
 		mCtrlPathBegin	->set( adv_cut_begin );
 		mCtrlPathEnd	->set( adv_cut_end );
+		calcp->setVar(LLCalc::CUT_BEGIN, cut_begin);
+		calcp->setVar(LLCalc::CUT_END, cut_end);
+		calcp->setVar(LLCalc::PATH_BEGIN, adv_cut_begin);
+		calcp->setVar(LLCalc::PATH_END, adv_cut_end);
 
 		// Twist
 		F32 twist		= volume_params.getTwist();
@@ -697,18 +723,24 @@ void LLPanelObject::getState( )
 
 		mSpinTwist		->set( twist );
 		mSpinTwistBegin	->set( twist_begin );
+		calcp->setVar(LLCalc::TWIST_END, twist);
+		calcp->setVar(LLCalc::TWIST_BEGIN, twist_begin);
 
 		// Shear
 		F32 shear_x = volume_params.getShearX();
 		F32 shear_y = volume_params.getShearY();
 		mSpinShearX->set( shear_x );
 		mSpinShearY->set( shear_y );
+		calcp->setVar(LLCalc::X_SHEAR, shear_x);
+		calcp->setVar(LLCalc::Y_SHEAR, shear_y);
 
 		// Taper
 		F32 taper_x	= volume_params.getTaperX();
 		F32 taper_y = volume_params.getTaperY();
 		mSpinTaperX->set( taper_x );
 		mSpinTaperY->set( taper_y );
+		calcp->setVar(LLCalc::X_TAPER, taper_x);
+		calcp->setVar(LLCalc::Y_TAPER, taper_y);
 
 		// Radius offset.
 		F32 radius_offset = volume_params.getRadiusOffset();
@@ -738,10 +770,12 @@ void LLPanelObject::getState( )
 			}
 		}
 		mSpinRadiusOffset->set( radius_offset);
+		calcp->setVar(LLCalc::RADIUS_OFFSET, radius_offset);
 
 		// Revolutions
 		F32 revolutions = volume_params.getRevolutions();
 		mSpinRevolutions->set( revolutions );
+		calcp->setVar(LLCalc::REVOLUTIONS, revolutions);
 		
 		// Skew
 		F32 skew	= volume_params.getSkew();
@@ -766,6 +800,7 @@ void LLPanelObject::getState( )
 			}
 		}
 		mSpinSkew->set( skew );
+		calcp->setVar(LLCalc::SKEW, skew);
 	}
 	
 	// Compute control visibility, label names, and twist range.
@@ -869,6 +904,8 @@ void LLPanelObject::getState( )
 	case MI_RING:
 		mSpinScaleX->set( scale_x );
 		mSpinScaleY->set( scale_y );
+		calcp->setVar(LLCalc::X_HOLE, scale_x);
+		calcp->setVar(LLCalc::Y_HOLE, scale_y);
 		mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
 		mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
 		mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
@@ -883,6 +920,14 @@ void LLPanelObject::getState( )
 			mSpinScaleX->setMaxValue(1.f);
 			mSpinScaleY->setMinValue(-1.f);
 			mSpinScaleY->setMaxValue(1.f);
+
+			// Torus' Hole Size is Box/Cyl/Prism's Taper
+			calcp->setVar(LLCalc::X_TAPER, 1.f - scale_x);
+			calcp->setVar(LLCalc::Y_TAPER, 1.f - scale_y);
+
+			// Box/Cyl/Prism have no hole size
+			calcp->setVar(LLCalc::X_HOLE, 0.f);
+			calcp->setVar(LLCalc::Y_HOLE, 0.f);
 		}
 		break;
 	}
diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp
index 0645fd8a54a23b1728a27b111931a45c2fcf07f4..f19b54c1d4d9350e7cd02aca0d5968a6fd328211 100644
--- a/indra/newview/llpanelwearing.cpp
+++ b/indra/newview/llpanelwearing.cpp
@@ -38,6 +38,8 @@
 #include "llsidetray.h"
 #include "llviewermenu.h"
 #include "llwearableitemslist.h"
+#include "llsdserialize.h"
+#include "llclipboard.h"
 
 // Context menu and Gear menu helper.
 static void edit_outfit()
@@ -58,6 +60,7 @@ class LLWearingGearMenu
 
 		registrar.add("Gear.Edit", boost::bind(&edit_outfit));
 		registrar.add("Gear.TakeOff", boost::bind(&LLWearingGearMenu::onTakeOff, this));
+		registrar.add("Gear.Copy", boost::bind(&LLPanelWearing::copyToClipboard, mPanelWearing));
 
 		enable_registrar.add("Gear.OnEnable", boost::bind(&LLPanelWearing::isActionEnabled, mPanelWearing, _2));
 
@@ -280,4 +283,25 @@ void LLPanelWearing::getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const
 	mCOFItemsList->getSelectedUUIDs(selected_uuids);
 }
 
+void LLPanelWearing::copyToClipboard()
+{
+	std::string text;
+	std::vector<LLSD> data;
+	mCOFItemsList->getValues(data);
+
+	for(std::vector<LLSD>::const_iterator iter = data.begin(); iter != data.end();)
+	{
+		LLSD uuid = (*iter);
+		LLViewerInventoryItem* item = gInventory.getItem(uuid);
+
+		iter++;
+		if (item != NULL)
+		{
+			// Append a newline to all but the last line
+			text += iter != data.end() ? item->getName() + "\n" : item->getName();
+		}
+	}
+
+	gClipboard.copyFromString(utf8str_to_wstring(text));
+}
 // EOF
diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h
index 157b2c4c5f0ed35c5d063377897cffa1cdfc7ae3..9a212b3cca92c2feafd0e644c1ba1f44c5794e7c 100644
--- a/indra/newview/llpanelwearing.h
+++ b/indra/newview/llpanelwearing.h
@@ -60,6 +60,8 @@ class LLPanelWearing : public LLPanelAppearanceTab
 
 	/*virtual*/ void getSelectedItemsUUIDs(uuid_vec_t& selected_uuids) const;
 
+	/*virtual*/ void copyToClipboard();
+
 	boost::signals2::connection setSelectionChangeCallback(commit_callback_t cb);
 
 	bool hasItemSelected();
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
index 500c2a7b863dcd74d7e29a3b22b48e5c4aa3225d..bd41aa64f0a6df82afab3b367e54d6218a2b7398 100644
--- a/indra/newview/lltexlayer.cpp
+++ b/indra/newview/lltexlayer.cpp
@@ -51,6 +51,9 @@
 
 using namespace LLVOAvatarDefines;
 
+static const S32 BAKE_UPLOAD_ATTEMPTS = 7;
+static const F32 BAKE_UPLOAD_RETRY_DELAY = 2.f; // actual delay grows by power of 2 each attempt
+
 class LLTexLayerInfo
 {
 	friend class LLTexLayer;
@@ -93,11 +96,13 @@ class LLTexLayerInfo
 //-----------------------------------------------------------------------------
 LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar,
 									 LLTexLayerSet* layerset,
-									 const LLUUID& id) : 
+									 const LLUUID& id,
+									 bool highest_res) :
 	mAvatar(avatar),
 	mTexLayerSet(layerset),
 	mID(id),
-	mStartTime(LLFrameTimer::getTotalTime())		// Record starting time
+	mStartTime(LLFrameTimer::getTotalTime()),		// Record starting time
+	mIsHighestRes(highest_res)
 { 
 }
 
@@ -116,6 +121,7 @@ LLTexLayerSetBuffer::LLTexLayerSetBuffer(LLTexLayerSet* const owner,
 	mUploadPending(FALSE), // Not used for any logic here, just to sync sending of updates
 	mNeedsUpload(FALSE),
 	mNumLowresUploads(0),
+	mUploadFailCount(0),
 	mNeedsUpdate(TRUE),
 	mNumLowresUpdates(0),
 	mTexLayerSet(owner)
@@ -204,6 +210,7 @@ void LLTexLayerSetBuffer::cancelUpload()
 	mNeedsUpload = FALSE;
 	mUploadPending = FALSE;
 	mNeedsUploadTimer.pause();
+	mUploadRetryTimer.reset();
 }
 
 void LLTexLayerSetBuffer::pushProjection() const
@@ -356,25 +363,38 @@ BOOL LLTexLayerSetBuffer::isReadyToUpload() const
 	if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries.
 	if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) return FALSE; // Don't upload if avatar is using composites.
 
-	// If we requested an upload and have the final LOD ready, then upload.
-	if (mTexLayerSet->isLocalTextureDataFinal()) return TRUE;
-
-	// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
-	// we aren't doing uploads too frequently.
-	const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
-	if (texture_timeout != 0)
+	BOOL ready = FALSE;
+	if (mTexLayerSet->isLocalTextureDataFinal())
+	{
+		// If we requested an upload and have the final LOD ready, upload (or wait a while if this is a retry)
+		if (mUploadFailCount == 0)
+		{
+			ready = TRUE;
+		}
+		else
+		{
+			ready = mUploadRetryTimer.getElapsedTimeF32() >= BAKE_UPLOAD_RETRY_DELAY * (1 << (mUploadFailCount - 1));
+		}
+	}
+	else
 	{
-		// The timeout period increases exponentially between every lowres upload in order to prevent
-		// spamming the server with frequent uploads.
-		const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
+		// Upload if we've hit a timeout.  Upload is a pretty expensive process so we need to make sure
+		// we aren't doing uploads too frequently.
+		const U32 texture_timeout = gSavedSettings.getU32("AvatarBakedTextureUploadTimeout");
+		if (texture_timeout != 0)
+		{
+			// The timeout period increases exponentially between every lowres upload in order to prevent
+			// spamming the server with frequent uploads.
+			const U32 texture_timeout_threshold = texture_timeout*(1 << mNumLowresUploads);
 
-		// If we hit our timeout and have textures available at even lower resolution, then upload.
-		const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
-		const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
-		if (has_lower_lod && is_upload_textures_timeout) return TRUE; 
+			// If we hit our timeout and have textures available at even lower resolution, then upload.
+			const BOOL is_upload_textures_timeout = mNeedsUploadTimer.getElapsedTimeF32() >= texture_timeout_threshold;
+			const BOOL has_lower_lod = mTexLayerSet->isLocalTextureDataAvailable();
+			ready = has_lower_lod && is_upload_textures_timeout;
+		}
 	}
 
-	return FALSE;
+	return ready;
 }
 
 BOOL LLTexLayerSetBuffer::isReadyToUpdate() const
@@ -482,17 +502,20 @@ void LLTexLayerSetBuffer::doUpload()
 			
 			if (valid)
 			{
+				const bool highest_lod = mTexLayerSet->isLocalTextureDataFinal();
 				// Baked_upload_data is owned by the responder and deleted after the request completes.
 				LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, 
 																			 this->mTexLayerSet, 
-																			 asset_id);
+																			 asset_id,
+																			 highest_lod);
 				// upload ID is used to avoid overlaps, e.g. when the user rapidly makes two changes outside of Face Edit.
 				mUploadID = asset_id;
 
 				// Upload the image
 				const std::string url = gAgent.getRegion()->getCapability("UploadBakedTexture");
 				if(!url.empty()
-					&& !LLPipeline::sForceOldBakedUpload) // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& !LLPipeline::sForceOldBakedUpload // toggle debug setting UploadBakedTexOld to change between the new caps method and old method
+					&& (mUploadFailCount < (BAKE_UPLOAD_ATTEMPTS - 1))) // Try last ditch attempt via asset store if cap upload is failing.
 				{
 					LLSD body = LLSD::emptyMap();
 					// The responder will call LLTexLayerSetBuffer::onTextureUploadComplete()
@@ -511,7 +534,6 @@ void LLTexLayerSetBuffer::doUpload()
 					llinfos << "Baked texture upload via Asset Store." <<  llendl;
 				}
 
-				const BOOL highest_lod = mTexLayerSet->isLocalTextureDataFinal();	
 				if (highest_lod)
 				{
 					// Sending the final LOD for the baked texture.  All done, pause 
@@ -603,14 +625,15 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
 {
 	LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
 
-	if ((result == 0) &&
-		isAgentAvatarValid() &&
+	if (isAgentAvatarValid() &&
 		!gAgentAvatarp->isDead() &&
 		(baked_upload_data->mAvatar == gAgentAvatarp) && // Sanity check: only the user's avatar should be uploading textures.
 		(baked_upload_data->mTexLayerSet->hasComposite()))
 	{
 		LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getComposite();
-			
+		S32 failures = layerset_buffer->mUploadFailCount;
+		layerset_buffer->mUploadFailCount = 0;
+
 		if (layerset_buffer->mUploadID.isNull())
 		{
 			// The upload got canceled, we should be in the
@@ -626,20 +649,28 @@ void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
 		{
 			// This is the upload we're currently waiting for.
 			layerset_buffer->mUploadID.setNull();
+			const std::string name(baked_upload_data->mTexLayerSet->getBodyRegionName());
+			const std::string resolution = baked_upload_data->mIsHighestRes ? " full res " : " low res ";
 			if (result >= 0)
 			{
-				layerset_buffer->mUploadPending = FALSE;
+				layerset_buffer->mUploadPending = FALSE; // Allows sending of AgentSetAppearance later
 				LLVOAvatarDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->mTexLayerSet);
 				// Update baked texture info with the new UUID
 				U64 now = LLFrameTimer::getTotalTime();		// Record starting time
-				llinfos << "Baked texture upload took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
+				llinfos << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << llendl;
 				gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
 			}
 			else
 			{	
-				// Avatar appearance is changing, ignore the upload results
-				llinfos << "Baked upload failed. Reason: " << result << llendl;
-				// *FIX: retry upload after n seconds, asset server could be busy
+				++failures;
+				S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes
+				llwarns << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << llendl;
+				if (failures < max_attempts)
+				{
+					layerset_buffer->mUploadFailCount = failures;
+					layerset_buffer->mUploadRetryTimer.start();
+					layerset_buffer->requestUpload();
+				}
 			}
 		}
 		else
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
index 2d710d2dce862294abb6f877c4593e7597fb75e1..85dadb213c55d777bdb2e329d677a43830d34f8d 100644
--- a/indra/newview/lltexlayer.h
+++ b/indra/newview/lltexlayer.h
@@ -312,6 +312,8 @@ class LLTexLayerSetBuffer : public LLViewerDynamicTexture
 	BOOL					mUploadPending; 				// Whether we have received back the new baked textures
 	LLUUID					mUploadID; 						// The current upload process (null if none).
 	LLFrameTimer    		mNeedsUploadTimer; 				// Tracks time since upload was requested and performed.
+	S32						mUploadFailCount;				// Number of consecutive upload failures
+	LLFrameTimer    		mUploadRetryTimer; 				// Tracks time since last upload failure.
 
 	//--------------------------------------------------------------------
 	// Updates
@@ -363,12 +365,14 @@ struct LLBakedUploadData
 {
 	LLBakedUploadData(const LLVOAvatarSelf* avatar, 
 					  LLTexLayerSet* layerset, 
-					  const LLUUID& id);
+					  const LLUUID& id,
+					  bool highest_res);
 	~LLBakedUploadData() {}
 	const LLUUID				mID;
 	const LLVOAvatarSelf*		mAvatar; // note: backlink only; don't LLPointer 
 	LLTexLayerSet*				mTexLayerSet;
    	const U64					mStartTime;	// for measuring baked texture upload time
+   	const bool					mIsHighestRes; // whether this is a "final" bake, or intermediate low res
 };
 
 #endif  // LL_LLTEXLAYER_H
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 87ca80260f4337a52e6dc54bceb31c2708651525..b87ca1eaecd73974b396528250cde6550b448a36 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -566,7 +566,7 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)
 	return true;
 }
 
-void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value)
+void toggle_updater_service_active(const LLSD& new_value)
 {
     if(new_value.asInteger())
     {
@@ -735,7 +735,7 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2));
 	gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2));
 	gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2));
-	gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(&toggle_updater_service_active);
+	gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(boost::bind(&toggle_updater_service_active, _2));
 	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));
 	gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));
 }
diff --git a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
index 0ac2c14253500db711a98e9ccf4186ebe2285a3e..0e858ccf107f34dda72b12467fe1b86587d7cd84 100644
--- a/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_wearing_gear.xml
@@ -20,4 +20,11 @@
          function="Gear.OnEnable"
          parameter="take_off" />
     </menu_item_call>
+    <menu_item_call
+     label="Copy outfit list to clipboard"
+     layout="topleft"
+     name="copy">
+        <on_click
+         function="Gear.Copy" />
+    </menu_item_call>
 </toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
index 1c22a5c02e984138a04ad7cd0b3013c789404fed..e639f0dc9dd7abf1b66b391975591bfa1f7fe4e0 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml
@@ -268,7 +268,7 @@
      height="23"
      layout="topleft"
      left_delta="50"
-	 top_pad="5"
+     top_pad="5"
      name="updater_service_combobox"
      width="300">
         <combo_box.item
diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp
index 51ff754c27af02ce7eac61277b277b2362032e08..170babbb985695ef22fcb6b78fd1b9e65003f9cf 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.cpp
+++ b/indra/win_crash_logger/llcrashloggerwindows.cpp
@@ -296,6 +296,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
 
 bool LLCrashLoggerWindows::mainLoop()
 {	
+	llinfos << "CrashSubmitBehavior is " << mCrashBehavior << llendl;
 
 	// Note: parent hwnd is 0 (the desktop).  No dlg proc.  See Petzold (5th ed) HexCalc example, Chapter 11, p529
 	// win_crash_logger.rc has been edited by hand.
@@ -308,6 +309,7 @@ bool LLCrashLoggerWindows::mainLoop()
 
 	if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
 	{
+		llinfos << "Showing crash report submit progress window." << llendl;
 		ShowWindow(gHwndProgress, SW_SHOW );
 		sendCrashLogs();
 	}
@@ -354,7 +356,7 @@ bool LLCrashLoggerWindows::mainLoop()
 
 void LLCrashLoggerWindows::updateApplication(const std::string& message)
 {
-	LLCrashLogger::updateApplication();
+	LLCrashLogger::updateApplication(message);
 	if(!message.empty()) show_progress(message);
 	update_messages();
 }
@@ -370,6 +372,3 @@ bool LLCrashLoggerWindows::cleanup()
 	PostQuitMessage(0);
 	return true;
 }
-
-
-
diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h
index 24c564457ceab400ac373b399c2ff8f49c49997b..5c45a998b328f221015280171bffcdb1fe26ea84 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.h
+++ b/indra/win_crash_logger/llcrashloggerwindows.h
@@ -41,7 +41,6 @@ class LLCrashLoggerWindows : public LLCrashLogger
 	virtual void updateApplication(const std::string& message = LLStringUtil::null);
 	virtual bool cleanup();
 	virtual void gatherPlatformSpecificFiles();
-	//void annotateCallStack();
 	void setHandle(HINSTANCE hInst) { mhInst = hInst; }
 private:
 	void ProcessDlgItemText(HWND hWnd, int nIDDlgItem);
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
index 5c220533173a727772ec2762966e05747a8c6ec7..8e916ae437e796391d2723485a9334b3875d2554 100644
--- a/indra/win_crash_logger/win_crash_logger.cpp
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -24,51 +24,30 @@
  * $/LicenseInfo$
  */
 
-// win_crash_logger.cpp : Defines the entry point for the application.
-//
-
-// Must be first include, precompiled headers.
 #include "linden_common.h"
-
 #include "stdafx.h"
-
 #include <stdlib.h>
-
 #include "llcrashloggerwindows.h"
 
-
-
-//
-// Implementation
-//
-
 int APIENTRY WinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
 {
-	llinfos << "Starting crash reporter" << llendl;
+	llinfos << "Starting crash reporter." << llendl;
 
 	LLCrashLoggerWindows app;
 	app.setHandle(hInstance);
-	bool ok = app.init();
-	if(!ok)
+	app.parseCommandOptions(__argc, __argv);
+
+	if (! app.init())
 	{
 		llwarns << "Unable to initialize application." << llendl;
 		return -1;
 	}
 
-		// Run the application main loop
-	if(!LLApp::isQuitting()) app.mainLoop();
-
-	if (!app.isError())
-	{
-		//
-		// We don't want to do cleanup here if the error handler got called -
-		// the assumption is that the error handler is responsible for doing
-		// app cleanup if there was a problem.
-		//
-		app.cleanup();
-	}
+	app.mainLoop();
+	app.cleanup();
+	llinfos << "Crash reporter finished normally." << llendl;
 	return 0;
 }