From d9448c6f52218146113d1d5c5ca4c4d5f01dc5cf Mon Sep 17 00:00:00 2001
From: Callum Prentice <callum@gmail.com>
Date: Thu, 17 Sep 2020 09:45:06 -0700
Subject: The folder where the disk cache lives was originally renamed from
 llvfs to llcache but @henri's suggestion that that doesn't reflect the other
 files in the same place and it should be llfilesystem is a good one so I
 changed it over

---
 indra/CMakeLists.txt                            |    2 +-
 indra/cmake/CMakeLists.txt                      |    2 +-
 indra/cmake/LLCache.cmake                       |    7 -
 indra/cmake/LLFileSystem.cmake                  |    7 +
 indra/llappearance/CMakeLists.txt               |   20 +-
 indra/llaudio/CMakeLists.txt                    |    6 +-
 indra/llcache/CMakeLists.txt                    |   98 --
 indra/llcache/lldir.cpp                         | 1134 -----------------------
 indra/llcache/lldir.h                           |  280 ------
 indra/llcache/lldir_linux.cpp                   |  269 ------
 indra/llcache/lldir_linux.h                     |   64 --
 indra/llcache/lldir_mac.cpp                     |  205 ----
 indra/llcache/lldir_mac.h                       |   56 --
 indra/llcache/lldir_solaris.cpp                 |  266 ------
 indra/llcache/lldir_solaris.h                   |   61 --
 indra/llcache/lldir_utils_objc.h                |   43 -
 indra/llcache/lldir_utils_objc.mm               |  108 ---
 indra/llcache/lldir_win32.cpp                   |  452 ---------
 indra/llcache/lldir_win32.h                     |   59 --
 indra/llcache/lldirguard.h                      |   72 --
 indra/llcache/lldiriterator.cpp                 |  243 -----
 indra/llcache/lldiriterator.h                   |   87 --
 indra/llcache/lldiskcache.cpp                   |  387 --------
 indra/llcache/lldiskcache.h                     |   82 --
 indra/llcache/lllfsthread.cpp                   |  245 -----
 indra/llcache/lllfsthread.h                     |  147 ---
 indra/llcache/tests/lldir_test.cpp              |  767 ---------------
 indra/llcache/tests/lldiriterator_test.cpp      |   65 --
 indra/llcharacter/CMakeLists.txt                |    6 +-
 indra/llcrashlogger/CMakeLists.txt              |    4 +-
 indra/llfilesystem/CMakeLists.txt               |   98 ++
 indra/llfilesystem/lldir.cpp                    | 1134 +++++++++++++++++++++++
 indra/llfilesystem/lldir.h                      |  280 ++++++
 indra/llfilesystem/lldir_linux.cpp              |  269 ++++++
 indra/llfilesystem/lldir_linux.h                |   64 ++
 indra/llfilesystem/lldir_mac.cpp                |  205 ++++
 indra/llfilesystem/lldir_mac.h                  |   56 ++
 indra/llfilesystem/lldir_solaris.cpp            |  266 ++++++
 indra/llfilesystem/lldir_solaris.h              |   61 ++
 indra/llfilesystem/lldir_utils_objc.h           |   43 +
 indra/llfilesystem/lldir_utils_objc.mm          |  108 +++
 indra/llfilesystem/lldir_win32.cpp              |  452 +++++++++
 indra/llfilesystem/lldir_win32.h                |   59 ++
 indra/llfilesystem/lldirguard.h                 |   72 ++
 indra/llfilesystem/lldiriterator.cpp            |  243 +++++
 indra/llfilesystem/lldiriterator.h              |   87 ++
 indra/llfilesystem/lldiskcache.cpp              |  387 ++++++++
 indra/llfilesystem/lldiskcache.h                |   82 ++
 indra/llfilesystem/lllfsthread.cpp              |  245 +++++
 indra/llfilesystem/lllfsthread.h                |  147 +++
 indra/llfilesystem/tests/lldir_test.cpp         |  767 +++++++++++++++
 indra/llfilesystem/tests/lldiriterator_test.cpp |   65 ++
 indra/llimage/CMakeLists.txt                    |    6 +-
 indra/llinventory/CMakeLists.txt                |    4 +-
 indra/llmessage/CMakeLists.txt                  |   12 +-
 indra/llrender/CMakeLists.txt                   |   10 +-
 indra/llui/CMakeLists.txt                       |    6 +-
 indra/llwindow/CMakeLists.txt                   |    8 +-
 indra/llxml/CMakeLists.txt                      |    6 +-
 indra/mac_crash_logger/CMakeLists.txt           |    6 +-
 indra/newview/CMakeLists.txt                    |   10 +-
 indra/test/CMakeLists.txt                       |    7 +-
 indra/win_crash_logger/CMakeLists.txt           |    6 +-
 63 files changed, 5250 insertions(+), 5265 deletions(-)
 delete mode 100644 indra/cmake/LLCache.cmake
 create mode 100644 indra/cmake/LLFileSystem.cmake
 delete mode 100644 indra/llcache/CMakeLists.txt
 delete mode 100644 indra/llcache/lldir.cpp
 delete mode 100644 indra/llcache/lldir.h
 delete mode 100644 indra/llcache/lldir_linux.cpp
 delete mode 100644 indra/llcache/lldir_linux.h
 delete mode 100644 indra/llcache/lldir_mac.cpp
 delete mode 100644 indra/llcache/lldir_mac.h
 delete mode 100644 indra/llcache/lldir_solaris.cpp
 delete mode 100644 indra/llcache/lldir_solaris.h
 delete mode 100644 indra/llcache/lldir_utils_objc.h
 delete mode 100644 indra/llcache/lldir_utils_objc.mm
 delete mode 100644 indra/llcache/lldir_win32.cpp
 delete mode 100644 indra/llcache/lldir_win32.h
 delete mode 100644 indra/llcache/lldirguard.h
 delete mode 100644 indra/llcache/lldiriterator.cpp
 delete mode 100644 indra/llcache/lldiriterator.h
 delete mode 100644 indra/llcache/lldiskcache.cpp
 delete mode 100644 indra/llcache/lldiskcache.h
 delete mode 100644 indra/llcache/lllfsthread.cpp
 delete mode 100644 indra/llcache/lllfsthread.h
 delete mode 100644 indra/llcache/tests/lldir_test.cpp
 delete mode 100644 indra/llcache/tests/lldiriterator_test.cpp
 create mode 100644 indra/llfilesystem/CMakeLists.txt
 create mode 100644 indra/llfilesystem/lldir.cpp
 create mode 100644 indra/llfilesystem/lldir.h
 create mode 100644 indra/llfilesystem/lldir_linux.cpp
 create mode 100644 indra/llfilesystem/lldir_linux.h
 create mode 100644 indra/llfilesystem/lldir_mac.cpp
 create mode 100644 indra/llfilesystem/lldir_mac.h
 create mode 100644 indra/llfilesystem/lldir_solaris.cpp
 create mode 100644 indra/llfilesystem/lldir_solaris.h
 create mode 100644 indra/llfilesystem/lldir_utils_objc.h
 create mode 100644 indra/llfilesystem/lldir_utils_objc.mm
 create mode 100644 indra/llfilesystem/lldir_win32.cpp
 create mode 100644 indra/llfilesystem/lldir_win32.h
 create mode 100644 indra/llfilesystem/lldirguard.h
 create mode 100644 indra/llfilesystem/lldiriterator.cpp
 create mode 100644 indra/llfilesystem/lldiriterator.h
 create mode 100644 indra/llfilesystem/lldiskcache.cpp
 create mode 100644 indra/llfilesystem/lldiskcache.h
 create mode 100644 indra/llfilesystem/lllfsthread.cpp
 create mode 100644 indra/llfilesystem/lllfsthread.h
 create mode 100644 indra/llfilesystem/tests/lldir_test.cpp
 create mode 100644 indra/llfilesystem/tests/lldiriterator_test.cpp

diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 04279cc887..4b39bfe332 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -40,7 +40,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llmath)
 add_subdirectory(${LIBS_OPEN_PREFIX}llmessage)
 add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive)
 add_subdirectory(${LIBS_OPEN_PREFIX}llrender)
-add_subdirectory(${LIBS_OPEN_PREFIX}llcache)
+add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem)
 add_subdirectory(${LIBS_OPEN_PREFIX}llwindow)
 add_subdirectory(${LIBS_OPEN_PREFIX}llxml)
 
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt
index d464c8ad74..352dfc0641 100644
--- a/indra/cmake/CMakeLists.txt
+++ b/indra/cmake/CMakeLists.txt
@@ -69,7 +69,7 @@ set(cmake_SOURCE_FILES
     LLSharedLibs.cmake
     LLTestCommand.cmake
     LLUI.cmake
-    LLCache.cmake
+    LLFileSystem.cmake
     LLWindow.cmake
     LLXML.cmake
     Linking.cmake
diff --git a/indra/cmake/LLCache.cmake b/indra/cmake/LLCache.cmake
deleted file mode 100644
index 6a82774133..0000000000
--- a/indra/cmake/LLCache.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- cmake -*-
-
-set(LLCACHE_INCLUDE_DIRS
-    ${LIBS_OPEN_DIR}/llcache
-    )
-
-set(LLCACHE_LIBRARIES llcache)
diff --git a/indra/cmake/LLFileSystem.cmake b/indra/cmake/LLFileSystem.cmake
new file mode 100644
index 0000000000..2e6c42c30c
--- /dev/null
+++ b/indra/cmake/LLFileSystem.cmake
@@ -0,0 +1,7 @@
+# -*- cmake -*-
+
+set(LLFILESYSTEM_INCLUDE_DIRS
+    ${LIBS_OPEN_DIR}/llfilesystem
+    )
+
+set(LLFILESYSTEM_LIBRARIES llfilesystem)
diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt
index ff784387dc..268849ad74 100644
--- a/indra/llappearance/CMakeLists.txt
+++ b/indra/llappearance/CMakeLists.txt
@@ -11,7 +11,7 @@ include(LLMath)
 include(LLMessage)
 include(LLCoreHttp)
 include(LLRender)
-include(LLCache)
+include(LLFileSystem)
 include(LLWindow)
 include(LLXML)
 include(Linking)
@@ -23,7 +23,7 @@ include_directories(
     ${LLINVENTORY_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     )
@@ -83,7 +83,7 @@ target_link_libraries(llappearance
     ${LLINVENTORY_LIBRARIES}
     ${LLIMAGE_LIBRARIES}
     ${LLRENDER_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLMATH_LIBRARIES}
@@ -100,7 +100,7 @@ if (BUILD_HEADLESS)
       ${LLINVENTORY_LIBRARIES}
       ${LLIMAGE_LIBRARIES}
       ${LLRENDERHEADLESS_LIBRARIES}
-      ${LLCACHE_LIBRARIES}
+      ${LLFILESYSTEM_LIBRARIES}
       ${LLMATH_LIBRARIES}
       ${LLXML_LIBRARIES}
       ${LLMATH_LIBRARIES}
@@ -109,15 +109,3 @@ if (BUILD_HEADLESS)
       ${LLCOMMON_LIBRARIES}
       )
 endif (BUILD_HEADLESS)
-
-#add unit tests
-#if (LL_TESTS)
-#    INCLUDE(LLAddBuildTest)
-#    SET(llappearance_TEST_SOURCE_FILES
-#      # no real unit tests yet!
-#      )
-#    LL_ADD_PROJECT_UNIT_TESTS(llappearance "${llappearance_TEST_SOURCE_FILES}")
-
-    #set(TEST_DEBUG on)
-#    set(test_libs llappearance ${LLCOMMON_LIBRARIES})
-#endif (LL_TESTS)
diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index 2498561029..9dcd4d697e 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -9,14 +9,14 @@ include(OPENAL)
 include(LLCommon)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 
 include_directories(
     ${LLAUDIO_INCLUDE_DIRS}
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${OGG_INCLUDE_DIRS}
     ${VORBISENC_INCLUDE_DIRS}
     ${VORBISFILE_INCLUDE_DIRS}
@@ -82,7 +82,7 @@ target_link_libraries(
     ${LLCOMMON_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${VORBISENC_LIBRARIES}
     ${VORBISFILE_LIBRARIES}
     ${VORBIS_LIBRARIES}
diff --git a/indra/llcache/CMakeLists.txt b/indra/llcache/CMakeLists.txt
deleted file mode 100644
index f1e4e7e0a3..0000000000
--- a/indra/llcache/CMakeLists.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- cmake -*-
-
-project(llcache)
-
-include(00-Common)
-include(LLCommon)
-include(UnixInstall)
-
-include_directories(
-    ${LLCOMMON_INCLUDE_DIRS}
-    ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
-    )
-
-set(llcache_SOURCE_FILES
-    lldir.cpp
-    lldiriterator.cpp
-    lllfsthread.cpp
-    lldiskcache.cpp
-    )
-
-set(llcache_HEADER_FILES
-    CMakeLists.txt
-
-    lldir.h
-    lldirguard.h
-    lldiriterator.h
-    lllfsthread.h
-    lldiskcache.h
-    )
-
-if (DARWIN)
-  LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.mm)
-  LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.h)
-  LIST(APPEND llcache_SOURCE_FILES lldir_mac.cpp)
-  LIST(APPEND llcache_HEADER_FILES lldir_mac.h)
-endif (DARWIN)
-
-if (LINUX)
-  LIST(APPEND llcache_SOURCE_FILES lldir_linux.cpp)
-  LIST(APPEND llcache_HEADER_FILES lldir_linux.h)
-
-  if (INSTALL)
-    set_source_files_properties(lldir_linux.cpp
-                                PROPERTIES COMPILE_FLAGS
-                                "-DAPP_RO_DATA_DIR=\\\"${APP_SHARE_DIR}\\\""
-                                )
-  endif (INSTALL)
-endif (LINUX)
-
-if (WINDOWS)
-  LIST(APPEND llcache_SOURCE_FILES lldir_win32.cpp)
-  LIST(APPEND llcache_HEADER_FILES lldir_win32.h)
-endif (WINDOWS)
-
-set_source_files_properties(${llcache_HEADER_FILES}
-                            PROPERTIES HEADER_FILE_ONLY TRUE)
-
-list(APPEND llcache_SOURCE_FILES ${llcache_HEADER_FILES})
-
-add_library (llcache ${llcache_SOURCE_FILES})
-
-set(cache_BOOST_LIBRARIES
-    ${BOOST_FILESYSTEM_LIBRARY}
-    ${BOOST_SYSTEM_LIBRARY}
-    )
-
-target_link_libraries(llcache
-    ${LLCOMMON_LIBRARIES}
-    ${cache_BOOST_LIBRARIES}
-    )
-
-if (DARWIN)
-  include(CMakeFindFrameworks)
-  find_library(COCOA_LIBRARY Cocoa)
-  target_link_libraries(llcache ${COCOA_LIBRARY})
-endif (DARWIN)
-
-
-# Add tests
-if (LL_TESTS)
-    include(LLAddBuildTest)
-    # UNIT TESTS
-    SET(llcache_TEST_SOURCE_FILES
-    lldiriterator.cpp
-    )
-
-    set_source_files_properties(lldiriterator.cpp
-    PROPERTIES
-    LL_TEST_ADDITIONAL_LIBRARIES "${cache_BOOST_LIBRARIES}"
-    )
-    LL_ADD_PROJECT_UNIT_TESTS(llcache "${llcache_TEST_SOURCE_FILES}")
-
-    # INTEGRATION TESTS
-    set(test_libs llmath llcommon llcache ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
-
-    # TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests.
-    LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}")
-endif (LL_TESTS)
diff --git a/indra/llcache/lldir.cpp b/indra/llcache/lldir.cpp
deleted file mode 100644
index 10fbc06c61..0000000000
--- a/indra/llcache/lldir.cpp
+++ /dev/null
@@ -1,1134 +0,0 @@
-/** 
- * @file lldir.cpp
- * @brief implementation of directory utilities base class
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#if !LL_WINDOWS
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#else
-#include <direct.h>
-#endif
-
-#include "lldir.h"
-
-#include "llerror.h"
-#include "lltimer.h"	// ms_sleep()
-#include "lluuid.h"
-
-#include "lldiriterator.h"
-#include "stringize.h"
-#include "llstring.h"
-#include <boost/filesystem.hpp>
-#include <boost/foreach.hpp>
-#include <boost/range/begin.hpp>
-#include <boost/range/end.hpp>
-#include <boost/assign/list_of.hpp>
-#include <boost/bind.hpp>
-#include <boost/ref.hpp>
-#include <algorithm>
-
-using boost::assign::list_of;
-using boost::assign::map_list_of;
-
-#if LL_WINDOWS
-#include "lldir_win32.h"
-LLDir_Win32 gDirUtil;
-#elif LL_DARWIN
-#include "lldir_mac.h"
-LLDir_Mac gDirUtil;
-#elif LL_SOLARIS
-#include "lldir_solaris.h"
-LLDir_Solaris gDirUtil;
-#else
-#include "lldir_linux.h"
-LLDir_Linux gDirUtil;
-#endif
-
-LLDir *gDirUtilp = (LLDir *)&gDirUtil;
-
-/// Values for findSkinnedFilenames(subdir) parameter
-const char
-	*LLDir::XUI      = "xui",
-	*LLDir::TEXTURES = "textures",
-	*LLDir::SKINBASE = "";
-
-static const char* const empty = "";
-std::string LLDir::sDumpDir = "";
-
-LLDir::LLDir()
-:	mAppName(""),
-	mExecutablePathAndName(""),
-	mExecutableFilename(""),
-	mExecutableDir(""),
-	mAppRODataDir(""),
-	mOSUserDir(""),
-	mOSUserAppDir(""),
-	mLindenUserDir(""),
-	mOSCacheDir(""),
-	mCAFile(""),
-	mTempDir(""),
-	mDirDelimiter("/"), // fallback to forward slash if not overridden
-	mLanguage("en"),
-	mUserName("undefined")
-{
-}
-
-LLDir::~LLDir()
-{
-}
-
-std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
-{
-    //Returns a vector of fullpath filenames.
-
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
-    boost::filesystem::path p(utf8str_to_utf16str(dirname));
-#else
-    boost::filesystem::path p(dirname);
-#endif
-
-    std::vector<std::string> v;
-    
-    if (exists(p))
-    {
-        if (is_directory(p))
-        {
-            boost::filesystem::directory_iterator end_iter;
-            for (boost::filesystem::directory_iterator dir_itr(p);
-                 dir_itr != end_iter;
-                 ++dir_itr)
-            {
-                if (boost::filesystem::is_regular_file(dir_itr->status()))
-                {
-                    v.push_back(dir_itr->path().filename().string());
-                }
-            }
-        }
-    }
-    return v;
-}   
-            
-S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
-{
-	S32 count = 0;
-	std::string filename; 
-	std::string fullpath;
-	S32 result;
-
-	// File masks starting with "/" will match nothing, so we consider them invalid.
-	if (LLStringUtil::startsWith(mask, getDirDelimiter()))
-	{
-		LL_WARNS() << "Invalid file mask: " << mask << LL_ENDL;
-		llassert(!"Invalid file mask");
-	}
-
-	LLDirIterator iter(dirname, mask);
-	while (iter.next(filename))
-	{
-		fullpath = add(dirname, filename);
-
-		if(LLFile::isdir(fullpath))
-		{
-			// skipping directory traversal filenames
-			count++;
-			continue;
-		}
-
-		S32 retry_count = 0;
-		while (retry_count < 5)
-		{
-			if (0 != LLFile::remove(fullpath))
-			{
-				retry_count++;
-				result = errno;
-				LL_WARNS() << "Problem removing " << fullpath << " - errorcode: "
-						<< result << " attempt " << retry_count << LL_ENDL;
-
-				if(retry_count >= 5)
-				{
-					LL_WARNS() << "Failed to remove " << fullpath << LL_ENDL ;
-					return count ;
-				}
-
-				ms_sleep(100);
-			}
-			else
-			{
-				if (retry_count)
-				{
-					LL_WARNS() << "Successfully removed " << fullpath << LL_ENDL;
-				}
-				break;
-			}			
-		}
-		count++;
-	}
-	return count;
-}
-
-U32 LLDir::deleteDirAndContents(const std::string& dir_name)
-{
-    //Removes the directory and its contents.  Returns number of files deleted.
-	
-	U32 num_deleted = 0;
-
-	try
-	{
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
-		boost::filesystem::path dir_path(utf8str_to_utf16str(dir_name));
-#else
-		boost::filesystem::path dir_path(dir_name);
-#endif
-
-	   if (boost::filesystem::exists (dir_path))
-	   {
-	      if (!boost::filesystem::is_empty (dir_path))
-		  {   // Directory has content
-		     num_deleted = boost::filesystem::remove_all (dir_path);
-		  }
-		  else
-		  {   // Directory is empty
-		     boost::filesystem::remove (dir_path);
-		  }
-	   }
-	}
-	catch (boost::filesystem::filesystem_error &er)
-	{ 
-		LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL;
-	} 
-	return num_deleted;
-}
-
-const std::string LLDir::findFile(const std::string &filename, 
-						   const std::string& searchPath1, 
-						   const std::string& searchPath2, 
-						   const std::string& searchPath3) const
-{
-	std::vector<std::string> search_paths;
-	search_paths.push_back(searchPath1);
-	search_paths.push_back(searchPath2);
-	search_paths.push_back(searchPath3);
-	return findFile(filename, search_paths);
-}
-
-const std::string LLDir::findFile(const std::string& filename, const std::vector<std::string> search_paths) const
-{
-	std::vector<std::string>::const_iterator search_path_iter;
-	for (search_path_iter = search_paths.begin();
-		search_path_iter != search_paths.end();
-		++search_path_iter)
-	{
-		if (!search_path_iter->empty())
-		{
-			std::string filename_and_path = (*search_path_iter);
-			if (!filename.empty())
-			{
-				filename_and_path += getDirDelimiter() + filename;
-			}
-			if (fileExists(filename_and_path))
-			{
-				return filename_and_path;
-			}
-		}
-	}
-	return "";
-}
-
-
-const std::string &LLDir::getExecutablePathAndName() const
-{
-	return mExecutablePathAndName;
-}
-
-const std::string &LLDir::getExecutableFilename() const
-{
-	return mExecutableFilename;
-}
-
-const std::string &LLDir::getExecutableDir() const
-{
-	return mExecutableDir;
-}
-
-const std::string &LLDir::getWorkingDir() const
-{
-	return mWorkingDir;
-}
-
-const std::string &LLDir::getAppName() const
-{
-	return mAppName;
-}
-
-const std::string &LLDir::getAppRODataDir() const
-{
-	return mAppRODataDir;
-}
-
-const std::string &LLDir::getOSUserDir() const
-{
-	return mOSUserDir;
-}
-
-const std::string &LLDir::getOSUserAppDir() const
-{
-	return mOSUserAppDir;
-}
-
-const std::string &LLDir::getLindenUserDir() const
-{
-	if (mLindenUserDir.empty())
-	{
-		LL_DEBUGS() << "getLindenUserDir() called early, we don't have the user name yet - returning empty string to caller" << LL_ENDL;
-	}
-
-	return mLindenUserDir;
-}
-
-const std::string& LLDir::getChatLogsDir() const
-{
-	return mChatLogsDir;
-}
-
-void LLDir::setDumpDir( const std::string& path )
-{
-    LLDir::sDumpDir = path;
-    if (LLStringUtil::endsWith(sDumpDir, mDirDelimiter))
-    {
-        sDumpDir.erase(sDumpDir.size() - mDirDelimiter.size());
-    }
-}
-
-const std::string &LLDir::getDumpDir() const
-{
-    if (sDumpDir.empty() )
-    {
-        LLUUID uid;
-        uid.generate();
-        
-        sDumpDir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "")
-                    + "dump-" + uid.asString();
-
-        dir_exists_or_crash(sDumpDir);  
-    }
-
-	return LLDir::sDumpDir;
-}
-
-const std::string &LLDir::getPerAccountChatLogsDir() const
-{
-	return mPerAccountChatLogsDir;
-}
-
-const std::string &LLDir::getTempDir() const
-{
-	return mTempDir;
-}
-
-const std::string  LLDir::getCacheDir(bool get_default) const
-{
-	if (mCacheDir.empty() || get_default)
-	{
-		if (!mDefaultCacheDir.empty())
-		{	// Set at startup - can't set here due to const API
-			return mDefaultCacheDir;
-		}
-		
-		std::string res = buildSLOSCacheDir();
-		return res;
-	}
-	else
-	{
-		return mCacheDir;
-	}
-}
-
-// Return the default cache directory
-std::string LLDir::buildSLOSCacheDir() const
-{
-	std::string res;
-	if (getOSCacheDir().empty())
-	{
-		if (getOSUserAppDir().empty())
-		{
-			res = "data";
-		}
-		else
-		{
-			res = add(getOSUserAppDir(), "cache");
-		}
-	}
-	else
-	{
-		res = add(getOSCacheDir(), "SecondLife");
-	}
-	return res;
-}
-
-
-
-const std::string &LLDir::getOSCacheDir() const
-{
-	return mOSCacheDir;
-}
-
-
-const std::string &LLDir::getCAFile() const
-{
-	return mCAFile;
-}
-
-const std::string &LLDir::getDirDelimiter() const
-{
-	return mDirDelimiter;
-}
-
-const std::string& LLDir::getDefaultSkinDir() const
-{
-	return mDefaultSkinDir;
-}
-
-const std::string &LLDir::getSkinDir() const
-{
-	return mSkinDir;
-}
-
-const std::string &LLDir::getUserDefaultSkinDir() const
-{
-    return mUserDefaultSkinDir;
-}
-
-const std::string &LLDir::getUserSkinDir() const
-{
-	return mUserSkinDir;
-}
-
-const std::string LLDir::getSkinBaseDir() const
-{
-	return mSkinBaseDir;
-}
-
-const std::string &LLDir::getLLPluginDir() const
-{
-	return mLLPluginDir;
-}
-
-const std::string &LLDir::getUserName() const
-{
-	return mUserName;
-}
-
-static std::string ELLPathToString(ELLPath location)
-{
-	typedef std::map<ELLPath, const char*> ELLPathMap;
-#define ENT(symbol) (symbol, #symbol)
-	static const ELLPathMap sMap = map_list_of
-		ENT(LL_PATH_NONE)
-		ENT(LL_PATH_USER_SETTINGS)
-		ENT(LL_PATH_APP_SETTINGS)
-		ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet
-		ENT(LL_PATH_CACHE)
-		ENT(LL_PATH_CHARACTER)
-		ENT(LL_PATH_HELP)
-		ENT(LL_PATH_LOGS)
-		ENT(LL_PATH_TEMP)
-		ENT(LL_PATH_SKINS)
-		ENT(LL_PATH_TOP_SKIN)
-		ENT(LL_PATH_CHAT_LOGS)
-		ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS)
-		ENT(LL_PATH_USER_SKIN)
-		ENT(LL_PATH_LOCAL_ASSETS)
-		ENT(LL_PATH_EXECUTABLE)
-		ENT(LL_PATH_DEFAULT_SKIN)
-		ENT(LL_PATH_FONTS)
-		ENT(LL_PATH_LAST)
-	;
-#undef ENT
-
-	ELLPathMap::const_iterator found = sMap.find(location);
-	if (found != sMap.end())
-		return found->second;
-	return STRINGIZE("Invalid ELLPath value " << location);
-}
-
-std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const
-{
-	return getExpandedFilename(location, "", filename);
-}
-
-std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir, const std::string& filename) const
-{
-	return getExpandedFilename(location, "", subdir, filename);
-}
-
-std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir1, const std::string& subdir2, const std::string& in_filename) const
-{
-	std::string prefix;
-	switch (location)
-	{
-	case LL_PATH_NONE:
-		// Do nothing
-		break;
-
-	case LL_PATH_APP_SETTINGS:
-		prefix = add(getAppRODataDir(), "app_settings");
-		break;
-	
-	case LL_PATH_CHARACTER:
-		prefix = add(getAppRODataDir(), "character");
-		break;
-		
-	case LL_PATH_HELP:
-		prefix = "help";
-		break;
-		
-	case LL_PATH_CACHE:
-		prefix = getCacheDir();
-		break;
-		
-    case LL_PATH_DUMP:
-        prefix=getDumpDir();
-        break;
-            
-	case LL_PATH_USER_SETTINGS:
-		prefix = add(getOSUserAppDir(), "user_settings");
-		break;
-
-	case LL_PATH_PER_SL_ACCOUNT:
-		prefix = getLindenUserDir();
-		if (prefix.empty())
-		{
-			// if we're asking for the per-SL-account directory but we haven't
-			// logged in yet (or otherwise don't know the account name from
-			// which to build this string), then intentionally return a blank
-			// string to the caller and skip the below warning about a blank
-			// prefix.
-			LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: "
-							   << ELLPathToString(location)
-							   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
-							   << "' => ''" << LL_ENDL;
-			return std::string();
-		}
-		break;
-		
-	case LL_PATH_CHAT_LOGS:
-		prefix = getChatLogsDir();
-		break;
-		
-	case LL_PATH_PER_ACCOUNT_CHAT_LOGS:
-		prefix = getPerAccountChatLogsDir();
-		if (prefix.empty())
-		{
-			// potentially directory was not set yet
-			// intentionally return a blank string to the caller
-			LL_DEBUGS("LLDir") << "Conversation log directory is not yet set" << LL_ENDL;
-			return std::string();
-		}
-		break;
-
-	case LL_PATH_LOGS:
-		prefix = add(getOSUserAppDir(), "logs");
-		break;
-
-	case LL_PATH_TEMP:
-		prefix = getTempDir();
-		break;
-
-	case LL_PATH_TOP_SKIN:
-		prefix = getSkinDir();
-		break;
-
-	case LL_PATH_DEFAULT_SKIN:
-		prefix = getDefaultSkinDir();
-		break;
-
-	case LL_PATH_USER_SKIN:
-		prefix = getUserSkinDir();
-		break;
-
-	case LL_PATH_SKINS:
-		prefix = getSkinBaseDir();
-		break;
-
-	case LL_PATH_LOCAL_ASSETS:
-		prefix = add(getAppRODataDir(), "local_assets");
-		break;
-
-	case LL_PATH_EXECUTABLE:
-		prefix = getExecutableDir();
-		break;
-		
-	case LL_PATH_FONTS:
-		prefix = add(getAppRODataDir(), "fonts");
-		break;
-		
-	default:
-		llassert(0);
-	}
-
-	if (prefix.empty())
-	{
-		LL_WARNS() << ELLPathToString(location)
-				<< ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
-				<< "': prefix is empty, possible bad filename" << LL_ENDL;
-	}
-
-	std::string expanded_filename = add(prefix, subdir1, subdir2);
-	if (expanded_filename.empty() && in_filename.empty())
-	{
-		return "";
-	}
-	// Use explicit concatenation here instead of another add() call. Callers
-	// passing in_filename as "" expect to obtain a pathname ending with
-	// mDirSeparator so they can later directly concatenate with a specific
-	// filename. A caller using add() doesn't care, but there's still code
-	// loose in the system that uses std::string::operator+().
-	expanded_filename += mDirDelimiter;
-	expanded_filename += in_filename;
-
-	LL_DEBUGS("LLDir") << ELLPathToString(location)
-					   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
-					   << "' => '" << expanded_filename << "'" << LL_ENDL;
-	return expanded_filename;
-}
-
-std::string LLDir::getBaseFileName(const std::string& filepath, bool strip_exten) const
-{
-	std::size_t offset = filepath.find_last_of(getDirDelimiter());
-	offset = (offset == std::string::npos) ? 0 : offset+1;
-	std::string res = filepath.substr(offset, std::string::npos);
-	if (strip_exten)
-	{
-		offset = res.find_last_of('.');
-		if (offset != std::string::npos &&
-		    offset != 0) // if basename STARTS with '.', don't strip
-		{
-			res = res.substr(0, offset);
-		}
-	}
-	return res;
-}
-
-std::string LLDir::getDirName(const std::string& filepath) const
-{
-	std::size_t offset = filepath.find_last_of(getDirDelimiter());
-	S32 len = (offset == std::string::npos) ? 0 : offset;
-	std::string dirname = filepath.substr(0, len);
-	return dirname;
-}
-
-std::string LLDir::getExtension(const std::string& filepath) const
-{
-	if (filepath.empty())
-		return std::string();
-	std::string basename = getBaseFileName(filepath, false);
-	std::size_t offset = basename.find_last_of('.');
-	std::string exten = (offset == std::string::npos || offset == 0) ? "" : basename.substr(offset+1);
-	LLStringUtil::toLower(exten);
-	return exten;
-}
-
-std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir,
-											   const std::string &filename,
-											   ESkinConstraint constraint) const
-{
-	// This implementation is basically just as described in the declaration comments.
-	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint));
-	if (found.empty())
-	{
-		return "";
-	}
-	return found.front();
-}
-
-std::string LLDir::findSkinnedFilename(const std::string &subdir,
-									   const std::string &filename,
-									   ESkinConstraint constraint) const
-{
-	// This implementation is basically just as described in the declaration comments.
-	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint));
-	if (found.empty())
-	{
-		return "";
-	}
-	return found.back();
-}
-
-// This method exists because the two code paths for
-// findSkinnedFilenames(ALL_SKINS) and findSkinnedFilenames(CURRENT_SKIN) must
-// generate the list of candidate pathnames in identical ways. The only
-// difference is in the body of the inner loop.
-template <typename FUNCTION>
-void LLDir::walkSearchSkinDirs(const std::string& subdir,
-							   const std::vector<std::string>& subsubdirs,
-							   const std::string& filename,
-							   const FUNCTION& function) const
-{
-	BOOST_FOREACH(std::string skindir, mSearchSkinDirs)
-	{
-		std::string subdir_path(add(skindir, subdir));
-		BOOST_FOREACH(std::string subsubdir, subsubdirs)
-		{
-			std::string full_path(add(subdir_path, subsubdir, filename));
-			if (fileExists(full_path))
-			{
-				function(subsubdir, full_path);
-			}
-		}
-	}
-}
-
-// ridiculous little helper function that should go away when we can use lambda
-inline void push_back(std::vector<std::string>& vector, const std::string& value)
-{
-	vector.push_back(value);
-}
-
-typedef std::map<std::string, std::string> StringMap;
-// ridiculous little helper function that should go away when we can use lambda
-inline void store_in_map(StringMap& map, const std::string& key, const std::string& value)
-{
-	map[key] = value;
-}
-
-std::vector<std::string> LLDir::findSkinnedFilenames(const std::string& subdir,
-													 const std::string& filename,
-													 ESkinConstraint constraint) const
-{
-	// Recognize subdirs that have no localization.
-	static const std::set<std::string> sUnlocalized = list_of
-		("")                        // top-level directory not localized
-		("textures")                // textures not localized
-	;
-
-	LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename
-					   << "', constraint "
-					   << ((constraint == CURRENT_SKIN)? "CURRENT_SKIN" : "ALL_SKINS")
-					   << LL_ENDL;
-
-	// Build results vector.
-	std::vector<std::string> results;
-	// Disallow filenames that may escape subdir
-	if (filename.find("..") != std::string::npos)
-	{
-		LL_WARNS("LLDir") << "Ignoring potentially relative filename '" << filename << "'" << LL_ENDL;
-		return results;
-	}
-
-	// Cache the default language directory for each subdir we've encountered.
-	// A cache entry whose value is the empty string means "not localized,
-	// don't bother checking again."
-	static StringMap sLocalized;
-
-	// Check whether we've already discovered if this subdir is localized.
-	StringMap::const_iterator found = sLocalized.find(subdir);
-	if (found == sLocalized.end())
-	{
-		// We have not yet determined that. Is it one of the subdirs "known"
-		// to be unlocalized?
-		if (sUnlocalized.find(subdir) != sUnlocalized.end())
-		{
-			// This subdir is known to be unlocalized. Remember that.
-			found = sLocalized.insert(StringMap::value_type(subdir, "")).first;
-		}
-		else
-		{
-			// We do not recognize this subdir. Investigate.
-			std::string subdir_path(add(getDefaultSkinDir(), subdir));
-			if (fileExists(add(subdir_path, "en")))
-			{
-				// defaultSkinDir/subdir contains subdir "en". That's our
-				// default language; this subdir is localized.
-				found = sLocalized.insert(StringMap::value_type(subdir, "en")).first;
-			}
-			else if (fileExists(add(subdir_path, "en-us")))
-			{
-				// defaultSkinDir/subdir contains subdir "en-us" but not "en".
-				// Set as default language; this subdir is localized.
-				found = sLocalized.insert(StringMap::value_type(subdir, "en-us")).first;
-			}
-			else
-			{
-				// defaultSkinDir/subdir contains neither "en" nor "en-us".
-				// Assume it's not localized. Remember that assumption.
-				found = sLocalized.insert(StringMap::value_type(subdir, "")).first;
-			}
-		}
-	}
-	// Every code path above should have resulted in 'found' becoming a valid
-	// iterator to an entry in sLocalized.
-	llassert(found != sLocalized.end());
-
-	// Now -- is this subdir localized, or not? The answer determines what
-	// subdirectories we check (under subdir) for the requested filename.
-	std::vector<std::string> subsubdirs;
-	if (found->second.empty())
-	{
-		// subdir is not localized. filename should be located directly within it.
-		subsubdirs.push_back("");
-	}
-	else
-	{
-		// subdir is localized, and found->second is the default language
-		// directory within it. Check both the default language and the
-		// current language -- if it differs from the default, of course.
-		subsubdirs.push_back(found->second);
-		if (mLanguage != found->second)
-		{
-			subsubdirs.push_back(mLanguage);
-		}
-	}
-
-	// The process we use depends on 'constraint'.
-	if (constraint != CURRENT_SKIN) // meaning ALL_SKINS
-	{
-		// ALL_SKINS is simpler: just return every pathname generated by
-		// walkSearchSkinDirs(). Tricky bit: walkSearchSkinDirs() passes its
-		// FUNCTION the subsubdir as well as the full pathname. We just want
-		// the full pathname.
-		walkSearchSkinDirs(subdir, subsubdirs, filename,
-						   boost::bind(push_back, boost::ref(results), _2));
-	}
-	else                            // CURRENT_SKIN
-	{
-		// CURRENT_SKIN turns out to be a bit of a misnomer because we might
-		// still return files from two different skins. In any case, this
-		// value of 'constraint' means we will return at most two paths: one
-		// for the default language, one for the current language (supposing
-		// those differ).
-		// It is important to allow a user to override only the localization
-		// for a particular file, for all viewer installs, without also
-		// overriding the default-language file.
-		// It is important to allow a user to override only the default-
-		// language file, for all viewer installs, without also overriding the
-		// applicable localization of that file.
-		// Therefore, treat the default language and the current language as
-		// two separate cases. For each, capture the most-specialized file
-		// that exists.
-		// Use a map keyed by subsubdir (i.e. language code). This allows us
-		// to handle the case of a single subsubdirs entry with the same logic
-		// that handles two. For every real file path generated by
-		// walkSearchSkinDirs(), update the map entry for its subsubdir.
-		StringMap path_for;
-		walkSearchSkinDirs(subdir, subsubdirs, filename,
-						   boost::bind(store_in_map, boost::ref(path_for), _1, _2));
-		// Now that we have a path for each of the default language and the
-		// current language, copy them -- in proper order -- into results.
-		// Don't drive this by walking the map itself: it matters that we
-		// generate results in the same order as subsubdirs.
-		BOOST_FOREACH(std::string subsubdir, subsubdirs)
-		{
-			StringMap::const_iterator found(path_for.find(subsubdir));
-			if (found != path_for.end())
-			{
-				results.push_back(found->second);
-			}
-		}
-	}
-
-	LL_DEBUGS("LLDir") << empty;
-	const char* comma = "";
-	BOOST_FOREACH(std::string path, results)
-	{
-		LL_CONT << comma << "'" << path << "'";
-		comma = ", ";
-	}
-	LL_CONT << LL_ENDL;
-
-	return results;
-}
-
-std::string LLDir::getTempFilename() const
-{
-	LLUUID random_uuid;
-	std::string uuid_str;
-
-	random_uuid.generate();
-	random_uuid.toString(uuid_str);
-
-	return add(getTempDir(), uuid_str + ".tmp");
-}
-
-// static
-std::string LLDir::getScrubbedFileName(const std::string uncleanFileName)
-{
-	std::string name(uncleanFileName);
-	const std::string illegalChars(getForbiddenFileChars());
-	// replace any illegal file chars with and underscore '_'
-	for( unsigned int i = 0; i < illegalChars.length(); i++ )
-	{
-		int j = -1;
-		while((j = name.find(illegalChars[i])) > -1)
-		{
-			name[j] = '_';
-		}
-	}
-	return name;
-}
-
-// static
-std::string LLDir::getForbiddenFileChars()
-{
-	return "\\/:*?\"<>|";
-}
-
-void LLDir::setLindenUserDir(const std::string &username)
-{
-	// if the username isn't set, that's bad
-	if (!username.empty())
-	{
-		// some platforms have case-sensitive filesystems, so be
-		// utterly consistent with our firstname/lastname case.
-		std::string userlower(username);
-		LLStringUtil::toLower(userlower);
-		LLStringUtil::replaceChar(userlower, ' ', '_');
-		mLindenUserDir = add(getOSUserAppDir(), userlower);
-	}
-	else
-	{
-		LL_ERRS() << "NULL name for LLDir::setLindenUserDir" << LL_ENDL;
-	}
-
-	dumpCurrentDirectories();	
-}
-
-void LLDir::setChatLogsDir(const std::string &path)
-{
-	if (!path.empty() )
-	{
-		mChatLogsDir = path;
-	}
-	else
-	{
-		LL_WARNS() << "Invalid name for LLDir::setChatLogsDir" << LL_ENDL;
-	}
-}
-
-void LLDir::updatePerAccountChatLogsDir()
-{
-	mPerAccountChatLogsDir = add(getChatLogsDir(), mUserName);
-}
-
-void LLDir::setPerAccountChatLogsDir(const std::string &username)
-{
-	// if both first and last aren't set, assume we're grabbing the cached dir
-	if (!username.empty())
-	{
-		// some platforms have case-sensitive filesystems, so be
-		// utterly consistent with our firstname/lastname case.
-		std::string userlower(username);
-		LLStringUtil::toLower(userlower);
-		LLStringUtil::replaceChar(userlower, ' ', '_');
-
-		mUserName = userlower;
-		updatePerAccountChatLogsDir();
-	}
-	else
-	{
-		LL_ERRS() << "NULL name for LLDir::setPerAccountChatLogsDir" << LL_ENDL;
-	}
-}
-
-void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language)
-{
-	LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'"
-					   << LL_ENDL;
-	mSkinName = skin_folder;
-	mLanguage = language;
-
-	// This method is called multiple times during viewer initialization. Each
-	// time it's called, reset mSearchSkinDirs.
-	mSearchSkinDirs.clear();
-
-	// base skin which is used as fallback for all skinned files
-	// e.g. c:\program files\secondlife\skins\default
-	mDefaultSkinDir = getSkinBaseDir();
-	append(mDefaultSkinDir, "default");
-	// This is always the most general of the search skin directories.
-	addSearchSkinDir(mDefaultSkinDir);
-
-	mSkinDir = getSkinBaseDir();
-	append(mSkinDir, skin_folder);
-	// Next level of generality is a skin installed with the viewer.
-	addSearchSkinDir(mSkinDir);
-
-	// user modifications to skins, current and default
-	// e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle
-	mUserSkinDir = getOSUserAppDir();
-	append(mUserSkinDir, "skins");
-	mUserDefaultSkinDir = mUserSkinDir;
-	append(mUserDefaultSkinDir, "default");
-	append(mUserSkinDir, skin_folder);
-	// Next level of generality is user modifications to default skin...
-	addSearchSkinDir(mUserDefaultSkinDir);
-	// then user-defined skins.
-	addSearchSkinDir(mUserSkinDir);
-}
-
-void LLDir::addSearchSkinDir(const std::string& skindir)
-{
-	if (std::find(mSearchSkinDirs.begin(), mSearchSkinDirs.end(), skindir) == mSearchSkinDirs.end())
-	{
-		LL_DEBUGS("LLDir") << "search skin: '" << skindir << "'" << LL_ENDL;
-		mSearchSkinDirs.push_back(skindir);
-	}
-}
-
-std::string LLDir::getSkinFolder() const
-{
-	return mSkinName;
-}
-
-std::string LLDir::getLanguage() const
-{
-	return mLanguage;
-}
-
-bool LLDir::setCacheDir(const std::string &path)
-{
-	if (path.empty() )
-	{
-		// reset to default
-		mCacheDir = "";
-		return true;
-	}
-	else
-	{
-		LLFile::mkdir(path);
-		std::string tempname = add(path, "temp");
-		LLFILE* file = LLFile::fopen(tempname,"wt");
-		if (file)
-		{
-			fclose(file);
-			LLFile::remove(tempname);
-			mCacheDir = path;
-			return true;
-		}
-		return false;
-	}
-}
-
-void LLDir::dumpCurrentDirectories(LLError::ELevel level)
-{
-	LL_VLOGS(level, "AppInit","Directories") << "Current Directories:" << LL_ENDL;
-
-	LL_VLOGS(level, "AppInit", "Directories") << "  CurPath:               " << getCurPath() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  AppName:               " << getAppName() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutableFilename:    " << getExecutableFilename() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutableDir:         " << getExecutableDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutablePathAndName: " << getExecutablePathAndName() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  WorkingDir:            " << getWorkingDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  AppRODataDir:          " << getAppRODataDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  OSUserDir:             " << getOSUserDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  OSUserAppDir:          " << getOSUserAppDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  LindenUserDir:         " << getLindenUserDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  TempDir:               " << getTempDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  CAFile:                " << getCAFile() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  SkinBaseDir:           " << getSkinBaseDir() << LL_ENDL;
-	LL_VLOGS(level, "AppInit", "Directories") << "  SkinDir:               " << getSkinDir() << LL_ENDL;
-}
-
-void LLDir::append(std::string& destpath, const std::string& name) const
-{
-	// Delegate question of whether we need a separator to helper method.
-	SepOff sepoff(needSep(destpath, name));
-	if (sepoff.first)               // do we need a separator?
-	{
-		destpath += mDirDelimiter;
-	}
-	// If destpath ends with a separator, AND name starts with one, skip
-	// name's leading separator.
-	destpath += name.substr(sepoff.second);
-}
-
-LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const
-{
-	if (path.empty() || name.empty())
-	{
-		// If either path or name are empty, we do not need a separator
-		// between them.
-		return SepOff(false, 0);
-	}
-	// Here we know path and name are both non-empty. But if path already ends
-	// with a separator, or if name already starts with a separator, we need
-	// not add one.
-	std::string::size_type seplen(mDirDelimiter.length());
-	bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter);
-	bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter);
-	if ((! path_ends_sep) && (! name_starts_sep))
-	{
-		// If neither path nor name brings a separator to the junction, then
-		// we need one.
-		return SepOff(true, 0);
-	}
-	if (path_ends_sep && name_starts_sep)
-	{
-		// But if BOTH path and name bring a separator, we need not add one.
-		// Moreover, we should actually skip the leading separator of 'name'.
-		return SepOff(false, (unsigned short)seplen);
-	}
-	// Here we know that either path_ends_sep or name_starts_sep is true --
-	// but not both. So don't add a separator, and don't skip any characters:
-	// simple concatenation will do the trick.
-	return SepOff(false, 0);
-}
-
-void dir_exists_or_crash(const std::string &dir_name)
-{
-#if LL_WINDOWS
-	// *FIX: lame - it doesn't do the same thing on windows. not so
-	// important since we don't deploy simulator to windows boxes.
-	LLFile::mkdir(dir_name, 0700);
-#else
-	struct stat dir_stat;
-	if(0 != LLFile::stat(dir_name, &dir_stat))
-	{
-		S32 stat_rv = errno;
-		if(ENOENT == stat_rv)
-		{
-		   if(0 != LLFile::mkdir(dir_name, 0700))		// octal
-		   {
-			   LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL;
-		   }
-		}
-		else
-		{
-			LL_ERRS() << "Unable to stat: " << dir_name << " errno = " << stat_rv
-				   << LL_ENDL;
-		}
-	}
-	else
-	{
-		// data_dir exists, make sure it's a directory.
-		if(!S_ISDIR(dir_stat.st_mode))
-		{
-			LL_ERRS() << "Data directory collision: " << dir_name << LL_ENDL;
-		}
-	}
-#endif
-}
diff --git a/indra/llcache/lldir.h b/indra/llcache/lldir.h
deleted file mode 100644
index 38e204ef04..0000000000
--- a/indra/llcache/lldir.h
+++ /dev/null
@@ -1,280 +0,0 @@
-/**  
- * @file lldir.h
- * @brief Definition of directory utilities class
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDIR_H
-#define LL_LLDIR_H
-
-#if LL_SOLARIS
-#include <sys/param.h>
-#define MAX_PATH MAXPATHLEN
-#endif
-
-// these numbers are read from settings_files.xml, so we need to be explicit
-typedef enum ELLPath
-{
-	LL_PATH_NONE = 0,
-	LL_PATH_USER_SETTINGS = 1,
-	LL_PATH_APP_SETTINGS = 2,	
-	LL_PATH_PER_SL_ACCOUNT = 3, // returns/expands to blank string if we don't know the account name yet
-	LL_PATH_CACHE = 4,	
-	LL_PATH_CHARACTER = 5,	
-	LL_PATH_HELP = 6,		
-	LL_PATH_LOGS = 7,
-	LL_PATH_TEMP = 8,
-	LL_PATH_SKINS = 9,
-	LL_PATH_TOP_SKIN = 10,
-	LL_PATH_CHAT_LOGS = 11,
-	LL_PATH_PER_ACCOUNT_CHAT_LOGS = 12,
-	LL_PATH_USER_SKIN = 14,
-	LL_PATH_LOCAL_ASSETS = 15,
-	LL_PATH_EXECUTABLE = 16,
-	LL_PATH_DEFAULT_SKIN = 17,
-	LL_PATH_FONTS = 18,
-    LL_PATH_DUMP = 19,
-	LL_PATH_LAST
-} ELLPath;
-
-/// Directory operations
-class LLDir
-{
- public:
-	LLDir();
-	virtual ~LLDir();
-
-	// app_name - Usually SecondLife, used for creating settings directories
-	// in OS-specific location, such as C:\Documents and Settings
-	// app_read_only_data_dir - Usually the source code directory, used
-	// for test applications to read newview data files.
-	virtual void initAppDirs(const std::string &app_name, 
-		const std::string& app_read_only_data_dir = "") = 0;
-
-	virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
-    U32 deleteDirAndContents(const std::string& dir_name);
-    std::vector<std::string> getFilesInDir(const std::string &dirname);
-// pure virtual functions
-	virtual std::string getCurPath() = 0;
-	virtual bool fileExists(const std::string &filename) const = 0;
-
-	const std::string findFile(const std::string& filename, const std::vector<std::string> filenames) const; 
-	const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const;
-
-	virtual std::string getLLPluginLauncher() = 0; // full path and name for the plugin shell
-	virtual std::string getLLPluginFilename(std::string base_name) = 0; // full path and name to the plugin DSO for this base_name (i.e. 'FOO' -> '/bar/baz/libFOO.so')
-
-	const std::string &getExecutablePathAndName() const;	// Full pathname of the executable
-	const std::string &getAppName() const;			// install directory under progams/ ie "SecondLife"
-	const std::string &getExecutableDir() const;	// Directory where the executable is located
-	const std::string &getExecutableFilename() const;// Filename of .exe
-	const std::string &getWorkingDir() const; // Current working directory
-	const std::string &getAppRODataDir() const;	// Location of read-only data files
-	const std::string &getOSUserDir() const;		// Location of the os-specific user dir
-	const std::string &getOSUserAppDir() const;	// Location of the os-specific user app dir
-	const std::string &getLindenUserDir() const;	// Location of the Linden user dir.
-	const std::string &getChatLogsDir() const;	// Location of the chat logs dir.
-	const std::string &getDumpDir() const;	// Location of the per-run dump dir.
-	const std::string &getPerAccountChatLogsDir() const;	// Location of the per account chat logs dir.
-	const std::string &getTempDir() const;			// Common temporary directory
-	const std::string  getCacheDir(bool get_default = false) const;	// Location of the cache.
-	const std::string &getOSCacheDir() const;		// location of OS-specific cache folder (may be empty string)
-	const std::string &getCAFile() const;			// File containing TLS certificate authorities
-	const std::string &getDirDelimiter() const;	// directory separator for platform (ie. '\' or '/' or ':')
-	const std::string &getDefaultSkinDir() const;	// folder for default skin. e.g. c:\program files\second life\skins\default
-	const std::string &getSkinDir() const;		// User-specified skin folder.
-	const std::string &getUserDefaultSkinDir() const; // dir with user modifications to default skin
-	const std::string &getUserSkinDir() const;		// User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin
-	const std::string getSkinBaseDir() const;		// folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins
-	const std::string &getLLPluginDir() const;		// Directory containing plugins and plugin shell
-	const std::string &getUserName() const;
-
-	// Expanded filename
-	std::string getExpandedFilename(ELLPath location, const std::string &filename) const;
-	std::string getExpandedFilename(ELLPath location, const std::string &subdir, const std::string &filename) const;
-	std::string getExpandedFilename(ELLPath location, const std::string &subdir1, const std::string &subdir2, const std::string &filename) const;
-
-	// Base and Directory name extraction
-	std::string getBaseFileName(const std::string& filepath, bool strip_exten = false) const;
-	std::string getDirName(const std::string& filepath) const;
-	std::string getExtension(const std::string& filepath) const; // Excludes '.', e.g getExtension("foo.wav") == "wav"
-
-	// these methods search the various skin paths for the specified file in the following order:
-	// getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir()
-	/// param value for findSkinnedFilenames(), explained below
-	enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS };
-	/**
-	 * Given a filename within skin, return an ordered sequence of paths to
-	 * search. Nonexistent files will be filtered out -- which means that the
-	 * vector might be empty.
-	 *
-	 * @param subdir Identify top-level skin subdirectory by passing one of
-	 * LLDir::XUI (file lives under "xui" subtree), LLDir::TEXTURES (file
-	 * lives under "textures" subtree), LLDir::SKINBASE (file lives at top
-	 * level of skin subdirectory).
-	 * @param filename Desired filename within subdir within skin, e.g.
-	 * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language.
-	 * @param constraint Callers perform two different kinds of processing.
-	 * When fetching a XUI file, for instance, the existence of @a filename in
-	 * the specified skin completely supercedes any @a filename in the default
-	 * skin. For that case, leave the default @a constraint=CURRENT_SKIN. The
-	 * returned vector will contain only
-	 * ".../<i>current_skin</i>/xui/en/<i>filename</i>",
-	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/<i>filename</i>".
-	 * But for (e.g.) "strings.xml", we want a given skin to be able to
-	 * override only specific entries from the default skin. Any string not
-	 * defined in the specified skin will be sought in the default skin. For
-	 * that case, pass @a constraint=ALL_SKINS. The returned vector will
-	 * contain at least ".../default/xui/en/strings.xml",
-	 * ".../default/xui/<i>current_language</i>/strings.xml",
-	 * ".../<i>current_skin</i>/xui/en/strings.xml",
-	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/strings.xml".
-	 */
-	std::vector<std::string> findSkinnedFilenames(const std::string& subdir,
-												  const std::string& filename,
-												  ESkinConstraint constraint=CURRENT_SKIN) const;
-	/// Values for findSkinnedFilenames(subdir) parameter
-	static const char *XUI, *TEXTURES, *SKINBASE;
-	/**
-	 * Return the base-language pathname from findSkinnedFilenames(), or
-	 * the empty string if no such file exists. Parameters are identical to
-	 * findSkinnedFilenames(). This is shorthand for capturing the vector
-	 * returned by findSkinnedFilenames(), checking for empty() and then
-	 * returning front().
-	 */
-	std::string findSkinnedFilenameBaseLang(const std::string &subdir,
-											const std::string &filename,
-											ESkinConstraint constraint=CURRENT_SKIN) const;
-	/**
-	 * Return the "most localized" pathname from findSkinnedFilenames(), or
-	 * the empty string if no such file exists. Parameters are identical to
-	 * findSkinnedFilenames(). This is shorthand for capturing the vector
-	 * returned by findSkinnedFilenames(), checking for empty() and then
-	 * returning back().
-	 */
-	std::string findSkinnedFilename(const std::string &subdir,
-									const std::string &filename,
-									ESkinConstraint constraint=CURRENT_SKIN) const;
-
-	// random filename in common temporary directory
-	std::string getTempFilename() const;
-
-	// For producing safe download file names from potentially unsafe ones
-	static std::string getScrubbedFileName(const std::string uncleanFileName);
-	static std::string getForbiddenFileChars();
-    void setDumpDir( const std::string& path );
-
-
-	virtual void setChatLogsDir(const std::string &path);		// Set the chat logs dir to this user's dir
-	virtual void setPerAccountChatLogsDir(const std::string &username);		// Set the per user chat log directory.
-	virtual void setLindenUserDir(const std::string &username);		// Set the linden user dir to this user's dir
-	virtual void setSkinFolder(const std::string &skin_folder, const std::string& language);
-	virtual std::string getSkinFolder() const;
-	virtual std::string getLanguage() const;
-	virtual bool setCacheDir(const std::string &path);
-	virtual void updatePerAccountChatLogsDir();
-
-	virtual void dumpCurrentDirectories(LLError::ELevel level = LLError::LEVEL_DEBUG);
-
-	// Utility routine
-	std::string buildSLOSCacheDir() const;
-
-	/// Append specified @a name to @a destpath, separated by getDirDelimiter()
-	/// if both are non-empty.
-	void append(std::string& destpath, const std::string& name) const;
-	/// Variadic form: append @a name0 and @a name1 and arbitrary other @a
-	/// names to @a destpath, separated by getDirDelimiter() as needed.
-	template <typename... NAMES>
-	void append(std::string& destpath, const std::string& name0, const std::string& name1,
-				const NAMES& ... names) const
-	{
-		// In a typical recursion case, we'd accept (destpath, name0, names).
-		// We accept (destpath, name0, name1, names) because it's important to
-		// delegate the two-argument case to the non-template implementation.
-		append(destpath, name0);
-		append(destpath, name1, names...);
-	}
-
-	/// Append specified @a names to @a path, separated by getDirDelimiter()
-	/// as needed. Return result, leaving @a path unmodified.
-	template <typename... NAMES>
-	std::string add(const std::string& path, const NAMES& ... names) const
-	{
-		std::string destpath(path);
-		append(destpath, names...);
-		return destpath;
-	}
-
-protected:
-	// Does an add() or append() call need a directory delimiter?
-	typedef std::pair<bool, unsigned short> SepOff;
-	SepOff needSep(const std::string& path, const std::string& name) const;
-	// build mSearchSkinDirs without adding duplicates
-	void addSearchSkinDir(const std::string& skindir);
-
-	// Internal to findSkinnedFilenames()
-	template <typename FUNCTION>
-	void walkSearchSkinDirs(const std::string& subdir,
-							const std::vector<std::string>& subsubdirs,
-							const std::string& filename,
-							const FUNCTION& function) const;
-
-	std::string mAppName;               // install directory under progams/ ie "SecondLife"   
-	std::string mExecutablePathAndName; // full path + Filename of .exe
-	std::string mExecutableFilename;    // Filename of .exe
-	std::string mExecutableDir;	 	 // Location of executable
-	std::string mWorkingDir;	 	 // Current working directory
-	std::string mAppRODataDir;			 // Location for static app data
-	std::string mOSUserDir;			 // OS Specific user directory
-	std::string mOSUserAppDir;			 // OS Specific user app directory
-	std::string mLindenUserDir;		 // Location for Linden user-specific data
-	std::string mPerAccountChatLogsDir;		 // Location for chat logs.
-	std::string mChatLogsDir;		 // Location for chat logs.
-	std::string mCAFile;				 // Location of the TLS certificate authority PEM file.
-	std::string mTempDir;
-	std::string mCacheDir;			// cache directory as set by user preference
-	std::string mDefaultCacheDir;	// default cache diretory
-	std::string mOSCacheDir;		// operating system cache dir
-	std::string mDirDelimiter;
-	std::string mSkinName;           // caller-specified skin name
-	std::string mSkinBaseDir;			// Base for skins paths.
-	std::string mDefaultSkinDir;			// Location for default skin info.
-	std::string mSkinDir;			// Location for current skin info.
-	std::string mUserDefaultSkinDir;		// Location for default skin info.
-	std::string mUserSkinDir;			// Location for user-modified skin info.
-	// Skin directories to search, most general to most specific. This order
-	// works well for composing fine-grained files, in which an individual item
-	// in a specific file overrides the corresponding item in more general
-	// files. Of course, for a file-level search, iterate backwards.
-	std::vector<std::string> mSearchSkinDirs;
-	std::string mLanguage;              // Current viewer language
-	std::string mLLPluginDir;			// Location for plugins and plugin shell
-    static std::string sDumpDir;            // Per-run crash report subdir of log directory.
-	std::string mUserName;				// Current user name
-};
-
-void dir_exists_or_crash(const std::string &dir_name);
-
-extern LLDir *gDirUtilp;
-
-#endif // LL_LLDIR_H
diff --git a/indra/llcache/lldir_linux.cpp b/indra/llcache/lldir_linux.cpp
deleted file mode 100644
index 80ad05345a..0000000000
--- a/indra/llcache/lldir_linux.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/** 
- * @file lldir_linux.cpp
- * @brief Implementation of directory utilities for linux
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lldir_linux.h"
-#include "llerror.h"
-#include "llrand.h"
-#include "llstring.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <glob.h>
-#include <pwd.h>
-
-
-static std::string getCurrentUserHome(char* fallback)
-{
-	const uid_t uid = getuid();
-	struct passwd *pw;
-
-	pw = getpwuid(uid);
-	if ((pw != NULL) && (pw->pw_dir != NULL))
-	{
-		return pw->pw_dir;
-	}
-
-	LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL;
-	auto home_env = LLStringUtil::getoptenv("HOME");
-	if (home_env)
-	{
-		return *home_env;
-	}
-	else
-	{
-		LL_WARNS() << "Couldn't detect home directory!  Falling back to " << fallback << LL_ENDL;
-		return fallback;
-	}
-}
-
-
-LLDir_Linux::LLDir_Linux()
-{
-	mDirDelimiter = "/";
-	mCurrentDirIndex = -1;
-	mCurrentDirCount = -1;
-	mDirp = NULL;
-
-	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
-	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
-	{
-		strcpy(tmp_str, "/tmp");
-		LL_WARNS() << "Could not get current directory; changing to "
-				<< tmp_str << LL_ENDL;
-		if (chdir(tmp_str) == -1)
-		{
-			LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL;
-		}
-	}
-
-	mExecutableFilename = "";
-	mExecutablePathAndName = "";
-	mExecutableDir = tmp_str;
-	mWorkingDir = tmp_str;
-#ifdef APP_RO_DATA_DIR
-	mAppRODataDir = APP_RO_DATA_DIR;
-#else
-	mAppRODataDir = tmp_str;
-#endif
-    std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-linux-");
-    if (build_dir_pos != std::string::npos)
-    {
-		// ...we're in a dev checkout
-		mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins";
-		LL_INFOS() << "Running in dev checkout with mSkinBaseDir "
-		 << mSkinBaseDir << LL_ENDL;
-    }
-    else
-    {
-		// ...normal installation running
-		mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins";
-    }	
-
-	mOSUserDir = getCurrentUserHome(tmp_str);
-	mOSUserAppDir = "";
-	mLindenUserDir = "";
-
-	char path [32];	/* Flawfinder: ignore */ 
-
-	// *NOTE: /proc/%d/exe doesn't work on FreeBSD. But that's ok,
-	// because this is the linux implementation.
-
-	snprintf (path, sizeof(path), "/proc/%d/exe", (int) getpid ()); 
-	int rc = readlink (path, tmp_str, sizeof (tmp_str)-1);	/* Flawfinder: ignore */ 
-	if ( (rc != -1) && (rc <= ((int) sizeof (tmp_str)-1)) )
-	{
-		tmp_str[rc] = '\0'; //readlink() doesn't 0-terminate the buffer
-		mExecutablePathAndName = tmp_str;
-		char *path_end;
-		if ((path_end = strrchr(tmp_str,'/')))
-		{
-			*path_end = '\0';
-			mExecutableDir = tmp_str;
-			mWorkingDir = tmp_str;
-			mExecutableFilename = path_end+1;
-		}
-		else
-		{
-			mExecutableFilename = tmp_str;
-		}
-	}
-
-	mLLPluginDir = mExecutableDir + mDirDelimiter + "llplugin";
-
-	// *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something.
-	mTempDir = "/tmp";
-}
-
-LLDir_Linux::~LLDir_Linux()
-{
-}
-
-// Implementation
-
-
-void LLDir_Linux::initAppDirs(const std::string &app_name,
-							  const std::string& app_read_only_data_dir)
-{
-	// Allow override so test apps can read newview directory
-	if (!app_read_only_data_dir.empty())
-	{
-		mAppRODataDir = app_read_only_data_dir;
-		mSkinBaseDir = add(mAppRODataDir, "skins");
-	}
-	mAppName = app_name;
-
-	std::string upper_app_name(app_name);
-	LLStringUtil::toUpper(upper_app_name);
-
-	auto app_home_env(LLStringUtil::getoptenv(upper_app_name + "_USER_DIR"));
-	if (app_home_env)
-	{
-		// user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
-		mOSUserAppDir = *app_home_env;
-	}
-	else
-	{
-		// traditionally on unixoids, MyApp gets ~/.myapp dir for data
-		mOSUserAppDir = mOSUserDir;
-		mOSUserAppDir += "/";
-		mOSUserAppDir += ".";
-		std::string lower_app_name(app_name);
-		LLStringUtil::toLower(lower_app_name);
-		mOSUserAppDir += lower_app_name;
-	}
-
-	// create any directories we expect to write to.
-
-	int res = LLFile::mkdir(mOSUserAppDir);
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
-		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
-		mOSUserAppDir = mOSUserDir;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
-	}
-	
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
-	}
-
-	mCAFile = getExpandedFilename(LL_PATH_EXECUTABLE, "ca-bundle.crt");
-}
-
-U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string &mask)
-{
-	U32 file_count = 0;
-	glob_t g;
-
-	std::string tmp_str;
-	tmp_str = dirname;
-	tmp_str += mask;
-	
-	if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
-	{
-		file_count = g.gl_pathc;
-
-		globfree(&g);
-	}
-
-	return (file_count);
-}
-
-std::string LLDir_Linux::getCurPath()
-{
-	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
-	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
-	{
-		LL_WARNS() << "Could not get current directory" << LL_ENDL;
-		tmp_str[0] = '\0';
-	}
-	return tmp_str;
-}
-
-
-bool LLDir_Linux::fileExists(const std::string &filename) const
-{
-	struct stat stat_data;
-	// Check the age of the file
-	// Now, we see if the files we've gathered are recent...
-	int res = stat(filename.c_str(), &stat_data);
-	if (!res)
-	{
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
-
-/*virtual*/ std::string LLDir_Linux::getLLPluginLauncher()
-{
-	return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
-		"SLPlugin";
-}
-
-/*virtual*/ std::string LLDir_Linux::getLLPluginFilename(std::string base_name)
-{
-	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
-		"lib" + base_name + ".so";
-}
diff --git a/indra/llcache/lldir_linux.h b/indra/llcache/lldir_linux.h
deleted file mode 100644
index e83a020ba4..0000000000
--- a/indra/llcache/lldir_linux.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/** 
- * @file lldir_linux.h
- * @brief Definition of directory utilities class for linux
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#if !LL_LINUX
-#error This header must not be included when compiling for any target other than Linux. Consider including lldir.h instead.
-#endif // !LL_LINUX
-
-#ifndef LL_LLDIR_LINUX_H
-#define LL_LLDIR_LINUX_H
-
-#include "lldir.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-class LLDir_Linux : public LLDir
-{
-public:
-	LLDir_Linux();
-	virtual ~LLDir_Linux();
-
-	/*virtual*/ void initAppDirs(const std::string &app_name,
-		const std::string& app_read_only_data_dir);
-
-	virtual std::string getCurPath();
-	virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
-	/*virtual*/ bool fileExists(const std::string &filename) const;
-
-	/*virtual*/ std::string getLLPluginLauncher();
-	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
-
-private:
-	DIR *mDirp;
-	int mCurrentDirIndex;
-	int mCurrentDirCount;
-	std::string mCurrentDir;
-};
-
-#endif // LL_LLDIR_LINUX_H
-
-
diff --git a/indra/llcache/lldir_mac.cpp b/indra/llcache/lldir_mac.cpp
deleted file mode 100644
index 3bc4ee844e..0000000000
--- a/indra/llcache/lldir_mac.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/** 
- * @file lldir_mac.cpp
- * @brief Implementation of directory utilities for Mac OS X
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */ 
-
-#if LL_DARWIN
-
-#include "linden_common.h"
-
-#include "lldir_mac.h"
-#include "llerror.h"
-#include "llrand.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <glob.h>
-#include <boost/filesystem.hpp>
-#include "lldir_utils_objc.h"
-
-// --------------------------------------------------------------------------------
-
-static bool CreateDirectory(const std::string &parent, 
-                            const std::string &child,
-                            std::string *fullname)
-{
-    
-    boost::filesystem::path p(parent);
-    p /= child;
-    
-    if (fullname)
-        *fullname = std::string(p.string());
-    
-    if (! boost::filesystem::create_directory(p))
-    {
-        return (boost::filesystem::is_directory(p));
-    }
-    return true;
-}
-
-// --------------------------------------------------------------------------------
-
-LLDir_Mac::LLDir_Mac()
-{
-	mDirDelimiter = "/";
-
-    const std::string     secondLifeString = "SecondLife";
-    
-    std::string *executablepathstr = getSystemExecutableFolder();
-
-    //NOTE:  LLINFOS/LLERRS will not output to log here.  The streams are not initialized.
-    
-	if (executablepathstr)
-	{
-		// mExecutablePathAndName
-		mExecutablePathAndName = *executablepathstr;
-        
-        boost::filesystem::path executablepath(*executablepathstr);
-        
-# ifndef BOOST_SYSTEM_NO_DEPRECATED
-#endif
-        mExecutableFilename = executablepath.filename().string();
-        mExecutableDir = executablepath.parent_path().string();
-		
-		// mAppRODataDir
-        std::string *resourcepath = getSystemResourceFolder();
-        mAppRODataDir = *resourcepath;
-		
-		// *NOTE: When running in a dev tree, use the copy of
-		// skins in indra/newview/ rather than in the application bundle.  This
-		// mirrors Windows dev environment behavior and allows direct checkin
-		// of edited skins/xui files. JC
-		
-		// MBW -- This keeps the mac application from finding other things.
-		// If this is really for skins, it should JUST apply to skins.
-        
-		std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-darwin-");
-		if (build_dir_pos != std::string::npos)
-		{
-			// ...we're in a dev checkout
-			mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos)
-				+ "/indra/newview/skins";
-			LL_INFOS() << "Running in dev checkout with mSkinBaseDir "
-				<< mSkinBaseDir << LL_ENDL;
-		}
-		else
-		{
-			// ...normal installation running
-			mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins";
-		}
-		
-		// mOSUserDir
-        std::string *appdir = getSystemApplicationSupportFolder();
-        std::string rootdir;
-
-        //Create root directory
-        if (CreateDirectory(*appdir, secondLifeString, &rootdir))
-        {
-            
-            // Save the full path to the folder
-            mOSUserDir = rootdir;
-            
-            // Create our sub-dirs
-            CreateDirectory(rootdir, std::string("data"), NULL);
-            CreateDirectory(rootdir, std::string("logs"), NULL);
-            CreateDirectory(rootdir, std::string("user_settings"), NULL);
-            CreateDirectory(rootdir, std::string("browser_profile"), NULL);
-        }
-    
-		//mOSCacheDir
-        std::string *cachedir =  getSystemCacheFolder();
-
-        if (cachedir)
-		
-		{
-            mOSCacheDir = *cachedir;
-            //TODO:  This changes from ~/Library/Cache/Secondlife to ~/Library/Cache/com.app.secondlife/Secondlife.  Last dir level could go away.
-            CreateDirectory(mOSCacheDir, secondLifeString, NULL);
-		}
-		
-		// mOSUserAppDir
-		mOSUserAppDir = mOSUserDir;
-		
-		// mTempDir
-        //Aura 120920 boost::filesystem::temp_directory_path() not yet implemented on mac. :(
-        std::string *tmpdir = getSystemTempFolder();
-        if (tmpdir)
-        {
-            
-            CreateDirectory(*tmpdir, secondLifeString, &mTempDir);
-            if (tmpdir) delete tmpdir;
-        }
-		
-		mWorkingDir = getCurPath();
-
-		mLLPluginDir = mAppRODataDir + mDirDelimiter + "llplugin";
-	}
-}
-
-LLDir_Mac::~LLDir_Mac()
-{
-}
-
-// Implementation
-
-
-void LLDir_Mac::initAppDirs(const std::string &app_name,
-							const std::string& app_read_only_data_dir)
-{
-	// Allow override so test apps can read newview directory
-	if (!app_read_only_data_dir.empty())
-	{
-		mAppRODataDir = app_read_only_data_dir;
-		mSkinBaseDir = add(mAppRODataDir, "skins");
-	}
-	mCAFile = add(mAppRODataDir, "ca-bundle.crt");
-}
-
-std::string LLDir_Mac::getCurPath()
-{
-	return boost::filesystem::path( boost::filesystem::current_path() ).string();
-}
-
-
-
-bool LLDir_Mac::fileExists(const std::string &filename) const
-{
-    return boost::filesystem::exists(filename);
-}
-
-
-/*virtual*/ std::string LLDir_Mac::getLLPluginLauncher()
-{
-	return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() +
-		"SLPlugin.app/Contents/MacOS/SLPlugin";
-}
-
-/*virtual*/ std::string LLDir_Mac::getLLPluginFilename(std::string base_name)
-{
-	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
-		base_name + ".dylib";
-}
-
-
-#endif // LL_DARWIN
diff --git a/indra/llcache/lldir_mac.h b/indra/llcache/lldir_mac.h
deleted file mode 100644
index 558727ebbc..0000000000
--- a/indra/llcache/lldir_mac.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/** 
- * @file lldir_mac.h
- * @brief Definition of directory utilities class for Mac OS X
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */ 
-
-#if !LL_DARWIN
-#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead.
-#endif // !LL_DARWIN
-
-#ifndef LL_LLDIR_MAC_H
-#define LL_LLDIR_MAC_H
-
-#include "lldir.h"
-
-#include <dirent.h>
-
-class LLDir_Mac : public LLDir
-{
-public:
-	LLDir_Mac();
-	virtual ~LLDir_Mac();
-
-	/*virtual*/ void initAppDirs(const std::string &app_name,
-		const std::string& app_read_only_data_dir);
-
-	virtual std::string getCurPath();
-	virtual bool fileExists(const std::string &filename) const;
-
-	/*virtual*/ std::string getLLPluginLauncher();
-	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
-};
-
-#endif // LL_LLDIR_MAC_H
-
-
diff --git a/indra/llcache/lldir_solaris.cpp b/indra/llcache/lldir_solaris.cpp
deleted file mode 100644
index f18560ff20..0000000000
--- a/indra/llcache/lldir_solaris.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/** 
- * @file fmodwrapper.cpp
- * @brief dummy source file for building a shared library to wrap libfmod.a
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lldir_solaris.h"
-#include "llerror.h"
-#include "llrand.h"
-#include "llstring.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <glob.h>
-#include <pwd.h>
-#include <sys/utsname.h>
-#define _STRUCTURED_PROC 1
-#include <sys/procfs.h>
-#include <fcntl.h>
-
-static std::string getCurrentUserHome(char* fallback)
-{
-	// fwiw this exactly duplicates getCurrentUserHome() in lldir_linux.cpp...
-	// we should either derive both from LLDir_Posix or just axe Solaris.
-	const uid_t uid = getuid();
-	struct passwd *pw;
-
-	pw = getpwuid(uid);
-	if ((pw != NULL) && (pw->pw_dir != NULL))
-	{
-		return pw->pw_dir;
-	}
-
-	LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL;
-	auto home_env = LLStringUtil::getoptenv("HOME");
-	if (home_env)
-	{
-		return *home_env;
-	}
-	else
-	{
-		LL_WARNS() << "Couldn't detect home directory!  Falling back to " << fallback << LL_ENDL;
-		return fallback;
-	}
-}
-
-
-LLDir_Solaris::LLDir_Solaris()
-{
-	mDirDelimiter = "/";
-	mCurrentDirIndex = -1;
-	mCurrentDirCount = -1;
-	mDirp = NULL;
-
-	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
-	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
-	{
-		strcpy(tmp_str, "/tmp");
-		LL_WARNS() << "Could not get current directory; changing to "
-				<< tmp_str << LL_ENDL;
-		if (chdir(tmp_str) == -1)
-		{
-			LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL;
-		}
-	}
-
-	mExecutableFilename = "";
-	mExecutablePathAndName = "";
-	mExecutableDir = strdup(tmp_str);
-	mWorkingDir = strdup(tmp_str);
-	mAppRODataDir = strdup(tmp_str);
-	mOSUserDir = getCurrentUserHome(tmp_str);
-	mOSUserAppDir = "";
-	mLindenUserDir = "";
-
-	char path [LL_MAX_PATH];	/* Flawfinder: ignore */ 
-
-	sprintf(path, "/proc/%d/psinfo", (int)getpid());
-	int proc_fd = -1;
-	if((proc_fd = open(path, O_RDONLY)) == -1){
-		LL_WARNS() << "unable to open " << path << LL_ENDL;
-		return;
-	}
-	psinfo_t proc_psinfo;
-	if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
-		LL_WARNS() << "Unable to read " << path << LL_ENDL;
-		close(proc_fd);
-		return;
-	}
-
-	close(proc_fd);
-
-	mExecutableFilename = strdup(proc_psinfo.pr_fname);
-	LL_INFOS() << "mExecutableFilename = [" << mExecutableFilename << "]" << LL_ENDL;
-
-	sprintf(path, "/proc/%d/path/a.out", (int)getpid());
-
-	char execpath[LL_MAX_PATH];
-	if(readlink(path, execpath, LL_MAX_PATH) == -1){
-		LL_WARNS() << "Unable to read link from " << path << LL_ENDL;
-		return;
-	}
-
-	char *p = execpath;			// nuke trash in link, if any exists
-	int i = 0;
-	while(*p != NULL && ++i < LL_MAX_PATH && isprint((int)(*p++)));
-	*p = NULL;
-
-	mExecutablePathAndName = strdup(execpath);
-	LL_INFOS() << "mExecutablePathAndName = [" << mExecutablePathAndName << "]" << LL_ENDL;
-
-	//NOTE: Why force people to cd into the package directory?
-	//      Look for SECONDLIFE env variable and use it, if set.
-
-	auto SECONDLIFE(LLDirUtil::getoptenv("SECONDLIFE"));
-	if(SECONDLIFE){
-		mExecutableDir = add(*SECONDLIFE, "bin"); //NOTE:  make sure we point at the bin
-	}else{
-		mExecutableDir = getDirName(execpath);
-		LL_INFOS() << "mExecutableDir = [" << mExecutableDir << "]" << LL_ENDL;
-	}
-
-	mLLPluginDir = add(mExecutableDir, "llplugin");
-
-	// *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something.
-	mTempDir = "/tmp";
-}
-
-LLDir_Solaris::~LLDir_Solaris()
-{
-}
-
-// Implementation
-
-
-void LLDir_Solaris::initAppDirs(const std::string &app_name,
-								const std::string& app_read_only_data_dir)
-{
-	// Allow override so test apps can read newview directory
-	if (!app_read_only_data_dir.empty())
-	{
-		mAppRODataDir = app_read_only_data_dir;
-		mSkinBaseDir = add(mAppRODataDir, "skins");
-	}
-	mAppName = app_name;
-
-	std::string upper_app_name(app_name);
-	LLStringUtil::toUpper(upper_app_name);
-
-	auto app_home_env(LLStringUtil::getoptenv(upper_app_name + "_USER_DIR"));
-	if (app_home_env)
-	{
-		// user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
-		mOSUserAppDir = *app_home_env;
-	}
-	else
-	{
-		// traditionally on unixoids, MyApp gets ~/.myapp dir for data
-		mOSUserAppDir = mOSUserDir;
-		mOSUserAppDir += "/";
-		mOSUserAppDir += ".";
-		std::string lower_app_name(app_name);
-		LLStringUtil::toLower(lower_app_name);
-		mOSUserAppDir += lower_app_name;
-	}
-
-	// create any directories we expect to write to.
-
-	int res = LLFile::mkdir(mOSUserAppDir);
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
-		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
-		mOSUserAppDir = mOSUserDir;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
-	}
-	
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
-	}
-
-	mCAFile = getExpandedFilename(LL_PATH_EXECUTABLE, "ca-bundle.crt");
-}
-
-U32 LLDir_Solaris::countFilesInDir(const std::string &dirname, const std::string &mask)
-{
-	U32 file_count = 0;
-	glob_t g;
-
-	std::string tmp_str;
-	tmp_str = dirname;
-	tmp_str += mask;
-	
-	if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
-	{
-		file_count = g.gl_pathc;
-
-		globfree(&g);
-	}
-
-	return (file_count);
-}
-
-std::string LLDir_Solaris::getCurPath()
-{
-	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
-	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
-	{
-		LL_WARNS() << "Could not get current directory" << LL_ENDL;
-		tmp_str[0] = '\0';
-	}
-	return tmp_str;
-}
-
-
-bool LLDir_Solaris::fileExists(const std::string &filename) const
-{
-	struct stat stat_data;
-	// Check the age of the file
-	// Now, we see if the files we've gathered are recent...
-	int res = stat(filename.c_str(), &stat_data);
-	if (!res)
-	{
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
diff --git a/indra/llcache/lldir_solaris.h b/indra/llcache/lldir_solaris.h
deleted file mode 100644
index c6dac57e14..0000000000
--- a/indra/llcache/lldir_solaris.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/** 
- * @file fmodwrapper.cpp
- * @brief dummy source file for building a shared library to wrap libfmod.a
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#if !LL_SOLARIS
-#error This header must not be included when compiling for any target other than Solaris. Consider including lldir.h instead.
-#endif // !LL_SOLARIS
-
-#ifndef LL_LLDIR_SOLARIS_H
-#define LL_LLDIR_SOLARIS_H
-
-#include "lldir.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-class LLDir_Solaris : public LLDir
-{
-public:
-	LLDir_Solaris();
-	virtual ~LLDir_Solaris();
-
-	/*virtual*/ void initAppDirs(const std::string &app_name,
-		const std::string& app_read_only_data_dir);
-
-	virtual std::string getCurPath();
-	virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
-	/*virtual*/ bool fileExists(const std::string &filename) const;
-
-private:
-	DIR *mDirp;
-	int mCurrentDirIndex;
-	int mCurrentDirCount;
-	std::string mCurrentDir;
-};
-
-#endif // LL_LLDIR_SOLARIS_H
-
-
diff --git a/indra/llcache/lldir_utils_objc.h b/indra/llcache/lldir_utils_objc.h
deleted file mode 100644
index 12019c4284..0000000000
--- a/indra/llcache/lldir_utils_objc.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/** 
- * @file lldir_utils_objc.h
- * @brief Definition of directory utilities class for Mac OS X
- *
- * $LicenseInfo:firstyear=2020&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2020, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */ 
-
-#if !LL_DARWIN
-#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead.
-#endif // !LL_DARWIN
-
-#ifndef LL_LLDIR_UTILS_OBJC_H
-#define LL_LLDIR_UTILS_OBJC_H
-
-#include <iostream>
-
-std::string* getSystemTempFolder();
-std::string* getSystemCacheFolder();
-std::string* getSystemApplicationSupportFolder();
-std::string* getSystemResourceFolder();
-std::string* getSystemExecutableFolder();
-
-
-#endif // LL_LLDIR_UTILS_OBJC_H
diff --git a/indra/llcache/lldir_utils_objc.mm b/indra/llcache/lldir_utils_objc.mm
deleted file mode 100644
index da55a2f897..0000000000
--- a/indra/llcache/lldir_utils_objc.mm
+++ /dev/null
@@ -1,108 +0,0 @@
-/** 
- * @file lldir_utils_objc.mm
- * @brief Cocoa implementation of directory utilities for Mac OS X
- *
- * $LicenseInfo:firstyear=2020&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2020, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */ 
-#if LL_DARWIN
-
-//WARNING:  This file CANNOT use standard linden includes due to conflicts between definitions of BOOL
-
-#include "lldir_utils_objc.h"
-#import <Cocoa/Cocoa.h>
-
-std::string* getSystemTempFolder()
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    NSString * tempDir = NSTemporaryDirectory();
-    if (tempDir == nil)
-        tempDir = @"/tmp";
-    std::string *result = ( new std::string([tempDir UTF8String]) );
-    [pool release];
-    
-    return result;
-}
-
-//findSystemDirectory scoped exclusively to this file. 
-std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
-                                   NSSearchPathDomainMask domainMask)
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    
-    std::string *result = nil;
-    NSString *path = nil;
-    
-    // Search for the path
-    NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
-                                                         domainMask,
-                                                         YES);
-    if ([paths count])
-    {
-        path = [paths objectAtIndex:0];
-        //HACK:  Always attempt to create directory, ignore errors.
-        NSError *error = nil;
-
-        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
-
-        
-        result = new std::string([path UTF8String]);        
-    }
-    [pool release];
-    return result;
-}
-
-std::string* getSystemExecutableFolder()
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-    NSString *bundlePath = [[NSBundle mainBundle] executablePath];
-    std::string *result = (new std::string([bundlePath UTF8String]));  
-    [pool release];
-
-    return result;
-}
-
-std::string* getSystemResourceFolder()
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-    NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
-    std::string *result = (new std::string([bundlePath UTF8String]));
-    [pool release];
-    
-    return result;
-}
-
-std::string* getSystemCacheFolder()
-{
-    return findSystemDirectory (NSCachesDirectory,
-                                NSUserDomainMask);
-}
-
-std::string* getSystemApplicationSupportFolder()
-{
-    return findSystemDirectory (NSApplicationSupportDirectory,
-                                NSUserDomainMask);
-    
-}
-
-#endif // LL_DARWIN
diff --git a/indra/llcache/lldir_win32.cpp b/indra/llcache/lldir_win32.cpp
deleted file mode 100644
index b3b3afb37e..0000000000
--- a/indra/llcache/lldir_win32.cpp
+++ /dev/null
@@ -1,452 +0,0 @@
-/** 
- * @file lldir_win32.cpp
- * @brief Implementation of directory utilities for windows
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#if LL_WINDOWS
-
-#include "linden_common.h"
-
-#include "lldir_win32.h"
-#include "llerror.h"
-#include "llstring.h"
-#include "stringize.h"
-#include "llfile.h"
-#include <shlobj.h>
-#include <fstream>
-
-#include <direct.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-// Utility stuff to get versions of the sh
-#define PACKVERSION(major,minor) MAKELONG(minor,major)
-DWORD GetDllVersion(LPCTSTR lpszDllName);
-
-namespace
-{ // anonymous
-    enum class prst { INIT, OPEN, SKIP } state = prst::INIT;
-    // This is called so early that we can't count on static objects being
-    // properly constructed yet, so declare a pointer instead of an instance.
-    std::ofstream* prelogf = nullptr;
-
-    void prelog(const std::string& message)
-    {
-        boost::optional<std::string> prelog_name;
-
-        switch (state)
-        {
-        case prst::INIT:
-            // assume we failed, until we succeed
-            state = prst::SKIP;
-
-            prelog_name = LLStringUtil::getoptenv("PRELOG");
-            if (! prelog_name)
-                // no PRELOG variable set, carry on
-                return;
-            prelogf = new llofstream(*prelog_name, std::ios_base::app);
-            if (! (prelogf && prelogf->is_open()))
-                // can't complain to anybody; how?
-                return;
-            // got the log file open, cool!
-            state = prst::OPEN;
-            (*prelogf) << "========================================================================"
-                       << std::endl;
-            // fall through, don't break
-
-        case prst::OPEN:
-            (*prelogf) << message << std::endl;
-            break;
-
-        case prst::SKIP:
-            // either PRELOG isn't set, or we failed to open that pathname
-            break;
-        }
-    }
-} // anonymous namespace
-
-#define PRELOG(expression) prelog(STRINGIZE(expression))
-
-LLDir_Win32::LLDir_Win32()
-{
-	// set this first: used by append() and add() methods
-	mDirDelimiter = "\\";
-
-	WCHAR w_str[MAX_PATH];
-	// Application Data is where user settings go. We rely on $APPDATA being
-	// correct.
-	auto APPDATA = LLStringUtil::getoptenv("APPDATA");
-	if (APPDATA)
-	{
-		mOSUserDir = *APPDATA;
-	}
-	PRELOG("APPDATA='" << mOSUserDir << "'");
-	// On Windows, we could have received a plain-ASCII pathname in which
-	// non-ASCII characters have been munged to '?', or the pathname could
-	// have been badly encoded and decoded such that we now have garbage
-	// instead of a valid path. Check that mOSUserDir actually exists.
-	if (mOSUserDir.empty() || ! fileExists(mOSUserDir))
-	{
-		PRELOG("APPDATA does not exist");
-		//HRESULT okay = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, w_str);
-		wchar_t *pwstr = NULL;
-		HRESULT okay = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &pwstr);
-		PRELOG("SHGetKnownFolderPath(FOLDERID_RoamingAppData) returned " << okay);
-		if (SUCCEEDED(okay) && pwstr)
-		{
-			// But of course, only update mOSUserDir if SHGetKnownFolderPath() works.
-			mOSUserDir = ll_convert_wide_to_string(pwstr);
-			// Not only that: update our environment so that child processes
-			// will see a reasonable value as well.
-			_wputenv_s(L"APPDATA", pwstr);
-			// SHGetKnownFolderPath() contract requires us to free pwstr
-			CoTaskMemFree(pwstr);
-			PRELOG("mOSUserDir='" << mOSUserDir << "'");
-		}
-	}
-
-	// We want cache files to go on the local disk, even if the
-	// user is on a network with a "roaming profile".
-	//
-	// On Vista this is:
-	//   C:\Users\James\AppData\Local
-	//
-	// We used to store the cache in AppData\Roaming, and the installer
-	// cleans up that version on upgrade.  JC
-	auto LOCALAPPDATA = LLStringUtil::getoptenv("LOCALAPPDATA");
-	if (LOCALAPPDATA)
-	{
-		mOSCacheDir = *LOCALAPPDATA;
-	}
-	PRELOG("LOCALAPPDATA='" << mOSCacheDir << "'");
-	// Windows really does not deal well with pathnames containing non-ASCII
-	// characters. See above remarks about APPDATA.
-	if (mOSCacheDir.empty() || ! fileExists(mOSCacheDir))
-	{
-		PRELOG("LOCALAPPDATA does not exist");
-		//HRESULT okay = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, w_str);
-		wchar_t *pwstr = NULL;
-		HRESULT okay = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &pwstr);
-		PRELOG("SHGetKnownFolderPath(FOLDERID_LocalAppData) returned " << okay);
-		if (SUCCEEDED(okay) && pwstr)
-		{
-			// But of course, only update mOSCacheDir if SHGetKnownFolderPath() works.
-			mOSCacheDir = ll_convert_wide_to_string(pwstr);
-			// Update our environment so that child processes will see a
-			// reasonable value as well.
-			_wputenv_s(L"LOCALAPPDATA", pwstr);
-			// SHGetKnownFolderPath() contract requires us to free pwstr
-			CoTaskMemFree(pwstr);
-			PRELOG("mOSCacheDir='" << mOSCacheDir << "'");
-		}
-	}
-
-	if (GetTempPath(MAX_PATH, w_str))
-	{
-		if (wcslen(w_str))	/* Flawfinder: ignore */ 
-		{
-			w_str[wcslen(w_str)-1] = '\0'; /* Flawfinder: ignore */ // remove trailing slash
-		}
-		mTempDir = utf16str_to_utf8str(llutf16string(w_str));
-
-		if (mOSUserDir.empty())
-		{
-			mOSUserDir = mTempDir;
-		}
-
-		if (mOSCacheDir.empty())
-		{
-			mOSCacheDir = mTempDir;
-		}
-	}
-	else
-	{
-		mTempDir = mOSUserDir;
-	}
-
-/*==========================================================================*|
-	// Now that we've got mOSUserDir, one way or another, let's see how we did
-	// with our environment variables.
-	{
-		auto report = [this](std::ostream& out){
-			out << "mOSUserDir  = '" << mOSUserDir  << "'\n"
-				<< "mOSCacheDir = '" << mOSCacheDir << "'\n"
-				<< "mTempDir    = '" << mTempDir    << "'" << std::endl;
-		};
-		int res = LLFile::mkdir(mOSUserDir);
-		if (res == -1)
-		{
-			// If we couldn't even create the directory, just blurt to stderr
-			report(std::cerr);
-		}
-		else
-		{
-			// successfully created logdir, plunk a log file there
-			std::string logfilename(add(mOSUserDir, "lldir.log"));
-			std::ofstream logfile(logfilename.c_str());
-			if (! logfile.is_open())
-			{
-				report(std::cerr);
-			}
-			else
-			{
-				report(logfile);
-			}
-		}
-	}
-|*==========================================================================*/
-
-//	fprintf(stderr, "mTempDir = <%s>",mTempDir);
-
-	// Set working directory, for LLDir::getWorkingDir()
-	GetCurrentDirectory(MAX_PATH, w_str);
-	mWorkingDir = utf16str_to_utf8str(llutf16string(w_str));
-
-	// Set the executable directory
-	S32 size = GetModuleFileName(NULL, w_str, MAX_PATH);
-	if (size)
-	{
-		w_str[size] = '\0';
-		mExecutablePathAndName = utf16str_to_utf8str(llutf16string(w_str));
-		S32 path_end = mExecutablePathAndName.find_last_of('\\');
-		if (path_end != std::string::npos)
-		{
-			mExecutableDir = mExecutablePathAndName.substr(0, path_end);
-			mExecutableFilename = mExecutablePathAndName.substr(path_end+1, std::string::npos);
-		}
-		else
-		{
-			mExecutableFilename = mExecutablePathAndName;
-		}
-
-	}
-	else
-	{
-		fprintf(stderr, "Couldn't get APP path, assuming current directory!");
-		mExecutableDir = mWorkingDir;
-		// Assume it's the current directory
-	}
-
-	// mAppRODataDir = ".";	
-
-	// Determine the location of the App-Read-Only-Data
-	// Try the working directory then the exe's dir.
-	mAppRODataDir = mWorkingDir;	
-
-
-//	if (mExecutableDir.find("indra") == std::string::npos)
-	
-	// *NOTE:Mani - It is a mistake to put viewer specific code in
-	// the LLDir implementation. The references to 'skins' and 
-	// 'llplugin' need to go somewhere else.
-	// alas... this also gets called during static initialization 
-	// time due to the construction of gDirUtil in lldir.cpp.
-	if(! LLFile::isdir(add(mAppRODataDir, "skins")))
-	{
-		// What? No skins in the working dir?
-		// Try the executable's directory.
-		mAppRODataDir = mExecutableDir;
-	}
-
-//	LL_INFOS() << "mAppRODataDir = " << mAppRODataDir << LL_ENDL;
-
-	mSkinBaseDir = add(mAppRODataDir, "skins");
-
-	// Build the default cache directory
-	mDefaultCacheDir = buildSLOSCacheDir();
-	
-	// Make sure it exists
-	int res = LLFile::mkdir(mDefaultCacheDir);
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << mDefaultCacheDir << LL_ENDL;
-	}
-
-	mLLPluginDir = add(mExecutableDir, "llplugin");
-}
-
-LLDir_Win32::~LLDir_Win32()
-{
-}
-
-// Implementation
-
-void LLDir_Win32::initAppDirs(const std::string &app_name,
-							  const std::string& app_read_only_data_dir)
-{
-	// Allow override so test apps can read newview directory
-	if (!app_read_only_data_dir.empty())
-	{
-		mAppRODataDir = app_read_only_data_dir;
-		mSkinBaseDir = add(mAppRODataDir, "skins");
-	}
-	mAppName = app_name;
-	mOSUserAppDir = add(mOSUserDir, app_name);
-
-	int res = LLFile::mkdir(mOSUserAppDir);
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
-		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
-		mOSUserAppDir = mOSUserDir;
-	}
-	//dumpCurrentDirectories();
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
-	}
-
-	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
-	if (res == -1)
-	{
-		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
-	}
-
-	mCAFile = getExpandedFilename( LL_PATH_EXECUTABLE, "ca-bundle.crt" );
-}
-
-U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string &mask)
-{
-	HANDLE count_search_h;
-	U32 file_count;
-
-	file_count = 0;
-
-	WIN32_FIND_DATA FileData;
-
-	llutf16string pathname = utf8str_to_utf16str(dirname);
-	pathname += utf8str_to_utf16str(mask);
-	
-	if ((count_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)   
-	{
-		file_count++;
-
-		while (FindNextFile(count_search_h, &FileData))
-		{
-			file_count++;
-		}
-		   
-		FindClose(count_search_h);
-	}
-
-	return (file_count);
-}
-
-std::string LLDir_Win32::getCurPath()
-{
-	WCHAR w_str[MAX_PATH];
-	GetCurrentDirectory(MAX_PATH, w_str);
-
-	return utf16str_to_utf8str(llutf16string(w_str));
-}
-
-
-bool LLDir_Win32::fileExists(const std::string &filename) const
-{
-	llstat stat_data;
-	// Check the age of the file
-	// Now, we see if the files we've gathered are recent...
-	int res = LLFile::stat(filename, &stat_data);
-	if (!res)
-	{
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-}
-
-
-/*virtual*/ std::string LLDir_Win32::getLLPluginLauncher()
-{
-	return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
-		"SLPlugin.exe";
-}
-
-/*virtual*/ std::string LLDir_Win32::getLLPluginFilename(std::string base_name)
-{
-	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
-		base_name + ".dll";
-}
-
-
-#if 0
-// Utility function to get version number of a DLL
-
-#define PACKVERSION(major,minor) MAKELONG(minor,major)
-
-DWORD GetDllVersion(LPCTSTR lpszDllName)
-{
-
-    HINSTANCE hinstDll;
-    DWORD dwVersion = 0;
-
-    hinstDll = LoadLibrary(lpszDllName);	/* Flawfinder: ignore */ 
-	
-    if(hinstDll)
-    {
-        DLLGETVERSIONPROC pDllGetVersion;
-
-        pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");
-
-/*Because some DLLs might not implement this function, you
-  must test for it explicitly. Depending on the particular 
-  DLL, the lack of a DllGetVersion function can be a useful
-  indicator of the version.
-*/
-        if(pDllGetVersion)
-        {
-            DLLVERSIONINFO dvi;
-            HRESULT hr;
-
-            ZeroMemory(&dvi, sizeof(dvi));
-            dvi.cbSize = sizeof(dvi);
-
-            hr = (*pDllGetVersion)(&dvi);
-
-            if(SUCCEEDED(hr))
-            {
-                dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
-            }
-        }
-        
-        FreeLibrary(hinstDll);
-    }
-    return dwVersion;
-}
-#endif
-
-#endif
-
-
diff --git a/indra/llcache/lldir_win32.h b/indra/llcache/lldir_win32.h
deleted file mode 100644
index 450efaf9da..0000000000
--- a/indra/llcache/lldir_win32.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/** 
- * @file lldir_win32.h
- * @brief Definition of directory utilities class for windows
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#if !LL_WINDOWS
-#error This header must not be included when compiling for any target other than Windows. Consider including lldir.h instead.
-#endif // !LL_WINDOWS
-
-#ifndef LL_LLDIR_WIN32_H
-#define LL_LLDIR_WIN32_H
-
-#include "lldir.h"
-
-class LLDir_Win32 : public LLDir
-{
-public:
-	LLDir_Win32();
-	virtual ~LLDir_Win32();
-
-	/*virtual*/ void initAppDirs(const std::string &app_name,
-		const std::string& app_read_only_data_dir);
-
-	/*virtual*/ std::string getCurPath();
-	/*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask);
-	/*virtual*/ bool fileExists(const std::string &filename) const;
-
-	/*virtual*/ std::string getLLPluginLauncher();
-	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
-
-private:
-	void* mDirSearch_h;
-	llutf16string mCurrentDir;
-};
-
-#endif // LL_LLDIR_WIN32_H
-
-
diff --git a/indra/llcache/lldirguard.h b/indra/llcache/lldirguard.h
deleted file mode 100644
index 37b9e9b83e..0000000000
--- a/indra/llcache/lldirguard.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/** 
- * @file lldirguard.h
- * @brief Protect working directory from being changed in scope.
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_DIRGUARD_H
-#define LL_DIRGUARD_H
-
-#include "linden_common.h"
-#include "llerror.h"
-
-#if LL_WINDOWS
-class LLDirectoryGuard
-{
-public:
-	LLDirectoryGuard()
-	{
-		mOrigDirLen = GetCurrentDirectory(MAX_PATH, mOrigDir);
-	}
-
-	~LLDirectoryGuard()
-	{
-		mFinalDirLen = GetCurrentDirectory(MAX_PATH, mFinalDir);
-		if ((mOrigDirLen!=mFinalDirLen) ||
-			(wcsncmp(mOrigDir,mFinalDir,mOrigDirLen)!=0))
-		{
-			// Dir has changed
-			std::string mOrigDirUtf8 = utf16str_to_utf8str(llutf16string(mOrigDir));
-			std::string mFinalDirUtf8 = utf16str_to_utf8str(llutf16string(mFinalDir));
-			LL_INFOS() << "Resetting working dir from " << mFinalDirUtf8 << " to " << mOrigDirUtf8 << LL_ENDL;
-			SetCurrentDirectory(mOrigDir);
-		}
-	}
-
-private:
-	TCHAR mOrigDir[MAX_PATH];
-	DWORD mOrigDirLen;
-	TCHAR mFinalDir[MAX_PATH];
-	DWORD mFinalDirLen;
-};
-#else // No-op outside Windows.
-class LLDirectoryGuard
-{
-public:
-	LLDirectoryGuard() {}
-	~LLDirectoryGuard() {}
-};
-#endif 
-
-
-#endif
diff --git a/indra/llcache/lldiriterator.cpp b/indra/llcache/lldiriterator.cpp
deleted file mode 100644
index 3eb64e69d9..0000000000
--- a/indra/llcache/lldiriterator.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * @file lldiriterator.cpp
- * @brief Iterator through directory entries matching the search pattern.
- *
- * $LicenseInfo:firstyear=2010&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "lldiriterator.h"
-
-#include "fix_macros.h"
-#include <boost/filesystem.hpp>
-#include <boost/regex.hpp>
-
-namespace fs = boost::filesystem;
-
-static std::string glob_to_regex(const std::string& glob);
-
-class LLDirIterator::Impl
-{
-public:
-	Impl(const std::string &dirname, const std::string &mask);
-	~Impl();
-
-	bool next(std::string &fname);
-
-private:
-	boost::regex			mFilterExp;
-	fs::directory_iterator	mIter;
-	bool					mIsValid;
-};
-
-LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask)
-	: mIsValid(false)
-{
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
-	fs::path dir_path(utf8str_to_utf16str(dirname));
-#else
-	fs::path dir_path(dirname);
-#endif
-
-	bool is_dir = false;
-
-	// Check if path is a directory.
-	try
-	{
-		is_dir = fs::is_directory(dir_path);
-	}
-	catch (const fs::filesystem_error& e)
-	{
-		LL_WARNS() << e.what() << LL_ENDL;
-		return;
-	}
-
-	if (!is_dir)
-	{
-		LL_WARNS() << "Invalid path: \"" << dir_path.string() << "\"" << LL_ENDL;
-		return;
-	}
-
-	// Initialize the directory iterator for the given path.
-	try
-	{
-		mIter = fs::directory_iterator(dir_path);
-	}
-	catch (const fs::filesystem_error& e)
-	{
-		LL_WARNS() << e.what() << LL_ENDL;
-		return;
-	}
-
-	// Convert the glob mask to a regular expression
-	std::string exp = glob_to_regex(mask);
-
-	// Initialize boost::regex with the expression converted from
-	// the glob mask.
-	// An exception is thrown if the expression is not valid.
-	try
-	{
-		mFilterExp.assign(exp);
-	}
-	catch (boost::regex_error& e)
-	{
-		LL_WARNS() << "\"" << exp << "\" is not a valid regular expression: "
-				<< e.what() << LL_ENDL;
-		return;
-	}
-
-	mIsValid = true;
-}
-
-LLDirIterator::Impl::~Impl()
-{
-}
-
-bool LLDirIterator::Impl::next(std::string &fname)
-{
-	fname = "";
-
-	if (!mIsValid)
-	{
-		LL_WARNS() << "The iterator is not correctly initialized." << LL_ENDL;
-		return false;
-	}
-
-	fs::directory_iterator end_itr; // default construction yields past-the-end
-	bool found = false;
-
-	// Check if path is a directory.
-	try
-	{
-		while (mIter != end_itr && !found)
-		{
-			boost::smatch match;
-			std::string name = mIter->path().filename().string();
-			found = boost::regex_match(name, match, mFilterExp);
-			if (found)
-			{
-				fname = name;
-			}
-
-			++mIter;
-		}
-	}
-	catch (const fs::filesystem_error& e)
-	{
-		LL_WARNS() << e.what() << LL_ENDL;
-	}
-
-	return found;
-}
-
-/**
-Converts the incoming glob into a regex. This involves
-converting incoming glob expressions to regex equivilents and
-at the same time, escaping any regex meaningful characters which
-do not have glob meaning, i.e.
-            .()+|^$ 
-in the input.
-*/
-std::string glob_to_regex(const std::string& glob)
-{
-	std::string regex;
-	regex.reserve(glob.size()<<1);
-	S32 braces = 0;
-	bool escaped = false;
-	bool square_brace_open = false;
-
-	for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i)
-	{
-		char c = *i;
-
-		switch (c)
-		{
-			case '*':
-				if (glob.begin() == i)
-				{
-					regex+="[^.].*";
-				}
-				else
-				{
-					regex+= escaped ? "*" : ".*";
-				}
-				break;
-			case '?':
-				regex+= escaped ? '?' : '.';
-				break;
-			case '{':
-				braces++;
-				regex+='(';
-				break;
-			case '}':
-				if (!braces)
-				{
-					LL_ERRS() << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << LL_ENDL;
-				}
-
-				regex+=')';
-				braces--;
-				break;
-			case ',':
-				regex+= braces ? '|' : c;
-				break;
-			case '!':
-				regex+= square_brace_open ? '^' : c;
-				break;
-			case '.': // This collection have different regex meaning
-			case '^': // and so need escaping.
-			case '(': 
-			case ')':
-			case '+':
-			case '|':
-			case '$':
-				regex += '\\'; 
-			default:
-				regex += c;
-				break;
-		}
-
-		escaped = ('\\' == c);
-		square_brace_open = ('[' == c);
-	}
-
-	if (braces)
-	{
-		LL_ERRS() << "glob_to_regex: Unterminated brace expression: " << glob << LL_ENDL;
-	}
-
-	return regex;
-}
-
-LLDirIterator::LLDirIterator(const std::string &dirname, const std::string &mask)
-{
-	mImpl = new Impl(dirname, mask);
-}
-
-LLDirIterator::~LLDirIterator()
-{
-	delete mImpl;
-}
-
-bool LLDirIterator::next(std::string &fname)
-{
-	return mImpl->next(fname);
-}
diff --git a/indra/llcache/lldiriterator.h b/indra/llcache/lldiriterator.h
deleted file mode 100644
index 0b48be41b3..0000000000
--- a/indra/llcache/lldiriterator.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @file lldiriterator.h
- * @brief Iterator through directory entries matching the search pattern.
- *
- * $LicenseInfo:firstyear=2010&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDIRITERATOR_H
-#define LL_LLDIRITERATOR_H
-
-#include "linden_common.h"
-
-/**
- * Class LLDirIterator
- *
- * Iterates through directory entries matching the search pattern.
- */
-class LLDirIterator
-{
-public:
-	/**
-	 * Constructs LLDirIterator object to search for glob pattern
-	 * matches in a directory.
-	 *
-	 * @param dirname - name of a directory to search in.
-	 * @param mask - search pattern, a glob expression
-	 *
-	 * Wildcards supported in glob expressions:
-	 * --------------------------------------------------------------
-	 * | Wildcard 	| Matches										|
-	 * --------------------------------------------------------------
-	 * | 	* 		|zero or more characters						|
-	 * | 	?		|exactly one character							|
-	 * | [abcde]	|exactly one character listed					|
-	 * | [a-e]		|exactly one character in the given range		|
-	 * | [!abcde]	|any character that is not listed				|
-	 * | [!a-e]		|any character that is not in the given range	|
-	 * | {abc,xyz}	|exactly one entire word in the options given	|
-	 * --------------------------------------------------------------
-	 */
-	LLDirIterator(const std::string &dirname, const std::string &mask);
-
-	~LLDirIterator();
-
-	/**
-	 * Searches for the next directory entry matching the glob mask
-	 * specified upon iterator construction.
-	 * Returns true if a match is found, sets fname
-	 * parameter to the name of the matched directory entry and
-	 * increments the iterator position.
-	 *
-	 * Typical usage:
-	 * <code>
-	 * LLDirIterator iter(directory, pattern);
-	 * if ( iter.next(scanResult) )
-	 * </code>
-	 *
-	 * @param fname - name of the matched directory entry.
-	 * @return true if a match is found, false otherwise.
-	 */
-	bool next(std::string &fname);
-
-protected:
-	class Impl;
-	Impl* mImpl;
-};
-
-#endif //LL_LLDIRITERATOR_H
diff --git a/indra/llcache/lldiskcache.cpp b/indra/llcache/lldiskcache.cpp
deleted file mode 100644
index af93049e07..0000000000
--- a/indra/llcache/lldiskcache.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-/** 
- * @file lldiskcache.cpp
- * @brief Implementation of virtual file
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lldiskcache.h"
-
-#include "llerror.h"
-#include "llthread.h"
-#include "lltimer.h"
-#include "llfasttimer.h"
-#include "llmemory.h"
-
-#include <fstream>
-#include "lldir.h"
-
-const S32 LLDiskCache::READ			= 0x00000001;
-const S32 LLDiskCache::WRITE		= 0x00000002;
-const S32 LLDiskCache::READ_WRITE	= 0x00000003;  // LLDiskCache::READ & LLDiskCache::WRITE
-const S32 LLDiskCache::APPEND		= 0x00000006;  // 0x00000004 & LLDiskCache::WRITE
-
-static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait");
-
-LLDiskCache::LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
-{
-	mFileType =	file_type;
-	mFileID = file_id;
-	mPosition = 0;
-    mBytesRead = 0;
-    mReadComplete = FALSE;
-	mMode = mode;
-}
-
-LLDiskCache::~LLDiskCache()
-{
-}
-
-const std::string assetTypeToString(LLAssetType::EType at)
-{
-    /**
-     * Make use of the C++17 (or is it 14) feature that allows
-     * for inline initialization of an std::map<>
-     */
-    typedef std::map<LLAssetType::EType, std::string> asset_type_to_name_t;
-    asset_type_to_name_t asset_type_to_name =
-    {
-        { LLAssetType::AT_TEXTURE, "TEXTURE" },
-        { LLAssetType::AT_SOUND, "SOUND" },
-        { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" },
-        { LLAssetType::AT_LANDMARK, "LANDMARK" },
-        { LLAssetType::AT_SCRIPT, "SCRIPT" },
-        { LLAssetType::AT_CLOTHING, "CLOTHING" },
-        { LLAssetType::AT_OBJECT, "OBJECT" },
-        { LLAssetType::AT_NOTECARD, "NOTECARD" },
-        { LLAssetType::AT_CATEGORY, "CATEGORY" },
-        { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" },
-        { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" },
-        { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" },
-        { LLAssetType::AT_BODYPART, "BODYPART" },
-        { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" },
-        { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" },
-        { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" },
-        { LLAssetType::AT_ANIMATION, "ANIMATION" },
-        { LLAssetType::AT_GESTURE, "GESTURE" },
-        { LLAssetType::AT_SIMSTATE, "SIMSTATE" },
-        { LLAssetType::AT_LINK, "LINK" },
-        { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" },
-        { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" },
-        { LLAssetType::AT_WIDGET, "WIDGET" },
-        { LLAssetType::AT_PERSON, "PERSON" },
-        { LLAssetType::AT_MESH, "MESH" },
-        { LLAssetType::AT_SETTINGS, "SETTINGS" },
-        { LLAssetType::AT_UNKNOWN, "UNKNOWN" }
-    };
-
-    asset_type_to_name_t::iterator iter = asset_type_to_name.find(at);
-    if (iter != asset_type_to_name.end())
-    {
-        return iter->second;
-    }
-
-    return std::string("UNKNOWN");
-}
-
-const std::string idToFilepath(const std::string id, LLAssetType::EType at)
-{
-    /**
-     * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of
-     * course,  will be greatly expanded upon
-     */
-    std::ostringstream ss;
-    ss << "00cache_";
-    ss << id;
-    ss << "_";
-    ss << assetTypeToString(at);
-    ss << ".txt";
-
-    const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str());
-
-    return filepath;
-}
-
-// static
-bool LLDiskCache::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
-{
-	std::string id_str;
-	file_id.toString(id_str);
-	const std::string filename = idToFilepath(id_str, file_type);
-
-	std::ifstream file(filename, std::ios::binary);
-	if (file.is_open())
-	{
-		file.seekg(0, std::ios::end);
-		return file.tellg() > 0;
-	}
-	return false;
-}
-
-// static
-bool LLDiskCache::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
-{
-    std::string id_str;
-    file_id.toString(id_str);
-    const std::string filename = idToFilepath(id_str, file_type);
-    
-    std::remove(filename.c_str());
-
-    return true;
-}
-
-// static
-bool LLDiskCache::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type,
-                         const LLUUID &new_file_id, const LLAssetType::EType new_file_type)
-{
-    std::string old_id_str;
-    old_file_id.toString(old_id_str);
-    const std::string old_filename = idToFilepath(old_id_str, old_file_type);
-
-    std::string new_id_str;
-    new_file_id.toString(new_id_str);
-    const std::string new_filename = idToFilepath(new_id_str, new_file_type);
-
-    if (std::rename(old_filename.c_str(), new_filename.c_str()))
-    {
-        // We would like to return FALSE here indicating the operation
-        // failed but the original code does not and doing so seems to
-        // break a lot of things so we go with the flow... 
-        //return FALSE;
-    }
-
-    return TRUE;
-}
-
-// static
-S32 LLDiskCache::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type)
-{
-    std::string id_str;
-    file_id.toString(id_str);
-    const std::string filename = idToFilepath(id_str, file_type);
-
-    S32 file_size = 0;
-    std::ifstream file(filename, std::ios::binary);
-    if (file.is_open())
-    {
-        file.seekg(0, std::ios::end);
-        file_size = file.tellg();
-    }
-
-    return file_size;
-}
-
-BOOL LLDiskCache::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
-{
-	BOOL success = TRUE;
-
-    mReadComplete = FALSE;
-
-    std::string id;
-    mFileID.toString(id);
-    const std::string filename = idToFilepath(id, mFileType);
-
-    std::ifstream file(filename, std::ios::binary);
-    if (file.is_open())
-    {
-        file.seekg(mPosition, std::ios::beg);
-
-        file.read((char*)buffer, bytes);
-
-        if (file)
-        {
-            mBytesRead = bytes;
-        }
-        else
-        {
-            mBytesRead = file.gcount();
-        }
-
-        file.close();
-
-        mPosition += mBytesRead;
-        if (!mBytesRead)
-        {
-            success = FALSE;
-        }
-
-        mReadComplete = TRUE;
-    }
-
-    return success;
-}
-
-BOOL LLDiskCache::isReadComplete()
-{
-    if (mReadComplete)
-    {
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-S32 LLDiskCache::getLastBytesRead()
-{
-	return mBytesRead;
-}
-
-BOOL LLDiskCache::eof()
-{
-	return mPosition >= getSize();
-}
-
-BOOL LLDiskCache::write(const U8 *buffer, S32 bytes)
-{
-    std::string id_str;
-    mFileID.toString(id_str);
-    const std::string filename = idToFilepath(id_str, mFileType);
-
-    BOOL success = FALSE;
-
-    if (mMode == APPEND)
-    {
-        std::ofstream ofs(filename, std::ios::app | std::ios::binary);
-        if (ofs)
-        {
-            ofs.write((const char*)buffer, bytes);
-
-            success = TRUE;
-        }
-    }
-    else
-    {
-        std::ofstream ofs(filename, std::ios::binary);
-        if (ofs)
-        {
-            ofs.write((const char*)buffer, bytes);
-
-            mPosition += bytes;
-
-            success = TRUE;
-        }
-    }
-
-    return success;
-}
-
-//static
-BOOL LLDiskCache::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type)
-{
-	LLDiskCache file(uuid, type, LLDiskCache::WRITE);
-	file.setMaxSize(bytes);
-	return file.write(buffer, bytes);
-}
-
-BOOL LLDiskCache::seek(S32 offset, S32 origin)
-{
-	if (-1 == origin)
-	{
-		origin = mPosition;
-	}
-
-	S32 new_pos = origin + offset;
-
-	S32 size = getSize();
-
-	if (new_pos > size)
-	{
-		LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL;
-
-		mPosition = size;
-		return FALSE;
-	}
-	else if (new_pos < 0)
-	{
-		LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL;
-
-		mPosition = 0;
-		return FALSE;
-	}
-
-	mPosition = new_pos;
-	return TRUE;
-}
-
-S32 LLDiskCache::tell() const
-{
-	return mPosition;
-}
-
-S32 LLDiskCache::getSize()
-{
-    return LLDiskCache::getFileSize(mFileID, mFileType);
-}
-
-S32 LLDiskCache::getMaxSize()
-{
-    // offer up a huge size since we don't care what the max is 
-    return INT_MAX;
-}
-
-BOOL LLDiskCache::setMaxSize(S32 size)
-{
-    // we don't care what the max size is so we do nothing
-    // and return true to indicate all was okay
-    return TRUE;
-}
-
-BOOL LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
-{
-    LLDiskCache::renameFile(mFileID, mFileType, new_id, new_type);
-
-    mFileID = new_id;
-    mFileType = new_type;
-
-    return TRUE;
-}
-
-BOOL LLDiskCache::remove()
-{
-    LLDiskCache::removeFile(mFileID, mFileType);
-
-    return TRUE;
-}
-
-// static
-void LLDiskCache::initClass()
-{
-}
-
-// static
-void LLDiskCache::cleanupClass()
-{
-}
-
-bool LLDiskCache::isLocked()
-{
-    // I don't think we care about this test since there is no locking
-    // TODO: remove this function and calling sites?
-    return FALSE;
-}
-
-void LLDiskCache::waitForLock()
-{
-    // TODO: remove this function and calling sites?
-}
diff --git a/indra/llcache/lldiskcache.h b/indra/llcache/lldiskcache.h
deleted file mode 100644
index 7ad06a8689..0000000000
--- a/indra/llcache/lldiskcache.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/** 
- * @file lldiskcacke.h
- * @brief Definition of virtual file
- *
- * $LicenseInfo:firstyear=2002&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDISKCACHE_H
-#define LL_LLDISKCACHE_H
-
-#include "lluuid.h"
-#include "llassettype.h"
-
-class LLDiskCache
-{
-public:
-	LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLDiskCache::READ);
-	~LLDiskCache();
-
-	BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f);	/* Flawfinder: ignore */ 
-	BOOL isReadComplete();
-	S32  getLastBytesRead();
-	BOOL eof();
-
-	BOOL write(const U8 *buffer, S32 bytes);
-	static BOOL writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type);
-	BOOL seek(S32 offset, S32 origin = -1);
-	S32  tell() const;
-
-	S32 getSize();
-	S32 getMaxSize();
-	BOOL setMaxSize(S32 size);
-	BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type);
-	BOOL remove();
-
-	bool isLocked();
-	void waitForLock();
-
-    static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type);
-    static bool removeFile(const LLUUID &file_id, const LLAssetType::EType file_type);
-    static bool renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type,
-                           const LLUUID &new_file_id, const LLAssetType::EType new_file_type);
-    static S32 getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type);
-	
-	static void initClass();
-	static void cleanupClass();
-
-public:
-	static const S32 READ;
-	static const S32 WRITE;
-	static const S32 READ_WRITE;
-	static const S32 APPEND;
-	
-protected:
-	LLAssetType::EType mFileType;
-    BOOL    mReadComplete;
-	LLUUID	mFileID;
-	S32		mPosition;
-	S32		mMode;
-	S32		mBytesRead;
-};
-
-#endif  // LL_LLDISKCACHE_H
diff --git a/indra/llcache/lllfsthread.cpp b/indra/llcache/lllfsthread.cpp
deleted file mode 100644
index be8e83a56f..0000000000
--- a/indra/llcache/lllfsthread.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/** 
- * @file lllfsthread.cpp
- * @brief LLLFSThread base class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "lllfsthread.h"
-#include "llstl.h"
-#include "llapr.h"
-
-//============================================================================
-
-/*static*/ LLLFSThread* LLLFSThread::sLocal = NULL;
-
-//============================================================================
-// Run on MAIN thread
-//static
-void LLLFSThread::initClass(bool local_is_threaded)
-{
-	llassert(sLocal == NULL);
-	sLocal = new LLLFSThread(local_is_threaded);
-}
-
-//static
-S32 LLLFSThread::updateClass(U32 ms_elapsed)
-{
-	sLocal->update((F32)ms_elapsed);
-	return sLocal->getPending();
-}
-
-//static
-void LLLFSThread::cleanupClass()
-{
-	llassert(sLocal != NULL);
-	sLocal->setQuitting();
-	while (sLocal->getPending())
-	{
-		sLocal->update(0);
-	}
-	delete sLocal;
-	sLocal = NULL;
-}
-
-//----------------------------------------------------------------------------
-
-LLLFSThread::LLLFSThread(bool threaded) :
-	LLQueuedThread("LFS", threaded),
-	mPriorityCounter(PRIORITY_LOWBITS)
-{
-	if(!mLocalAPRFilePoolp)
-	{
-		mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
-	}
-}
-
-LLLFSThread::~LLLFSThread()
-{
-	// mLocalAPRFilePoolp cleanup in LLThread
-	// ~LLQueuedThread() will be called here
-}
-
-//----------------------------------------------------------------------------
-
-LLLFSThread::handle_t LLLFSThread::read(const std::string& filename,	/* Flawfinder: ignore */ 
-										U8* buffer, S32 offset, S32 numbytes,
-										Responder* responder, U32 priority)
-{
-	handle_t handle = generateHandle();
-
-	if (priority == 0) priority = PRIORITY_NORMAL | priorityCounter();
-	else if (priority < PRIORITY_LOW) priority |= PRIORITY_LOW; // All reads are at least PRIORITY_LOW
-
-	Request* req = new Request(this, handle, priority,
-							   FILE_READ, filename,
-							   buffer, offset, numbytes,
-							   responder);
-
-	bool res = addRequest(req);
-	if (!res)
-	{
-		LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL;
-	}
-
-	return handle;
-}
-
-LLLFSThread::handle_t LLLFSThread::write(const std::string& filename,
-										 U8* buffer, S32 offset, S32 numbytes,
-										 Responder* responder, U32 priority)
-{
-	handle_t handle = generateHandle();
-
-	if (priority == 0) priority = PRIORITY_LOW | priorityCounter();
-	
-	Request* req = new Request(this, handle, priority,
-							   FILE_WRITE, filename,
-							   buffer, offset, numbytes,
-							   responder);
-
-	bool res = addRequest(req);
-	if (!res)
-	{
-		LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL;
-	}
-	
-	return handle;
-}
-
-//============================================================================
-
-LLLFSThread::Request::Request(LLLFSThread* thread,
-							  handle_t handle, U32 priority,
-							  operation_t op, const std::string& filename,
-							  U8* buffer, S32 offset, S32 numbytes,
-							  Responder* responder) :
-	QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
-	mThread(thread),
-	mOperation(op),
-	mFileName(filename),
-	mBuffer(buffer),
-	mOffset(offset),
-	mBytes(numbytes),
-	mBytesRead(0),
-	mResponder(responder)
-{
-	if (numbytes <= 0)
-	{
-		LL_WARNS() << "LLLFSThread: Request with numbytes = " << numbytes << LL_ENDL;
-	}
-}
-
-LLLFSThread::Request::~Request()
-{
-}
-
-// virtual, called from own thread
-void LLLFSThread::Request::finishRequest(bool completed)
-{
-	if (mResponder.notNull())
-	{
-		mResponder->completed(completed ? mBytesRead : 0);
-		mResponder = NULL;
-	}
-}
-
-void LLLFSThread::Request::deleteRequest()
-{
-	if (getStatus() == STATUS_QUEUED)
-	{
-		LL_ERRS() << "Attempt to delete a queued LLLFSThread::Request!" << LL_ENDL;
-	}	
-	if (mResponder.notNull())
-	{
-		mResponder->completed(0);
-		mResponder = NULL;
-	}
-	LLQueuedThread::QueuedRequest::deleteRequest();
-}
-
-bool LLLFSThread::Request::processRequest()
-{
-	bool complete = false;
-	if (mOperation ==  FILE_READ)
-	{
-		llassert(mOffset >= 0);
-		LLAPRFile infile ; // auto-closes
-		infile.open(mFileName, LL_APR_RB, mThread->getLocalAPRFilePool());
-		if (!infile.getFileHandle())
-		{
-			LL_WARNS() << "LLLFS: Unable to read file: " << mFileName << LL_ENDL;
-			mBytesRead = 0; // fail
-			return true;
-		}
-		S32 off;
-		if (mOffset < 0)
-			off = infile.seek(APR_END, 0);
-		else
-			off = infile.seek(APR_SET, mOffset);
-		llassert_always(off >= 0);
-		mBytesRead = infile.read(mBuffer, mBytes );
-		complete = true;
-// 		LL_INFOS() << "LLLFSThread::READ:" << mFileName << " Bytes: " << mBytesRead << LL_ENDL;
-	}
-	else if (mOperation ==  FILE_WRITE)
-	{
-		apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
-		if (mOffset < 0)
-			flags |= APR_APPEND;
-		LLAPRFile outfile ; // auto-closes
-		outfile.open(mFileName, flags, mThread->getLocalAPRFilePool());
-		if (!outfile.getFileHandle())
-		{
-			LL_WARNS() << "LLLFS: Unable to write file: " << mFileName << LL_ENDL;
-			mBytesRead = 0; // fail
-			return true;
-		}
-		if (mOffset >= 0)
-		{
-			S32 seek = outfile.seek(APR_SET, mOffset);
-			if (seek < 0)
-			{
-				LL_WARNS() << "LLLFS: Unable to write file (seek failed): " << mFileName << LL_ENDL;
-				mBytesRead = 0; // fail
-				return true;
-			}
-		}
-		mBytesRead = outfile.write(mBuffer, mBytes );
-		complete = true;
-// 		LL_INFOS() << "LLLFSThread::WRITE:" << mFileName << " Bytes: " << mBytesRead << "/" << mBytes << " Offset:" << mOffset << LL_ENDL;
-	}
-	else
-	{
-		LL_ERRS() << "LLLFSThread::unknown operation: " << (S32)mOperation << LL_ENDL;
-	}
-	return complete;
-}
-
-//============================================================================
-
-LLLFSThread::Responder::~Responder()
-{
-}
-
-//============================================================================
diff --git a/indra/llcache/lllfsthread.h b/indra/llcache/lllfsthread.h
deleted file mode 100644
index 58f658f7ba..0000000000
--- a/indra/llcache/lllfsthread.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/** 
- * @file lllfsthread.h
- * @brief LLLFSThread base class
- *
- * $LicenseInfo:firstyear=2000&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLLFSTHREAD_H
-#define LL_LLLFSTHREAD_H
-
-#include <queue>
-#include <string>
-#include <map>
-#include <set>
-
-#include "llpointer.h"
-#include "llqueuedthread.h"
-
-//============================================================================
-// Threaded Local File System
-//============================================================================
-
-class LLLFSThread : public LLQueuedThread
-{
-	//------------------------------------------------------------------------
-public:
-	enum operation_t {
-		FILE_READ,
-		FILE_WRITE,
-		FILE_RENAME,
-		FILE_REMOVE
-	};
-
-	//------------------------------------------------------------------------
-public:
-
-	class Responder : public LLThreadSafeRefCount
-	{
-	protected:
-		~Responder();
-	public:
-		virtual void completed(S32 bytes) = 0;
-	};
-
-	class Request : public QueuedRequest
-	{
-	protected:
-		virtual ~Request(); // use deleteRequest()
-		
-	public:
-		Request(LLLFSThread* thread,
-				handle_t handle, U32 priority, 
-				operation_t op, const std::string& filename,
-				U8* buffer, S32 offset, S32 numbytes,
-				Responder* responder);
-
-		S32 getBytes()
-		{
-			return mBytes;
-		}
-		S32 getBytesRead()
-		{
-			return mBytesRead;
-		}
-		S32 getOperation()
-		{
-			return mOperation;
-		}
-		U8* getBuffer()
-		{
-			return mBuffer;
-		}
-		const std::string& getFilename()
-		{
-			return mFileName;
-		}
-		
-		/*virtual*/ bool processRequest();
-		/*virtual*/ void finishRequest(bool completed);
-		/*virtual*/ void deleteRequest();
-		
-	private:
-		LLLFSThread* mThread;
-		operation_t mOperation;
-		
-		std::string mFileName;
-		
-		U8* mBuffer;	// dest for reads, source for writes, new UUID for rename
-		S32 mOffset;	// offset into file, -1 = append (WRITE only)
-		S32 mBytes;		// bytes to read from file, -1 = all
-		S32 mBytesRead;	// bytes read from file
-
-		LLPointer<Responder> mResponder;
-	};
-
-	//------------------------------------------------------------------------
-public:
-	LLLFSThread(bool threaded = TRUE);
-	~LLLFSThread();	
-
-	// Return a Request handle
-	handle_t read(const std::string& filename,	/* Flawfinder: ignore */ 
-				  U8* buffer, S32 offset, S32 numbytes,
-				  Responder* responder, U32 pri=0);
-	handle_t write(const std::string& filename,
-				   U8* buffer, S32 offset, S32 numbytes,
-				   Responder* responder, U32 pri=0);
-	
-	// Misc
-	U32 priorityCounter() { return mPriorityCounter-- & PRIORITY_LOWBITS; } // Use to order IO operations
-	
-	// static initializers
-	static void initClass(bool local_is_threaded = TRUE); // Setup sLocal
-	static S32 updateClass(U32 ms_elapsed);
-	static void cleanupClass();		// Delete sLocal
-
-	
-private:
-	U32 mPriorityCounter;
-	
-public:
-	static LLLFSThread* sLocal;		// Default local file thread
-};
-
-//============================================================================
-
-
-#endif // LL_LLLFSTHREAD_H
diff --git a/indra/llcache/tests/lldir_test.cpp b/indra/llcache/tests/lldir_test.cpp
deleted file mode 100644
index 3cff622a4b..0000000000
--- a/indra/llcache/tests/lldir_test.cpp
+++ /dev/null
@@ -1,767 +0,0 @@
-/**
- * @file lldir_test.cpp
- * @date 2008-05
- * @brief LLDir test cases.
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llstring.h"
-#include "tests/StringVec.h"
-#include "../lldir.h"
-#include "../lldiriterator.h"
-
-#include "../test/lltut.h"
-#include "stringize.h"
-#include <boost/foreach.hpp>
-#include <boost/assign/list_of.hpp>
-
-using boost::assign::list_of;
-
-// We use ensure_equals(..., vec(list_of(...))) not because it's functionally
-// required, but because ensure_equals() knows how to format a StringVec.
-// Turns out that when ensure_equals() displays a test failure with just
-// list_of("string")("another"), you see 'stringanother' vs. '("string",
-// "another")'.
-StringVec vec(const StringVec& v)
-{
-    return v;
-}
-
-// For some tests, use a dummy LLDir that uses memory data instead of touching
-// the filesystem
-struct LLDir_Dummy: public LLDir
-{
-    /*----------------------------- LLDir API ------------------------------*/
-    LLDir_Dummy()
-    {
-        // Initialize important LLDir data members based on the filesystem
-        // data below.
-        mDirDelimiter = "/";
-        mExecutableDir = "install";
-        mExecutableFilename = "test";
-        mExecutablePathAndName = add(mExecutableDir, mExecutableFilename);
-        mWorkingDir = mExecutableDir;
-        mAppRODataDir = "install";
-        mSkinBaseDir = add(mAppRODataDir, "skins");
-        mOSUserDir = "user";
-        mOSUserAppDir = mOSUserDir;
-        mLindenUserDir = "";
-
-        // Make the dummy filesystem look more or less like what we expect in
-        // the real one.
-        static const char* preload[] =
-        {
-            // We group these fixture-data pathnames by basename, rather than
-            // sorting by full path as you might expect, because the outcome
-            // of each test strongly depends on which skins/languages provide
-            // a given basename.
-            "install/skins/default/colors.xml",
-            "install/skins/steam/colors.xml",
-            "user/skins/default/colors.xml",
-            "user/skins/steam/colors.xml",
-
-            "install/skins/default/xui/en/strings.xml",
-            "install/skins/default/xui/fr/strings.xml",
-            "install/skins/steam/xui/en/strings.xml",
-            "install/skins/steam/xui/fr/strings.xml",
-            "user/skins/default/xui/en/strings.xml",
-            "user/skins/default/xui/fr/strings.xml",
-            "user/skins/steam/xui/en/strings.xml",
-            "user/skins/steam/xui/fr/strings.xml",
-
-            "install/skins/default/xui/en/floater.xml",
-            "install/skins/default/xui/fr/floater.xml",
-            "user/skins/default/xui/fr/floater.xml",
-
-            "install/skins/default/xui/en/newfile.xml",
-            "install/skins/default/xui/fr/newfile.xml",
-            "user/skins/default/xui/en/newfile.xml",
-
-            "install/skins/default/html/en-us/welcome.html",
-            "install/skins/default/html/fr/welcome.html",
-
-            "install/skins/default/textures/only_default.jpeg",
-            "install/skins/steam/textures/only_steam.jpeg",
-            "user/skins/default/textures/only_user_default.jpeg",
-            "user/skins/steam/textures/only_user_steam.jpeg",
-
-            "install/skins/default/future/somefile.txt"
-        };
-        BOOST_FOREACH(const char* path, preload)
-        {
-            buildFilesystem(path);
-        }
-    }
-
-    virtual ~LLDir_Dummy() {}
-
-    virtual void initAppDirs(const std::string& app_name, const std::string& app_read_only_data_dir)
-    {
-        // Implement this when we write a test that needs it
-    }
-
-    virtual std::string getCurPath()
-    {
-        // Implement this when we write a test that needs it
-        return "";
-    }
-
-    virtual U32 countFilesInDir(const std::string& dirname, const std::string& mask)
-    {
-        // Implement this when we write a test that needs it
-        return 0;
-    }
-
-    virtual bool fileExists(const std::string& pathname) const
-    {
-        // Record fileExists() calls so we can check whether caching is
-        // working right. Certain LLDir calls should be able to make decisions
-        // without calling fileExists() again, having already checked existence.
-        mChecked.insert(pathname);
-        // For our simple flat set of strings, see whether the identical
-        // pathname exists in our set.
-        return (mFilesystem.find(pathname) != mFilesystem.end());
-    }
-
-    virtual std::string getLLPluginLauncher()
-    {
-        // Implement this when we write a test that needs it
-        return "";
-    }
-
-    virtual std::string getLLPluginFilename(std::string base_name)
-    {
-        // Implement this when we write a test that needs it
-        return "";
-    }
-
-    /*----------------------------- Dummy data -----------------------------*/
-    void clearFilesystem() { mFilesystem.clear(); }
-    void buildFilesystem(const std::string& path)
-    {
-        // Split the pathname on slashes, ignoring leading, trailing, doubles
-        StringVec components;
-        LLStringUtil::getTokens(path, components, "/");
-        // Ensure we have an entry representing every level of this path
-        std::string partial;
-        BOOST_FOREACH(std::string component, components)
-        {
-            append(partial, component);
-            mFilesystem.insert(partial);
-        }
-    }
-
-    void clear_checked() { mChecked.clear(); }
-    void ensure_checked(const std::string& pathname) const
-    {
-        tut::ensure(STRINGIZE(pathname << " was not checked but should have been"),
-                    mChecked.find(pathname) != mChecked.end());
-    }
-    void ensure_not_checked(const std::string& pathname) const
-    {
-        tut::ensure(STRINGIZE(pathname << " was checked but should not have been"),
-                    mChecked.find(pathname) == mChecked.end());
-    }
-
-    std::set<std::string> mFilesystem;
-    mutable std::set<std::string> mChecked;
-};
-
-namespace tut
-{
-	struct LLDirTest
-        {
-        };
-        typedef test_group<LLDirTest> LLDirTest_t;
-        typedef LLDirTest_t::object LLDirTest_object_t;
-        tut::LLDirTest_t tut_LLDirTest("LLDir");
-
-	template<> template<>
-	void LLDirTest_object_t::test<1>()
-		// getDirDelimiter
-	{
-		ensure("getDirDelimiter", !gDirUtilp->getDirDelimiter().empty());
-	}
-
-	template<> template<>
-	void LLDirTest_object_t::test<2>()
-		// getBaseFileName
-	{
-		std::string delim = gDirUtilp->getDirDelimiter();
-		std::string rawFile = "foo";
-		std::string rawFileExt = "foo.bAr";
-		std::string rawFileNullExt = "foo.";
-		std::string rawExt = ".bAr";
-		std::string rawDot = ".";
-		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
-		std::string pathExt = pathNoExt + ".eXt";
-		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
-		std::string dottedPathExt = dottedPathNoExt + ".eXt";
-
-		// foo[.bAr]
-
-		ensure_equals("getBaseFileName/r-no-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(rawFile, false),
-			      "foo");
-
-		ensure_equals("getBaseFileName/r-no-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(rawFile, true),
-			      "foo");
-
-		ensure_equals("getBaseFileName/r-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(rawFileExt, false),
-			      "foo.bAr");
-
-		ensure_equals("getBaseFileName/r-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(rawFileExt, true),
-			      "foo");
-
-		// foo.
-
-		ensure_equals("getBaseFileName/rn-no-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(rawFileNullExt, false),
-			      "foo.");
-
-		ensure_equals("getBaseFileName/rn-no-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(rawFileNullExt, true),
-			      "foo");
-
-		// .bAr
-		// interesting case - with no basename, this IS the basename, not the extension.
-
-		ensure_equals("getBaseFileName/e-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(rawExt, false),
-			      ".bAr");
-
-		ensure_equals("getBaseFileName/e-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(rawExt, true),
-			      ".bAr");
-
-		// .
-
-		ensure_equals("getBaseFileName/d/no-strip-exten",
-			      gDirUtilp->getBaseFileName(rawDot, false),
-			      ".");
-
-		ensure_equals("getBaseFileName/d/strip-exten",
-			      gDirUtilp->getBaseFileName(rawDot, true),
-			      ".");
-
-		// aa/bb/cc/dd/ee[.eXt]
-
-		ensure_equals("getBaseFileName/no-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(pathNoExt, false),
-			      "ee");
-
-		ensure_equals("getBaseFileName/no-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(pathNoExt, true),
-			      "ee");
-
-		ensure_equals("getBaseFileName/ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(pathExt, false),
-			      "ee.eXt");
-
-		ensure_equals("getBaseFileName/ext/strip-exten",
-			      gDirUtilp->getBaseFileName(pathExt, true),
-			      "ee");
-
-		// aa/bb/cc.dd/ee[.eXt]
-
-		ensure_equals("getBaseFileName/d-no-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(dottedPathNoExt, false),
-			      "ee");
-
-		ensure_equals("getBaseFileName/d-no-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(dottedPathNoExt, true),
-			      "ee");
-
-		ensure_equals("getBaseFileName/d-ext/no-strip-exten",
-			      gDirUtilp->getBaseFileName(dottedPathExt, false),
-			      "ee.eXt");
-
-		ensure_equals("getBaseFileName/d-ext/strip-exten",
-			      gDirUtilp->getBaseFileName(dottedPathExt, true),
-			      "ee");
-	}
-
-	template<> template<>
-	void LLDirTest_object_t::test<3>()
-		// getDirName
-	{
-		std::string delim = gDirUtilp->getDirDelimiter();
-		std::string rawFile = "foo";
-		std::string rawFileExt = "foo.bAr";
-		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
-		std::string pathExt = pathNoExt + ".eXt";
-		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
-		std::string dottedPathExt = dottedPathNoExt + ".eXt";
-
-		// foo[.bAr]
-
-		ensure_equals("getDirName/r-no-ext",
-			      gDirUtilp->getDirName(rawFile),
-			      "");
-
-		ensure_equals("getDirName/r-ext",
-			      gDirUtilp->getDirName(rawFileExt),
-			      "");
-
-		// aa/bb/cc/dd/ee[.eXt]
-
-		ensure_equals("getDirName/no-ext",
-			      gDirUtilp->getDirName(pathNoExt),
-			      "aa" + delim + "bb" + delim + "cc" + delim + "dd");
-
-		ensure_equals("getDirName/ext",
-			      gDirUtilp->getDirName(pathExt),
-			      "aa" + delim + "bb" + delim + "cc" + delim + "dd");
-
-		// aa/bb/cc.dd/ee[.eXt]
-
-		ensure_equals("getDirName/d-no-ext",
-			      gDirUtilp->getDirName(dottedPathNoExt),
-			      "aa" + delim + "bb" + delim + "cc.dd");
-
-		ensure_equals("getDirName/d-ext",
-			      gDirUtilp->getDirName(dottedPathExt),
-			      "aa" + delim + "bb" + delim + "cc.dd");
-	}
-
-	template<> template<>
-	void LLDirTest_object_t::test<4>()
-		// getExtension
-	{
-		std::string delim = gDirUtilp->getDirDelimiter();
-		std::string rawFile = "foo";
-		std::string rawFileExt = "foo.bAr";
-		std::string rawFileNullExt = "foo.";
-		std::string rawExt = ".bAr";
-		std::string rawDot = ".";
-		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
-		std::string pathExt = pathNoExt + ".eXt";
-		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
-		std::string dottedPathExt = dottedPathNoExt + ".eXt";
-
-		// foo[.bAr]
-
-		ensure_equals("getExtension/r-no-ext",
-			      gDirUtilp->getExtension(rawFile),
-			      "");
-
-		ensure_equals("getExtension/r-ext",
-			      gDirUtilp->getExtension(rawFileExt),
-			      "bar");
-
-		// foo.
-
-		ensure_equals("getExtension/rn-no-ext",
-			      gDirUtilp->getExtension(rawFileNullExt),
-			      "");
-
-		// .bAr
-		// interesting case - with no basename, this IS the basename, not the extension.
-
-		ensure_equals("getExtension/e-ext",
-			      gDirUtilp->getExtension(rawExt),
-			      "");
-
-		// .
-
-		ensure_equals("getExtension/d",
-			      gDirUtilp->getExtension(rawDot),
-			      "");
-
-		// aa/bb/cc/dd/ee[.eXt]
-
-		ensure_equals("getExtension/no-ext",
-			      gDirUtilp->getExtension(pathNoExt),
-			      "");
-
-		ensure_equals("getExtension/ext",
-			      gDirUtilp->getExtension(pathExt),
-			      "ext");
-
-		// aa/bb/cc.dd/ee[.eXt]
-
-		ensure_equals("getExtension/d-no-ext",
-			      gDirUtilp->getExtension(dottedPathNoExt),
-			      "");
-
-		ensure_equals("getExtension/d-ext",
-			      gDirUtilp->getExtension(dottedPathExt),
-			      "ext");
-	}
-
-   std::string makeTestFile( const std::string& dir, const std::string& file )
-   {
-      std::string path = dir + file;
-      LLFILE* handle = LLFile::fopen( path, "w" );
-      ensure("failed to open test file '"+path+"'", handle != NULL );
-      // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs
-      // returns EOF; otherwise, it returns some other, nonnegative value."
-      ensure("failed to write to test file '"+path+"'", EOF != fputs("test file", handle) );
-      fclose(handle);
-      return path;
-   }
-
-   std::string makeTestDir( const std::string& dirbase )
-   {
-      int counter;
-      std::string uniqueDir;
-      bool foundUnused;
-      std::string delim = gDirUtilp->getDirDelimiter();
-      
-      for (counter=0, foundUnused=false; !foundUnused; counter++ )
-      {
-         char counterStr[3];
-         sprintf(counterStr, "%02d", counter);
-         uniqueDir = dirbase + counterStr;
-         foundUnused = ! ( LLFile::isdir(uniqueDir) || LLFile::isfile(uniqueDir) );
-      }
-      ensure("test directory '" + uniqueDir + "' creation failed", !LLFile::mkdir(uniqueDir));
-      
-      return uniqueDir + delim; // HACK - apparently, the trailing delimiter is needed...
-   }
-
-   static const char* DirScanFilename[5] = { "file1.abc", "file2.abc", "file1.xyz", "file2.xyz", "file1.mno" };
-
-   void scanTest(const std::string& directory, const std::string& pattern, bool correctResult[5])
-   {
-
-      // Scan directory and see if any file1.* files are found
-      std::string scanResult;
-      int   found = 0;
-      bool  filesFound[5] = { false, false, false, false, false };
-      //std::cerr << "searching '"+directory+"' for '"+pattern+"'\n";
-
-      LLDirIterator iter(directory, pattern);
-      while ( found <= 5 && iter.next(scanResult) )
-      {
-         found++;
-         //std::cerr << "  found '"+scanResult+"'\n";
-         int check;
-         for (check=0; check < 5 && ! ( scanResult == DirScanFilename[check] ); check++)
-         {
-         }
-         // check is now either 5 (not found) or the index of the matching name
-         if (check < 5)
-         {
-            ensure( "found file '"+(std::string)DirScanFilename[check]+"' twice", ! filesFound[check] );
-            filesFound[check] = true;
-         }
-         else // check is 5 - should not happen
-         {
-            fail( "found unknown file '"+scanResult+"'");
-         }
-      }
-      for (int i=0; i<5; i++)
-      {
-         if (correctResult[i])
-         {
-            ensure("scan of '"+directory+"' using '"+pattern+"' did not return '"+DirScanFilename[i]+"'", filesFound[i]);
-         }
-         else
-         {
-            ensure("scan of '"+directory+"' using '"+pattern+"' incorrectly returned '"+DirScanFilename[i]+"'", !filesFound[i]);
-         }
-      }
-   }
-   
-   template<> template<>
-   void LLDirTest_object_t::test<5>()
-      // LLDirIterator::next
-   {
-      std::string delim = gDirUtilp->getDirDelimiter();
-      std::string dirTemp = LLFile::tmpdir();
-
-      // Create the same 5 file names of the two directories
-
-      std::string dir1 = makeTestDir(dirTemp + "LLDirIterator");
-      std::string dir2 = makeTestDir(dirTemp + "LLDirIterator");
-      std::string dir1files[5];
-      std::string dir2files[5];
-      for (int i=0; i<5; i++)
-      {
-         dir1files[i] = makeTestFile(dir1, DirScanFilename[i]);
-         dir2files[i] = makeTestFile(dir2, DirScanFilename[i]);
-      }
-
-      // Scan dir1 and see if each of the 5 files is found exactly once
-      bool expected1[5] = { true, true, true, true, true };
-      scanTest(dir1, "*", expected1);
-
-      // Scan dir2 and see if only the 2 *.xyz files are found
-      bool  expected2[5] = { false, false, true, true, false };
-      scanTest(dir1, "*.xyz", expected2);
-
-      // Scan dir2 and see if only the 1 *.mno file is found
-      bool  expected3[5] = { false, false, false, false, true };
-      scanTest(dir2, "*.mno", expected3);
-
-      // Scan dir1 and see if any *.foo files are found
-      bool  expected4[5] = { false, false, false, false, false };
-      scanTest(dir1, "*.foo", expected4);
-
-      // Scan dir1 and see if any file1.* files are found
-      bool  expected5[5] = { true, false, true, false, true };
-      scanTest(dir1, "file1.*", expected5);
-
-      // Scan dir1 and see if any file1.* files are found
-      bool  expected6[5] = { true, true, false, false, false };
-      scanTest(dir1, "file?.abc", expected6);
-
-      // Scan dir2 and see if any file?.x?z files are found
-      bool  expected7[5] = { false, false, true, true, false };
-      scanTest(dir2, "file?.x?z", expected7);
-
-      // Scan dir2 and see if any file?.??c files are found
-      bool  expected8[5] = { true, true, false, false, false };
-      scanTest(dir2, "file?.??c", expected8);
-      scanTest(dir2, "*.??c", expected8);
-
-      // Scan dir1 and see if any *.?n? files are found
-      bool  expected9[5] = { false, false, false, false, true };
-      scanTest(dir1, "*.?n?", expected9);
-
-      // Scan dir1 and see if any *.???? files are found
-      bool  expected10[5] = { false, false, false, false, false };
-      scanTest(dir1, "*.????", expected10);
-
-      // Scan dir1 and see if any ?????.* files are found
-      bool  expected11[5] = { true, true, true, true, true };
-      scanTest(dir1, "?????.*", expected11);
-
-      // Scan dir1 and see if any ??l??.xyz files are found
-      bool  expected12[5] = { false, false, true, true, false };
-      scanTest(dir1, "??l??.xyz", expected12);
-
-      bool expected13[5] = { true, false, true, false, false };
-      scanTest(dir1, "file1.{abc,xyz}", expected13);
-
-      bool expected14[5] = { true, true, false, false, false };
-      scanTest(dir1, "file[0-9].abc", expected14);
-
-      bool expected15[5] = { true, true, false, false, false };
-      scanTest(dir1, "file[!a-z].abc", expected15);
-
-      // clean up all test files and directories
-      for (int i=0; i<5; i++)
-      {
-         LLFile::remove(dir1files[i]);
-         LLFile::remove(dir2files[i]);
-      }
-      LLFile::rmdir(dir1);
-      LLFile::rmdir(dir2);
-   }
-
-    template<> template<>
-    void LLDirTest_object_t::test<6>()
-    {
-        set_test_name("findSkinnedFilenames()");
-        LLDir_Dummy lldir;
-        /*------------------------ "default", "en" -------------------------*/
-        // Setting "default" means we shouldn't consider any "*/skins/steam"
-        // directories; setting "en" means we shouldn't consider any "xui/fr"
-        // directories.
-        lldir.setSkinFolder("default", "en");
-        ensure_equals(lldir.getSkinFolder(), "default");
-        ensure_equals(lldir.getLanguage(), "en");
-
-        // top-level directory of a skin isn't localized
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS),
-                      vec(list_of("install/skins/default/colors.xml")
-                                 ("user/skins/default/colors.xml")));
-        // We should not have needed to check for skins/default/en. We should
-        // just "know" that SKINBASE is not localized.
-        lldir.ensure_not_checked("install/skins/default/en");
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"),
-                      vec(list_of("install/skins/default/textures/only_default.jpeg")));
-        // Nor should we have needed to check skins/default/textures/en
-        // because textures is known not to be localized.
-        lldir.ensure_not_checked("install/skins/default/textures/en");
-
-        StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml")
-                               ("user/skins/default/xui/en/strings.xml")));
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
-                      expected);
-        // The first time, we had to probe to find out whether xui was localized.
-        lldir.ensure_checked("install/skins/default/xui/en");
-        lldir.clear_checked();
-        // Now make the same call again -- should return same result --
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
-                      expected);
-        // but this time it should remember that xui is localized.
-        lldir.ensure_not_checked("install/skins/default/xui/en");
-
-        // localized subdir with "en-us" instead of "en"
-        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
-                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
-        lldir.ensure_checked("install/skins/default/html/en");
-        lldir.ensure_checked("install/skins/default/html/en-us");
-        lldir.clear_checked();
-        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
-                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
-        lldir.ensure_not_checked("install/skins/default/html/en");
-        lldir.ensure_not_checked("install/skins/default/html/en-us");
-
-        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"),
-                      vec(list_of("install/skins/default/future/somefile.txt")));
-        // Test probing for an unrecognized unlocalized future subdir.
-        lldir.ensure_checked("install/skins/default/future/en");
-        lldir.clear_checked();
-        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"),
-                      vec(list_of("install/skins/default/future/somefile.txt")));
-        // Second time it should remember that future is unlocalized.
-        lldir.ensure_not_checked("install/skins/default/future/en");
-
-        // When language is set to "en", requesting an html file pulls up the
-        // "en-us" version -- not because it magically matches those strings,
-        // but because there's no "en" localization and it falls back on the
-        // default "en-us"! Note that it would probably still be better to
-        // make the default localization be "en" and allow "en-gb" (or
-        // whatever) localizations, which would work much more the way you'd
-        // expect.
-        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
-                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
-
-        /*------------------------ "default", "fr" -------------------------*/
-        // We start being able to distinguish localized subdirs from
-        // unlocalized when we ask for a non-English language.
-        lldir.setSkinFolder("default", "fr");
-        ensure_equals(lldir.getLanguage(), "fr");
-
-        // pass merge=true to request this filename in all relevant skins
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
-                      vec(list_of
-                          ("install/skins/default/xui/en/strings.xml")
-                          ("install/skins/default/xui/fr/strings.xml")
-                          ("user/skins/default/xui/en/strings.xml")
-                          ("user/skins/default/xui/fr/strings.xml")));
-
-        // pass (or default) merge=false to request only most specific skin
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
-                      vec(list_of
-                          ("user/skins/default/xui/en/strings.xml")
-                          ("user/skins/default/xui/fr/strings.xml")));
-
-        // Our dummy floater.xml has a user localization (for "fr") but no
-        // English override. This is a case in which CURRENT_SKIN nonetheless
-        // returns paths from two different skins.
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"),
-                      vec(list_of
-                          ("install/skins/default/xui/en/floater.xml")
-                          ("user/skins/default/xui/fr/floater.xml")));
-
-        // Our dummy newfile.xml has an English override but no user
-        // localization. This is another case in which CURRENT_SKIN
-        // nonetheless returns paths from two different skins.
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"),
-                      vec(list_of
-                          ("user/skins/default/xui/en/newfile.xml")
-                          ("install/skins/default/xui/fr/newfile.xml")));
-
-        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
-                      vec(list_of
-                          ("install/skins/default/html/en-us/welcome.html")
-                          ("install/skins/default/html/fr/welcome.html")));
-
-        /*------------------------ "default", "zh" -------------------------*/
-        lldir.setSkinFolder("default", "zh");
-        // Because strings.xml has only a "fr" override but no "zh" override
-        // in any skin, the most localized version we can find is "en".
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
-                      vec(list_of("user/skins/default/xui/en/strings.xml")));
-
-        /*------------------------- "steam", "en" --------------------------*/
-        lldir.setSkinFolder("steam", "en");
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS),
-                      vec(list_of
-                          ("install/skins/default/colors.xml")
-                          ("install/skins/steam/colors.xml")
-                          ("user/skins/default/colors.xml")
-                          ("user/skins/steam/colors.xml")));
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"),
-                      vec(list_of("install/skins/default/textures/only_default.jpeg")));
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"),
-                      vec(list_of("install/skins/steam/textures/only_steam.jpeg")));
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"),
-                      vec(list_of("user/skins/default/textures/only_user_default.jpeg")));
-
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"),
-                      vec(list_of("user/skins/steam/textures/only_user_steam.jpeg")));
-
-        // CURRENT_SKIN
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
-                      vec(list_of("user/skins/steam/xui/en/strings.xml")));
-
-        // pass constraint=ALL_SKINS to request this filename in all relevant skins
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
-                      vec(list_of
-                          ("install/skins/default/xui/en/strings.xml")
-                          ("install/skins/steam/xui/en/strings.xml")
-                          ("user/skins/default/xui/en/strings.xml")
-                          ("user/skins/steam/xui/en/strings.xml")));
-
-        /*------------------------- "steam", "fr" --------------------------*/
-        lldir.setSkinFolder("steam", "fr");
-
-        // pass CURRENT_SKIN to request only the most specialized files
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
-                      vec(list_of
-                          ("user/skins/steam/xui/en/strings.xml")
-                          ("user/skins/steam/xui/fr/strings.xml")));
-
-        // pass ALL_SKINS to request this filename in all relevant skins
-        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
-                      vec(list_of
-                          ("install/skins/default/xui/en/strings.xml")
-                          ("install/skins/default/xui/fr/strings.xml")
-                          ("install/skins/steam/xui/en/strings.xml")
-                          ("install/skins/steam/xui/fr/strings.xml")
-                          ("user/skins/default/xui/en/strings.xml")
-                          ("user/skins/default/xui/fr/strings.xml")
-                          ("user/skins/steam/xui/en/strings.xml")
-                          ("user/skins/steam/xui/fr/strings.xml")));
-    }
-
-    template<> template<>
-    void LLDirTest_object_t::test<7>()
-    {
-        set_test_name("add()");
-        LLDir_Dummy lldir;
-        ensure_equals("both empty", lldir.add("", ""), "");
-        ensure_equals("path empty", lldir.add("", "b"), "b");
-        ensure_equals("name empty", lldir.add("a", ""), "a");
-        ensure_equals("both simple", lldir.add("a", "b"), "a/b");
-        ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b");
-        ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b");
-        ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b");
-    }
-}
diff --git a/indra/llcache/tests/lldiriterator_test.cpp b/indra/llcache/tests/lldiriterator_test.cpp
deleted file mode 100644
index a65e3dada5..0000000000
--- a/indra/llcache/tests/lldiriterator_test.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * @file lldiriterator_test.cpp
- * @date 2011-06
- * @brief LLDirIterator test cases.
- *
- * $LicenseInfo:firstyear=2011&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Linden Research, Inc.
- * 
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.,
- * 
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- * 
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "lltut.h"
-#include "../lldiriterator.h"
-
-
-namespace tut
-{
-    
-    struct LLDirIteratorFixture
-    {
-        LLDirIteratorFixture()
-        {
-        }
-    };
-    typedef test_group<LLDirIteratorFixture> LLDirIteratorTest_factory;
-    typedef LLDirIteratorTest_factory::object LLDirIteratorTest_t;
-    LLDirIteratorTest_factory tf("LLDirIterator");
-
-    /*
-    CHOP-662 was originally introduced to deal with crashes deleting files from
-    a directory (VWR-25500). However, this introduced a crash looking for 
-    old chat logs as the glob_to_regex function in lldiriterator wasn't escaping lots of regexp characters
-    */
-    void test_chop_662(void)
-    {
-        //  Check a selection of bad group names from the crash reports 
-        LLDirIterator iter(".","+bad-group-name]+?\?-??.*");
-        LLDirIterator iter1(".","))--@---bad-group-name2((?\?-??.*\\.txt");
-        LLDirIterator iter2(".","__^v--x)Cuide d sua vida(x--v^__?\?-??.*"); 
-    }
-
-    template<> template<>
-	void LLDirIteratorTest_t::test<1>()
-    {
-       test_chop_662();
-    }
-
-}
diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt
index e236a307c2..d90ffb5543 100644
--- a/indra/llcharacter/CMakeLists.txt
+++ b/indra/llcharacter/CMakeLists.txt
@@ -6,14 +6,14 @@ include(00-Common)
 include(LLCommon)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     )
 include_directories(SYSTEM
@@ -85,6 +85,6 @@ target_link_libraries(
     ${LLCOMMON_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLXML_LIBRARIES}
     )
diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt
index 040a0e846c..d70a1e0fb0 100644
--- a/indra/llcrashlogger/CMakeLists.txt
+++ b/indra/llcrashlogger/CMakeLists.txt
@@ -7,7 +7,7 @@ include(LLCoreHttp)
 include(LLCommon)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 
 include_directories(
@@ -15,7 +15,7 @@ include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     )
 include_directories(SYSTEM
diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt
new file mode 100644
index 0000000000..4af14d6d3a
--- /dev/null
+++ b/indra/llfilesystem/CMakeLists.txt
@@ -0,0 +1,98 @@
+# -*- cmake -*-
+
+project(llfilesystem)
+
+include(00-Common)
+include(LLCommon)
+include(UnixInstall)
+
+include_directories(
+    ${LLCOMMON_INCLUDE_DIRS}
+    ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
+    )
+
+set(llfilesystem_SOURCE_FILES
+    lldir.cpp
+    lldiriterator.cpp
+    lllfsthread.cpp
+    lldiskcache.cpp
+    )
+
+set(llfilesystem_HEADER_FILES
+    CMakeLists.txt
+
+    lldir.h
+    lldirguard.h
+    lldiriterator.h
+    lllfsthread.h
+    lldiskcache.h
+    )
+
+if (DARWIN)
+  LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.mm)
+  LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.h)
+  LIST(APPEND llfilesystem_SOURCE_FILES lldir_mac.cpp)
+  LIST(APPEND llfilesystem_HEADER_FILES lldir_mac.h)
+endif (DARWIN)
+
+if (LINUX)
+  LIST(APPEND llfilesystem_SOURCE_FILES lldir_linux.cpp)
+  LIST(APPEND llfilesystem_HEADER_FILES lldir_linux.h)
+
+  if (INSTALL)
+    set_source_files_properties(lldir_linux.cpp
+                                PROPERTIES COMPILE_FLAGS
+                                "-DAPP_RO_DATA_DIR=\\\"${APP_SHARE_DIR}\\\""
+                                )
+  endif (INSTALL)
+endif (LINUX)
+
+if (WINDOWS)
+  LIST(APPEND llfilesystem_SOURCE_FILES lldir_win32.cpp)
+  LIST(APPEND llfilesystem_HEADER_FILES lldir_win32.h)
+endif (WINDOWS)
+
+set_source_files_properties(${llfilesystem_HEADER_FILES}
+                            PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llfilesystem_SOURCE_FILES ${llfilesystem_HEADER_FILES})
+
+add_library (llfilesystem ${llfilesystem_SOURCE_FILES})
+
+set(cache_BOOST_LIBRARIES
+    ${BOOST_FILESYSTEM_LIBRARY}
+    ${BOOST_SYSTEM_LIBRARY}
+    )
+
+target_link_libraries(llfilesystem
+    ${LLCOMMON_LIBRARIES}
+    ${cache_BOOST_LIBRARIES}
+    )
+
+if (DARWIN)
+  include(CMakeFindFrameworks)
+  find_library(COCOA_LIBRARY Cocoa)
+  target_link_libraries(llfilesystem ${COCOA_LIBRARY})
+endif (DARWIN)
+
+
+# Add tests
+if (LL_TESTS)
+    include(LLAddBuildTest)
+    # UNIT TESTS
+    SET(llfilesystem_TEST_SOURCE_FILES
+    lldiriterator.cpp
+    )
+
+    set_source_files_properties(lldiriterator.cpp
+    PROPERTIES
+    LL_TEST_ADDITIONAL_LIBRARIES "${cache_BOOST_LIBRARIES}"
+    )
+    LL_ADD_PROJECT_UNIT_TESTS(llfilesystem "${llfilesystem_TEST_SOURCE_FILES}")
+
+    # INTEGRATION TESTS
+    set(test_libs llmath llcommon llfilesystem ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+
+    # TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests.
+    LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}")
+endif (LL_TESTS)
diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp
new file mode 100644
index 0000000000..10fbc06c61
--- /dev/null
+++ b/indra/llfilesystem/lldir.cpp
@@ -0,0 +1,1134 @@
+/** 
+ * @file lldir.cpp
+ * @brief implementation of directory utilities base class
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#if !LL_WINDOWS
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#else
+#include <direct.h>
+#endif
+
+#include "lldir.h"
+
+#include "llerror.h"
+#include "lltimer.h"	// ms_sleep()
+#include "lluuid.h"
+
+#include "lldiriterator.h"
+#include "stringize.h"
+#include "llstring.h"
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/range/begin.hpp>
+#include <boost/range/end.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/bind.hpp>
+#include <boost/ref.hpp>
+#include <algorithm>
+
+using boost::assign::list_of;
+using boost::assign::map_list_of;
+
+#if LL_WINDOWS
+#include "lldir_win32.h"
+LLDir_Win32 gDirUtil;
+#elif LL_DARWIN
+#include "lldir_mac.h"
+LLDir_Mac gDirUtil;
+#elif LL_SOLARIS
+#include "lldir_solaris.h"
+LLDir_Solaris gDirUtil;
+#else
+#include "lldir_linux.h"
+LLDir_Linux gDirUtil;
+#endif
+
+LLDir *gDirUtilp = (LLDir *)&gDirUtil;
+
+/// Values for findSkinnedFilenames(subdir) parameter
+const char
+	*LLDir::XUI      = "xui",
+	*LLDir::TEXTURES = "textures",
+	*LLDir::SKINBASE = "";
+
+static const char* const empty = "";
+std::string LLDir::sDumpDir = "";
+
+LLDir::LLDir()
+:	mAppName(""),
+	mExecutablePathAndName(""),
+	mExecutableFilename(""),
+	mExecutableDir(""),
+	mAppRODataDir(""),
+	mOSUserDir(""),
+	mOSUserAppDir(""),
+	mLindenUserDir(""),
+	mOSCacheDir(""),
+	mCAFile(""),
+	mTempDir(""),
+	mDirDelimiter("/"), // fallback to forward slash if not overridden
+	mLanguage("en"),
+	mUserName("undefined")
+{
+}
+
+LLDir::~LLDir()
+{
+}
+
+std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
+{
+    //Returns a vector of fullpath filenames.
+
+#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
+    boost::filesystem::path p(utf8str_to_utf16str(dirname));
+#else
+    boost::filesystem::path p(dirname);
+#endif
+
+    std::vector<std::string> v;
+    
+    if (exists(p))
+    {
+        if (is_directory(p))
+        {
+            boost::filesystem::directory_iterator end_iter;
+            for (boost::filesystem::directory_iterator dir_itr(p);
+                 dir_itr != end_iter;
+                 ++dir_itr)
+            {
+                if (boost::filesystem::is_regular_file(dir_itr->status()))
+                {
+                    v.push_back(dir_itr->path().filename().string());
+                }
+            }
+        }
+    }
+    return v;
+}   
+            
+S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
+{
+	S32 count = 0;
+	std::string filename; 
+	std::string fullpath;
+	S32 result;
+
+	// File masks starting with "/" will match nothing, so we consider them invalid.
+	if (LLStringUtil::startsWith(mask, getDirDelimiter()))
+	{
+		LL_WARNS() << "Invalid file mask: " << mask << LL_ENDL;
+		llassert(!"Invalid file mask");
+	}
+
+	LLDirIterator iter(dirname, mask);
+	while (iter.next(filename))
+	{
+		fullpath = add(dirname, filename);
+
+		if(LLFile::isdir(fullpath))
+		{
+			// skipping directory traversal filenames
+			count++;
+			continue;
+		}
+
+		S32 retry_count = 0;
+		while (retry_count < 5)
+		{
+			if (0 != LLFile::remove(fullpath))
+			{
+				retry_count++;
+				result = errno;
+				LL_WARNS() << "Problem removing " << fullpath << " - errorcode: "
+						<< result << " attempt " << retry_count << LL_ENDL;
+
+				if(retry_count >= 5)
+				{
+					LL_WARNS() << "Failed to remove " << fullpath << LL_ENDL ;
+					return count ;
+				}
+
+				ms_sleep(100);
+			}
+			else
+			{
+				if (retry_count)
+				{
+					LL_WARNS() << "Successfully removed " << fullpath << LL_ENDL;
+				}
+				break;
+			}			
+		}
+		count++;
+	}
+	return count;
+}
+
+U32 LLDir::deleteDirAndContents(const std::string& dir_name)
+{
+    //Removes the directory and its contents.  Returns number of files deleted.
+	
+	U32 num_deleted = 0;
+
+	try
+	{
+#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
+		boost::filesystem::path dir_path(utf8str_to_utf16str(dir_name));
+#else
+		boost::filesystem::path dir_path(dir_name);
+#endif
+
+	   if (boost::filesystem::exists (dir_path))
+	   {
+	      if (!boost::filesystem::is_empty (dir_path))
+		  {   // Directory has content
+		     num_deleted = boost::filesystem::remove_all (dir_path);
+		  }
+		  else
+		  {   // Directory is empty
+		     boost::filesystem::remove (dir_path);
+		  }
+	   }
+	}
+	catch (boost::filesystem::filesystem_error &er)
+	{ 
+		LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL;
+	} 
+	return num_deleted;
+}
+
+const std::string LLDir::findFile(const std::string &filename, 
+						   const std::string& searchPath1, 
+						   const std::string& searchPath2, 
+						   const std::string& searchPath3) const
+{
+	std::vector<std::string> search_paths;
+	search_paths.push_back(searchPath1);
+	search_paths.push_back(searchPath2);
+	search_paths.push_back(searchPath3);
+	return findFile(filename, search_paths);
+}
+
+const std::string LLDir::findFile(const std::string& filename, const std::vector<std::string> search_paths) const
+{
+	std::vector<std::string>::const_iterator search_path_iter;
+	for (search_path_iter = search_paths.begin();
+		search_path_iter != search_paths.end();
+		++search_path_iter)
+	{
+		if (!search_path_iter->empty())
+		{
+			std::string filename_and_path = (*search_path_iter);
+			if (!filename.empty())
+			{
+				filename_and_path += getDirDelimiter() + filename;
+			}
+			if (fileExists(filename_and_path))
+			{
+				return filename_and_path;
+			}
+		}
+	}
+	return "";
+}
+
+
+const std::string &LLDir::getExecutablePathAndName() const
+{
+	return mExecutablePathAndName;
+}
+
+const std::string &LLDir::getExecutableFilename() const
+{
+	return mExecutableFilename;
+}
+
+const std::string &LLDir::getExecutableDir() const
+{
+	return mExecutableDir;
+}
+
+const std::string &LLDir::getWorkingDir() const
+{
+	return mWorkingDir;
+}
+
+const std::string &LLDir::getAppName() const
+{
+	return mAppName;
+}
+
+const std::string &LLDir::getAppRODataDir() const
+{
+	return mAppRODataDir;
+}
+
+const std::string &LLDir::getOSUserDir() const
+{
+	return mOSUserDir;
+}
+
+const std::string &LLDir::getOSUserAppDir() const
+{
+	return mOSUserAppDir;
+}
+
+const std::string &LLDir::getLindenUserDir() const
+{
+	if (mLindenUserDir.empty())
+	{
+		LL_DEBUGS() << "getLindenUserDir() called early, we don't have the user name yet - returning empty string to caller" << LL_ENDL;
+	}
+
+	return mLindenUserDir;
+}
+
+const std::string& LLDir::getChatLogsDir() const
+{
+	return mChatLogsDir;
+}
+
+void LLDir::setDumpDir( const std::string& path )
+{
+    LLDir::sDumpDir = path;
+    if (LLStringUtil::endsWith(sDumpDir, mDirDelimiter))
+    {
+        sDumpDir.erase(sDumpDir.size() - mDirDelimiter.size());
+    }
+}
+
+const std::string &LLDir::getDumpDir() const
+{
+    if (sDumpDir.empty() )
+    {
+        LLUUID uid;
+        uid.generate();
+        
+        sDumpDir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "")
+                    + "dump-" + uid.asString();
+
+        dir_exists_or_crash(sDumpDir);  
+    }
+
+	return LLDir::sDumpDir;
+}
+
+const std::string &LLDir::getPerAccountChatLogsDir() const
+{
+	return mPerAccountChatLogsDir;
+}
+
+const std::string &LLDir::getTempDir() const
+{
+	return mTempDir;
+}
+
+const std::string  LLDir::getCacheDir(bool get_default) const
+{
+	if (mCacheDir.empty() || get_default)
+	{
+		if (!mDefaultCacheDir.empty())
+		{	// Set at startup - can't set here due to const API
+			return mDefaultCacheDir;
+		}
+		
+		std::string res = buildSLOSCacheDir();
+		return res;
+	}
+	else
+	{
+		return mCacheDir;
+	}
+}
+
+// Return the default cache directory
+std::string LLDir::buildSLOSCacheDir() const
+{
+	std::string res;
+	if (getOSCacheDir().empty())
+	{
+		if (getOSUserAppDir().empty())
+		{
+			res = "data";
+		}
+		else
+		{
+			res = add(getOSUserAppDir(), "cache");
+		}
+	}
+	else
+	{
+		res = add(getOSCacheDir(), "SecondLife");
+	}
+	return res;
+}
+
+
+
+const std::string &LLDir::getOSCacheDir() const
+{
+	return mOSCacheDir;
+}
+
+
+const std::string &LLDir::getCAFile() const
+{
+	return mCAFile;
+}
+
+const std::string &LLDir::getDirDelimiter() const
+{
+	return mDirDelimiter;
+}
+
+const std::string& LLDir::getDefaultSkinDir() const
+{
+	return mDefaultSkinDir;
+}
+
+const std::string &LLDir::getSkinDir() const
+{
+	return mSkinDir;
+}
+
+const std::string &LLDir::getUserDefaultSkinDir() const
+{
+    return mUserDefaultSkinDir;
+}
+
+const std::string &LLDir::getUserSkinDir() const
+{
+	return mUserSkinDir;
+}
+
+const std::string LLDir::getSkinBaseDir() const
+{
+	return mSkinBaseDir;
+}
+
+const std::string &LLDir::getLLPluginDir() const
+{
+	return mLLPluginDir;
+}
+
+const std::string &LLDir::getUserName() const
+{
+	return mUserName;
+}
+
+static std::string ELLPathToString(ELLPath location)
+{
+	typedef std::map<ELLPath, const char*> ELLPathMap;
+#define ENT(symbol) (symbol, #symbol)
+	static const ELLPathMap sMap = map_list_of
+		ENT(LL_PATH_NONE)
+		ENT(LL_PATH_USER_SETTINGS)
+		ENT(LL_PATH_APP_SETTINGS)
+		ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet
+		ENT(LL_PATH_CACHE)
+		ENT(LL_PATH_CHARACTER)
+		ENT(LL_PATH_HELP)
+		ENT(LL_PATH_LOGS)
+		ENT(LL_PATH_TEMP)
+		ENT(LL_PATH_SKINS)
+		ENT(LL_PATH_TOP_SKIN)
+		ENT(LL_PATH_CHAT_LOGS)
+		ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS)
+		ENT(LL_PATH_USER_SKIN)
+		ENT(LL_PATH_LOCAL_ASSETS)
+		ENT(LL_PATH_EXECUTABLE)
+		ENT(LL_PATH_DEFAULT_SKIN)
+		ENT(LL_PATH_FONTS)
+		ENT(LL_PATH_LAST)
+	;
+#undef ENT
+
+	ELLPathMap::const_iterator found = sMap.find(location);
+	if (found != sMap.end())
+		return found->second;
+	return STRINGIZE("Invalid ELLPath value " << location);
+}
+
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const
+{
+	return getExpandedFilename(location, "", filename);
+}
+
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir, const std::string& filename) const
+{
+	return getExpandedFilename(location, "", subdir, filename);
+}
+
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir1, const std::string& subdir2, const std::string& in_filename) const
+{
+	std::string prefix;
+	switch (location)
+	{
+	case LL_PATH_NONE:
+		// Do nothing
+		break;
+
+	case LL_PATH_APP_SETTINGS:
+		prefix = add(getAppRODataDir(), "app_settings");
+		break;
+	
+	case LL_PATH_CHARACTER:
+		prefix = add(getAppRODataDir(), "character");
+		break;
+		
+	case LL_PATH_HELP:
+		prefix = "help";
+		break;
+		
+	case LL_PATH_CACHE:
+		prefix = getCacheDir();
+		break;
+		
+    case LL_PATH_DUMP:
+        prefix=getDumpDir();
+        break;
+            
+	case LL_PATH_USER_SETTINGS:
+		prefix = add(getOSUserAppDir(), "user_settings");
+		break;
+
+	case LL_PATH_PER_SL_ACCOUNT:
+		prefix = getLindenUserDir();
+		if (prefix.empty())
+		{
+			// if we're asking for the per-SL-account directory but we haven't
+			// logged in yet (or otherwise don't know the account name from
+			// which to build this string), then intentionally return a blank
+			// string to the caller and skip the below warning about a blank
+			// prefix.
+			LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: "
+							   << ELLPathToString(location)
+							   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
+							   << "' => ''" << LL_ENDL;
+			return std::string();
+		}
+		break;
+		
+	case LL_PATH_CHAT_LOGS:
+		prefix = getChatLogsDir();
+		break;
+		
+	case LL_PATH_PER_ACCOUNT_CHAT_LOGS:
+		prefix = getPerAccountChatLogsDir();
+		if (prefix.empty())
+		{
+			// potentially directory was not set yet
+			// intentionally return a blank string to the caller
+			LL_DEBUGS("LLDir") << "Conversation log directory is not yet set" << LL_ENDL;
+			return std::string();
+		}
+		break;
+
+	case LL_PATH_LOGS:
+		prefix = add(getOSUserAppDir(), "logs");
+		break;
+
+	case LL_PATH_TEMP:
+		prefix = getTempDir();
+		break;
+
+	case LL_PATH_TOP_SKIN:
+		prefix = getSkinDir();
+		break;
+
+	case LL_PATH_DEFAULT_SKIN:
+		prefix = getDefaultSkinDir();
+		break;
+
+	case LL_PATH_USER_SKIN:
+		prefix = getUserSkinDir();
+		break;
+
+	case LL_PATH_SKINS:
+		prefix = getSkinBaseDir();
+		break;
+
+	case LL_PATH_LOCAL_ASSETS:
+		prefix = add(getAppRODataDir(), "local_assets");
+		break;
+
+	case LL_PATH_EXECUTABLE:
+		prefix = getExecutableDir();
+		break;
+		
+	case LL_PATH_FONTS:
+		prefix = add(getAppRODataDir(), "fonts");
+		break;
+		
+	default:
+		llassert(0);
+	}
+
+	if (prefix.empty())
+	{
+		LL_WARNS() << ELLPathToString(location)
+				<< ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
+				<< "': prefix is empty, possible bad filename" << LL_ENDL;
+	}
+
+	std::string expanded_filename = add(prefix, subdir1, subdir2);
+	if (expanded_filename.empty() && in_filename.empty())
+	{
+		return "";
+	}
+	// Use explicit concatenation here instead of another add() call. Callers
+	// passing in_filename as "" expect to obtain a pathname ending with
+	// mDirSeparator so they can later directly concatenate with a specific
+	// filename. A caller using add() doesn't care, but there's still code
+	// loose in the system that uses std::string::operator+().
+	expanded_filename += mDirDelimiter;
+	expanded_filename += in_filename;
+
+	LL_DEBUGS("LLDir") << ELLPathToString(location)
+					   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
+					   << "' => '" << expanded_filename << "'" << LL_ENDL;
+	return expanded_filename;
+}
+
+std::string LLDir::getBaseFileName(const std::string& filepath, bool strip_exten) const
+{
+	std::size_t offset = filepath.find_last_of(getDirDelimiter());
+	offset = (offset == std::string::npos) ? 0 : offset+1;
+	std::string res = filepath.substr(offset, std::string::npos);
+	if (strip_exten)
+	{
+		offset = res.find_last_of('.');
+		if (offset != std::string::npos &&
+		    offset != 0) // if basename STARTS with '.', don't strip
+		{
+			res = res.substr(0, offset);
+		}
+	}
+	return res;
+}
+
+std::string LLDir::getDirName(const std::string& filepath) const
+{
+	std::size_t offset = filepath.find_last_of(getDirDelimiter());
+	S32 len = (offset == std::string::npos) ? 0 : offset;
+	std::string dirname = filepath.substr(0, len);
+	return dirname;
+}
+
+std::string LLDir::getExtension(const std::string& filepath) const
+{
+	if (filepath.empty())
+		return std::string();
+	std::string basename = getBaseFileName(filepath, false);
+	std::size_t offset = basename.find_last_of('.');
+	std::string exten = (offset == std::string::npos || offset == 0) ? "" : basename.substr(offset+1);
+	LLStringUtil::toLower(exten);
+	return exten;
+}
+
+std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir,
+											   const std::string &filename,
+											   ESkinConstraint constraint) const
+{
+	// This implementation is basically just as described in the declaration comments.
+	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint));
+	if (found.empty())
+	{
+		return "";
+	}
+	return found.front();
+}
+
+std::string LLDir::findSkinnedFilename(const std::string &subdir,
+									   const std::string &filename,
+									   ESkinConstraint constraint) const
+{
+	// This implementation is basically just as described in the declaration comments.
+	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint));
+	if (found.empty())
+	{
+		return "";
+	}
+	return found.back();
+}
+
+// This method exists because the two code paths for
+// findSkinnedFilenames(ALL_SKINS) and findSkinnedFilenames(CURRENT_SKIN) must
+// generate the list of candidate pathnames in identical ways. The only
+// difference is in the body of the inner loop.
+template <typename FUNCTION>
+void LLDir::walkSearchSkinDirs(const std::string& subdir,
+							   const std::vector<std::string>& subsubdirs,
+							   const std::string& filename,
+							   const FUNCTION& function) const
+{
+	BOOST_FOREACH(std::string skindir, mSearchSkinDirs)
+	{
+		std::string subdir_path(add(skindir, subdir));
+		BOOST_FOREACH(std::string subsubdir, subsubdirs)
+		{
+			std::string full_path(add(subdir_path, subsubdir, filename));
+			if (fileExists(full_path))
+			{
+				function(subsubdir, full_path);
+			}
+		}
+	}
+}
+
+// ridiculous little helper function that should go away when we can use lambda
+inline void push_back(std::vector<std::string>& vector, const std::string& value)
+{
+	vector.push_back(value);
+}
+
+typedef std::map<std::string, std::string> StringMap;
+// ridiculous little helper function that should go away when we can use lambda
+inline void store_in_map(StringMap& map, const std::string& key, const std::string& value)
+{
+	map[key] = value;
+}
+
+std::vector<std::string> LLDir::findSkinnedFilenames(const std::string& subdir,
+													 const std::string& filename,
+													 ESkinConstraint constraint) const
+{
+	// Recognize subdirs that have no localization.
+	static const std::set<std::string> sUnlocalized = list_of
+		("")                        // top-level directory not localized
+		("textures")                // textures not localized
+	;
+
+	LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename
+					   << "', constraint "
+					   << ((constraint == CURRENT_SKIN)? "CURRENT_SKIN" : "ALL_SKINS")
+					   << LL_ENDL;
+
+	// Build results vector.
+	std::vector<std::string> results;
+	// Disallow filenames that may escape subdir
+	if (filename.find("..") != std::string::npos)
+	{
+		LL_WARNS("LLDir") << "Ignoring potentially relative filename '" << filename << "'" << LL_ENDL;
+		return results;
+	}
+
+	// Cache the default language directory for each subdir we've encountered.
+	// A cache entry whose value is the empty string means "not localized,
+	// don't bother checking again."
+	static StringMap sLocalized;
+
+	// Check whether we've already discovered if this subdir is localized.
+	StringMap::const_iterator found = sLocalized.find(subdir);
+	if (found == sLocalized.end())
+	{
+		// We have not yet determined that. Is it one of the subdirs "known"
+		// to be unlocalized?
+		if (sUnlocalized.find(subdir) != sUnlocalized.end())
+		{
+			// This subdir is known to be unlocalized. Remember that.
+			found = sLocalized.insert(StringMap::value_type(subdir, "")).first;
+		}
+		else
+		{
+			// We do not recognize this subdir. Investigate.
+			std::string subdir_path(add(getDefaultSkinDir(), subdir));
+			if (fileExists(add(subdir_path, "en")))
+			{
+				// defaultSkinDir/subdir contains subdir "en". That's our
+				// default language; this subdir is localized.
+				found = sLocalized.insert(StringMap::value_type(subdir, "en")).first;
+			}
+			else if (fileExists(add(subdir_path, "en-us")))
+			{
+				// defaultSkinDir/subdir contains subdir "en-us" but not "en".
+				// Set as default language; this subdir is localized.
+				found = sLocalized.insert(StringMap::value_type(subdir, "en-us")).first;
+			}
+			else
+			{
+				// defaultSkinDir/subdir contains neither "en" nor "en-us".
+				// Assume it's not localized. Remember that assumption.
+				found = sLocalized.insert(StringMap::value_type(subdir, "")).first;
+			}
+		}
+	}
+	// Every code path above should have resulted in 'found' becoming a valid
+	// iterator to an entry in sLocalized.
+	llassert(found != sLocalized.end());
+
+	// Now -- is this subdir localized, or not? The answer determines what
+	// subdirectories we check (under subdir) for the requested filename.
+	std::vector<std::string> subsubdirs;
+	if (found->second.empty())
+	{
+		// subdir is not localized. filename should be located directly within it.
+		subsubdirs.push_back("");
+	}
+	else
+	{
+		// subdir is localized, and found->second is the default language
+		// directory within it. Check both the default language and the
+		// current language -- if it differs from the default, of course.
+		subsubdirs.push_back(found->second);
+		if (mLanguage != found->second)
+		{
+			subsubdirs.push_back(mLanguage);
+		}
+	}
+
+	// The process we use depends on 'constraint'.
+	if (constraint != CURRENT_SKIN) // meaning ALL_SKINS
+	{
+		// ALL_SKINS is simpler: just return every pathname generated by
+		// walkSearchSkinDirs(). Tricky bit: walkSearchSkinDirs() passes its
+		// FUNCTION the subsubdir as well as the full pathname. We just want
+		// the full pathname.
+		walkSearchSkinDirs(subdir, subsubdirs, filename,
+						   boost::bind(push_back, boost::ref(results), _2));
+	}
+	else                            // CURRENT_SKIN
+	{
+		// CURRENT_SKIN turns out to be a bit of a misnomer because we might
+		// still return files from two different skins. In any case, this
+		// value of 'constraint' means we will return at most two paths: one
+		// for the default language, one for the current language (supposing
+		// those differ).
+		// It is important to allow a user to override only the localization
+		// for a particular file, for all viewer installs, without also
+		// overriding the default-language file.
+		// It is important to allow a user to override only the default-
+		// language file, for all viewer installs, without also overriding the
+		// applicable localization of that file.
+		// Therefore, treat the default language and the current language as
+		// two separate cases. For each, capture the most-specialized file
+		// that exists.
+		// Use a map keyed by subsubdir (i.e. language code). This allows us
+		// to handle the case of a single subsubdirs entry with the same logic
+		// that handles two. For every real file path generated by
+		// walkSearchSkinDirs(), update the map entry for its subsubdir.
+		StringMap path_for;
+		walkSearchSkinDirs(subdir, subsubdirs, filename,
+						   boost::bind(store_in_map, boost::ref(path_for), _1, _2));
+		// Now that we have a path for each of the default language and the
+		// current language, copy them -- in proper order -- into results.
+		// Don't drive this by walking the map itself: it matters that we
+		// generate results in the same order as subsubdirs.
+		BOOST_FOREACH(std::string subsubdir, subsubdirs)
+		{
+			StringMap::const_iterator found(path_for.find(subsubdir));
+			if (found != path_for.end())
+			{
+				results.push_back(found->second);
+			}
+		}
+	}
+
+	LL_DEBUGS("LLDir") << empty;
+	const char* comma = "";
+	BOOST_FOREACH(std::string path, results)
+	{
+		LL_CONT << comma << "'" << path << "'";
+		comma = ", ";
+	}
+	LL_CONT << LL_ENDL;
+
+	return results;
+}
+
+std::string LLDir::getTempFilename() const
+{
+	LLUUID random_uuid;
+	std::string uuid_str;
+
+	random_uuid.generate();
+	random_uuid.toString(uuid_str);
+
+	return add(getTempDir(), uuid_str + ".tmp");
+}
+
+// static
+std::string LLDir::getScrubbedFileName(const std::string uncleanFileName)
+{
+	std::string name(uncleanFileName);
+	const std::string illegalChars(getForbiddenFileChars());
+	// replace any illegal file chars with and underscore '_'
+	for( unsigned int i = 0; i < illegalChars.length(); i++ )
+	{
+		int j = -1;
+		while((j = name.find(illegalChars[i])) > -1)
+		{
+			name[j] = '_';
+		}
+	}
+	return name;
+}
+
+// static
+std::string LLDir::getForbiddenFileChars()
+{
+	return "\\/:*?\"<>|";
+}
+
+void LLDir::setLindenUserDir(const std::string &username)
+{
+	// if the username isn't set, that's bad
+	if (!username.empty())
+	{
+		// some platforms have case-sensitive filesystems, so be
+		// utterly consistent with our firstname/lastname case.
+		std::string userlower(username);
+		LLStringUtil::toLower(userlower);
+		LLStringUtil::replaceChar(userlower, ' ', '_');
+		mLindenUserDir = add(getOSUserAppDir(), userlower);
+	}
+	else
+	{
+		LL_ERRS() << "NULL name for LLDir::setLindenUserDir" << LL_ENDL;
+	}
+
+	dumpCurrentDirectories();	
+}
+
+void LLDir::setChatLogsDir(const std::string &path)
+{
+	if (!path.empty() )
+	{
+		mChatLogsDir = path;
+	}
+	else
+	{
+		LL_WARNS() << "Invalid name for LLDir::setChatLogsDir" << LL_ENDL;
+	}
+}
+
+void LLDir::updatePerAccountChatLogsDir()
+{
+	mPerAccountChatLogsDir = add(getChatLogsDir(), mUserName);
+}
+
+void LLDir::setPerAccountChatLogsDir(const std::string &username)
+{
+	// if both first and last aren't set, assume we're grabbing the cached dir
+	if (!username.empty())
+	{
+		// some platforms have case-sensitive filesystems, so be
+		// utterly consistent with our firstname/lastname case.
+		std::string userlower(username);
+		LLStringUtil::toLower(userlower);
+		LLStringUtil::replaceChar(userlower, ' ', '_');
+
+		mUserName = userlower;
+		updatePerAccountChatLogsDir();
+	}
+	else
+	{
+		LL_ERRS() << "NULL name for LLDir::setPerAccountChatLogsDir" << LL_ENDL;
+	}
+}
+
+void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language)
+{
+	LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'"
+					   << LL_ENDL;
+	mSkinName = skin_folder;
+	mLanguage = language;
+
+	// This method is called multiple times during viewer initialization. Each
+	// time it's called, reset mSearchSkinDirs.
+	mSearchSkinDirs.clear();
+
+	// base skin which is used as fallback for all skinned files
+	// e.g. c:\program files\secondlife\skins\default
+	mDefaultSkinDir = getSkinBaseDir();
+	append(mDefaultSkinDir, "default");
+	// This is always the most general of the search skin directories.
+	addSearchSkinDir(mDefaultSkinDir);
+
+	mSkinDir = getSkinBaseDir();
+	append(mSkinDir, skin_folder);
+	// Next level of generality is a skin installed with the viewer.
+	addSearchSkinDir(mSkinDir);
+
+	// user modifications to skins, current and default
+	// e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle
+	mUserSkinDir = getOSUserAppDir();
+	append(mUserSkinDir, "skins");
+	mUserDefaultSkinDir = mUserSkinDir;
+	append(mUserDefaultSkinDir, "default");
+	append(mUserSkinDir, skin_folder);
+	// Next level of generality is user modifications to default skin...
+	addSearchSkinDir(mUserDefaultSkinDir);
+	// then user-defined skins.
+	addSearchSkinDir(mUserSkinDir);
+}
+
+void LLDir::addSearchSkinDir(const std::string& skindir)
+{
+	if (std::find(mSearchSkinDirs.begin(), mSearchSkinDirs.end(), skindir) == mSearchSkinDirs.end())
+	{
+		LL_DEBUGS("LLDir") << "search skin: '" << skindir << "'" << LL_ENDL;
+		mSearchSkinDirs.push_back(skindir);
+	}
+}
+
+std::string LLDir::getSkinFolder() const
+{
+	return mSkinName;
+}
+
+std::string LLDir::getLanguage() const
+{
+	return mLanguage;
+}
+
+bool LLDir::setCacheDir(const std::string &path)
+{
+	if (path.empty() )
+	{
+		// reset to default
+		mCacheDir = "";
+		return true;
+	}
+	else
+	{
+		LLFile::mkdir(path);
+		std::string tempname = add(path, "temp");
+		LLFILE* file = LLFile::fopen(tempname,"wt");
+		if (file)
+		{
+			fclose(file);
+			LLFile::remove(tempname);
+			mCacheDir = path;
+			return true;
+		}
+		return false;
+	}
+}
+
+void LLDir::dumpCurrentDirectories(LLError::ELevel level)
+{
+	LL_VLOGS(level, "AppInit","Directories") << "Current Directories:" << LL_ENDL;
+
+	LL_VLOGS(level, "AppInit", "Directories") << "  CurPath:               " << getCurPath() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  AppName:               " << getAppName() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutableFilename:    " << getExecutableFilename() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutableDir:         " << getExecutableDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  ExecutablePathAndName: " << getExecutablePathAndName() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  WorkingDir:            " << getWorkingDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  AppRODataDir:          " << getAppRODataDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  OSUserDir:             " << getOSUserDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  OSUserAppDir:          " << getOSUserAppDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  LindenUserDir:         " << getLindenUserDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  TempDir:               " << getTempDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  CAFile:                " << getCAFile() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  SkinBaseDir:           " << getSkinBaseDir() << LL_ENDL;
+	LL_VLOGS(level, "AppInit", "Directories") << "  SkinDir:               " << getSkinDir() << LL_ENDL;
+}
+
+void LLDir::append(std::string& destpath, const std::string& name) const
+{
+	// Delegate question of whether we need a separator to helper method.
+	SepOff sepoff(needSep(destpath, name));
+	if (sepoff.first)               // do we need a separator?
+	{
+		destpath += mDirDelimiter;
+	}
+	// If destpath ends with a separator, AND name starts with one, skip
+	// name's leading separator.
+	destpath += name.substr(sepoff.second);
+}
+
+LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const
+{
+	if (path.empty() || name.empty())
+	{
+		// If either path or name are empty, we do not need a separator
+		// between them.
+		return SepOff(false, 0);
+	}
+	// Here we know path and name are both non-empty. But if path already ends
+	// with a separator, or if name already starts with a separator, we need
+	// not add one.
+	std::string::size_type seplen(mDirDelimiter.length());
+	bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter);
+	bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter);
+	if ((! path_ends_sep) && (! name_starts_sep))
+	{
+		// If neither path nor name brings a separator to the junction, then
+		// we need one.
+		return SepOff(true, 0);
+	}
+	if (path_ends_sep && name_starts_sep)
+	{
+		// But if BOTH path and name bring a separator, we need not add one.
+		// Moreover, we should actually skip the leading separator of 'name'.
+		return SepOff(false, (unsigned short)seplen);
+	}
+	// Here we know that either path_ends_sep or name_starts_sep is true --
+	// but not both. So don't add a separator, and don't skip any characters:
+	// simple concatenation will do the trick.
+	return SepOff(false, 0);
+}
+
+void dir_exists_or_crash(const std::string &dir_name)
+{
+#if LL_WINDOWS
+	// *FIX: lame - it doesn't do the same thing on windows. not so
+	// important since we don't deploy simulator to windows boxes.
+	LLFile::mkdir(dir_name, 0700);
+#else
+	struct stat dir_stat;
+	if(0 != LLFile::stat(dir_name, &dir_stat))
+	{
+		S32 stat_rv = errno;
+		if(ENOENT == stat_rv)
+		{
+		   if(0 != LLFile::mkdir(dir_name, 0700))		// octal
+		   {
+			   LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL;
+		   }
+		}
+		else
+		{
+			LL_ERRS() << "Unable to stat: " << dir_name << " errno = " << stat_rv
+				   << LL_ENDL;
+		}
+	}
+	else
+	{
+		// data_dir exists, make sure it's a directory.
+		if(!S_ISDIR(dir_stat.st_mode))
+		{
+			LL_ERRS() << "Data directory collision: " << dir_name << LL_ENDL;
+		}
+	}
+#endif
+}
diff --git a/indra/llfilesystem/lldir.h b/indra/llfilesystem/lldir.h
new file mode 100644
index 0000000000..38e204ef04
--- /dev/null
+++ b/indra/llfilesystem/lldir.h
@@ -0,0 +1,280 @@
+/**  
+ * @file lldir.h
+ * @brief Definition of directory utilities class
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDIR_H
+#define LL_LLDIR_H
+
+#if LL_SOLARIS
+#include <sys/param.h>
+#define MAX_PATH MAXPATHLEN
+#endif
+
+// these numbers are read from settings_files.xml, so we need to be explicit
+typedef enum ELLPath
+{
+	LL_PATH_NONE = 0,
+	LL_PATH_USER_SETTINGS = 1,
+	LL_PATH_APP_SETTINGS = 2,	
+	LL_PATH_PER_SL_ACCOUNT = 3, // returns/expands to blank string if we don't know the account name yet
+	LL_PATH_CACHE = 4,	
+	LL_PATH_CHARACTER = 5,	
+	LL_PATH_HELP = 6,		
+	LL_PATH_LOGS = 7,
+	LL_PATH_TEMP = 8,
+	LL_PATH_SKINS = 9,
+	LL_PATH_TOP_SKIN = 10,
+	LL_PATH_CHAT_LOGS = 11,
+	LL_PATH_PER_ACCOUNT_CHAT_LOGS = 12,
+	LL_PATH_USER_SKIN = 14,
+	LL_PATH_LOCAL_ASSETS = 15,
+	LL_PATH_EXECUTABLE = 16,
+	LL_PATH_DEFAULT_SKIN = 17,
+	LL_PATH_FONTS = 18,
+    LL_PATH_DUMP = 19,
+	LL_PATH_LAST
+} ELLPath;
+
+/// Directory operations
+class LLDir
+{
+ public:
+	LLDir();
+	virtual ~LLDir();
+
+	// app_name - Usually SecondLife, used for creating settings directories
+	// in OS-specific location, such as C:\Documents and Settings
+	// app_read_only_data_dir - Usually the source code directory, used
+	// for test applications to read newview data files.
+	virtual void initAppDirs(const std::string &app_name, 
+		const std::string& app_read_only_data_dir = "") = 0;
+
+	virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
+    U32 deleteDirAndContents(const std::string& dir_name);
+    std::vector<std::string> getFilesInDir(const std::string &dirname);
+// pure virtual functions
+	virtual std::string getCurPath() = 0;
+	virtual bool fileExists(const std::string &filename) const = 0;
+
+	const std::string findFile(const std::string& filename, const std::vector<std::string> filenames) const; 
+	const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const;
+
+	virtual std::string getLLPluginLauncher() = 0; // full path and name for the plugin shell
+	virtual std::string getLLPluginFilename(std::string base_name) = 0; // full path and name to the plugin DSO for this base_name (i.e. 'FOO' -> '/bar/baz/libFOO.so')
+
+	const std::string &getExecutablePathAndName() const;	// Full pathname of the executable
+	const std::string &getAppName() const;			// install directory under progams/ ie "SecondLife"
+	const std::string &getExecutableDir() const;	// Directory where the executable is located
+	const std::string &getExecutableFilename() const;// Filename of .exe
+	const std::string &getWorkingDir() const; // Current working directory
+	const std::string &getAppRODataDir() const;	// Location of read-only data files
+	const std::string &getOSUserDir() const;		// Location of the os-specific user dir
+	const std::string &getOSUserAppDir() const;	// Location of the os-specific user app dir
+	const std::string &getLindenUserDir() const;	// Location of the Linden user dir.
+	const std::string &getChatLogsDir() const;	// Location of the chat logs dir.
+	const std::string &getDumpDir() const;	// Location of the per-run dump dir.
+	const std::string &getPerAccountChatLogsDir() const;	// Location of the per account chat logs dir.
+	const std::string &getTempDir() const;			// Common temporary directory
+	const std::string  getCacheDir(bool get_default = false) const;	// Location of the cache.
+	const std::string &getOSCacheDir() const;		// location of OS-specific cache folder (may be empty string)
+	const std::string &getCAFile() const;			// File containing TLS certificate authorities
+	const std::string &getDirDelimiter() const;	// directory separator for platform (ie. '\' or '/' or ':')
+	const std::string &getDefaultSkinDir() const;	// folder for default skin. e.g. c:\program files\second life\skins\default
+	const std::string &getSkinDir() const;		// User-specified skin folder.
+	const std::string &getUserDefaultSkinDir() const; // dir with user modifications to default skin
+	const std::string &getUserSkinDir() const;		// User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin
+	const std::string getSkinBaseDir() const;		// folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins
+	const std::string &getLLPluginDir() const;		// Directory containing plugins and plugin shell
+	const std::string &getUserName() const;
+
+	// Expanded filename
+	std::string getExpandedFilename(ELLPath location, const std::string &filename) const;
+	std::string getExpandedFilename(ELLPath location, const std::string &subdir, const std::string &filename) const;
+	std::string getExpandedFilename(ELLPath location, const std::string &subdir1, const std::string &subdir2, const std::string &filename) const;
+
+	// Base and Directory name extraction
+	std::string getBaseFileName(const std::string& filepath, bool strip_exten = false) const;
+	std::string getDirName(const std::string& filepath) const;
+	std::string getExtension(const std::string& filepath) const; // Excludes '.', e.g getExtension("foo.wav") == "wav"
+
+	// these methods search the various skin paths for the specified file in the following order:
+	// getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir()
+	/// param value for findSkinnedFilenames(), explained below
+	enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS };
+	/**
+	 * Given a filename within skin, return an ordered sequence of paths to
+	 * search. Nonexistent files will be filtered out -- which means that the
+	 * vector might be empty.
+	 *
+	 * @param subdir Identify top-level skin subdirectory by passing one of
+	 * LLDir::XUI (file lives under "xui" subtree), LLDir::TEXTURES (file
+	 * lives under "textures" subtree), LLDir::SKINBASE (file lives at top
+	 * level of skin subdirectory).
+	 * @param filename Desired filename within subdir within skin, e.g.
+	 * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language.
+	 * @param constraint Callers perform two different kinds of processing.
+	 * When fetching a XUI file, for instance, the existence of @a filename in
+	 * the specified skin completely supercedes any @a filename in the default
+	 * skin. For that case, leave the default @a constraint=CURRENT_SKIN. The
+	 * returned vector will contain only
+	 * ".../<i>current_skin</i>/xui/en/<i>filename</i>",
+	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/<i>filename</i>".
+	 * But for (e.g.) "strings.xml", we want a given skin to be able to
+	 * override only specific entries from the default skin. Any string not
+	 * defined in the specified skin will be sought in the default skin. For
+	 * that case, pass @a constraint=ALL_SKINS. The returned vector will
+	 * contain at least ".../default/xui/en/strings.xml",
+	 * ".../default/xui/<i>current_language</i>/strings.xml",
+	 * ".../<i>current_skin</i>/xui/en/strings.xml",
+	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/strings.xml".
+	 */
+	std::vector<std::string> findSkinnedFilenames(const std::string& subdir,
+												  const std::string& filename,
+												  ESkinConstraint constraint=CURRENT_SKIN) const;
+	/// Values for findSkinnedFilenames(subdir) parameter
+	static const char *XUI, *TEXTURES, *SKINBASE;
+	/**
+	 * Return the base-language pathname from findSkinnedFilenames(), or
+	 * the empty string if no such file exists. Parameters are identical to
+	 * findSkinnedFilenames(). This is shorthand for capturing the vector
+	 * returned by findSkinnedFilenames(), checking for empty() and then
+	 * returning front().
+	 */
+	std::string findSkinnedFilenameBaseLang(const std::string &subdir,
+											const std::string &filename,
+											ESkinConstraint constraint=CURRENT_SKIN) const;
+	/**
+	 * Return the "most localized" pathname from findSkinnedFilenames(), or
+	 * the empty string if no such file exists. Parameters are identical to
+	 * findSkinnedFilenames(). This is shorthand for capturing the vector
+	 * returned by findSkinnedFilenames(), checking for empty() and then
+	 * returning back().
+	 */
+	std::string findSkinnedFilename(const std::string &subdir,
+									const std::string &filename,
+									ESkinConstraint constraint=CURRENT_SKIN) const;
+
+	// random filename in common temporary directory
+	std::string getTempFilename() const;
+
+	// For producing safe download file names from potentially unsafe ones
+	static std::string getScrubbedFileName(const std::string uncleanFileName);
+	static std::string getForbiddenFileChars();
+    void setDumpDir( const std::string& path );
+
+
+	virtual void setChatLogsDir(const std::string &path);		// Set the chat logs dir to this user's dir
+	virtual void setPerAccountChatLogsDir(const std::string &username);		// Set the per user chat log directory.
+	virtual void setLindenUserDir(const std::string &username);		// Set the linden user dir to this user's dir
+	virtual void setSkinFolder(const std::string &skin_folder, const std::string& language);
+	virtual std::string getSkinFolder() const;
+	virtual std::string getLanguage() const;
+	virtual bool setCacheDir(const std::string &path);
+	virtual void updatePerAccountChatLogsDir();
+
+	virtual void dumpCurrentDirectories(LLError::ELevel level = LLError::LEVEL_DEBUG);
+
+	// Utility routine
+	std::string buildSLOSCacheDir() const;
+
+	/// Append specified @a name to @a destpath, separated by getDirDelimiter()
+	/// if both are non-empty.
+	void append(std::string& destpath, const std::string& name) const;
+	/// Variadic form: append @a name0 and @a name1 and arbitrary other @a
+	/// names to @a destpath, separated by getDirDelimiter() as needed.
+	template <typename... NAMES>
+	void append(std::string& destpath, const std::string& name0, const std::string& name1,
+				const NAMES& ... names) const
+	{
+		// In a typical recursion case, we'd accept (destpath, name0, names).
+		// We accept (destpath, name0, name1, names) because it's important to
+		// delegate the two-argument case to the non-template implementation.
+		append(destpath, name0);
+		append(destpath, name1, names...);
+	}
+
+	/// Append specified @a names to @a path, separated by getDirDelimiter()
+	/// as needed. Return result, leaving @a path unmodified.
+	template <typename... NAMES>
+	std::string add(const std::string& path, const NAMES& ... names) const
+	{
+		std::string destpath(path);
+		append(destpath, names...);
+		return destpath;
+	}
+
+protected:
+	// Does an add() or append() call need a directory delimiter?
+	typedef std::pair<bool, unsigned short> SepOff;
+	SepOff needSep(const std::string& path, const std::string& name) const;
+	// build mSearchSkinDirs without adding duplicates
+	void addSearchSkinDir(const std::string& skindir);
+
+	// Internal to findSkinnedFilenames()
+	template <typename FUNCTION>
+	void walkSearchSkinDirs(const std::string& subdir,
+							const std::vector<std::string>& subsubdirs,
+							const std::string& filename,
+							const FUNCTION& function) const;
+
+	std::string mAppName;               // install directory under progams/ ie "SecondLife"   
+	std::string mExecutablePathAndName; // full path + Filename of .exe
+	std::string mExecutableFilename;    // Filename of .exe
+	std::string mExecutableDir;	 	 // Location of executable
+	std::string mWorkingDir;	 	 // Current working directory
+	std::string mAppRODataDir;			 // Location for static app data
+	std::string mOSUserDir;			 // OS Specific user directory
+	std::string mOSUserAppDir;			 // OS Specific user app directory
+	std::string mLindenUserDir;		 // Location for Linden user-specific data
+	std::string mPerAccountChatLogsDir;		 // Location for chat logs.
+	std::string mChatLogsDir;		 // Location for chat logs.
+	std::string mCAFile;				 // Location of the TLS certificate authority PEM file.
+	std::string mTempDir;
+	std::string mCacheDir;			// cache directory as set by user preference
+	std::string mDefaultCacheDir;	// default cache diretory
+	std::string mOSCacheDir;		// operating system cache dir
+	std::string mDirDelimiter;
+	std::string mSkinName;           // caller-specified skin name
+	std::string mSkinBaseDir;			// Base for skins paths.
+	std::string mDefaultSkinDir;			// Location for default skin info.
+	std::string mSkinDir;			// Location for current skin info.
+	std::string mUserDefaultSkinDir;		// Location for default skin info.
+	std::string mUserSkinDir;			// Location for user-modified skin info.
+	// Skin directories to search, most general to most specific. This order
+	// works well for composing fine-grained files, in which an individual item
+	// in a specific file overrides the corresponding item in more general
+	// files. Of course, for a file-level search, iterate backwards.
+	std::vector<std::string> mSearchSkinDirs;
+	std::string mLanguage;              // Current viewer language
+	std::string mLLPluginDir;			// Location for plugins and plugin shell
+    static std::string sDumpDir;            // Per-run crash report subdir of log directory.
+	std::string mUserName;				// Current user name
+};
+
+void dir_exists_or_crash(const std::string &dir_name);
+
+extern LLDir *gDirUtilp;
+
+#endif // LL_LLDIR_H
diff --git a/indra/llfilesystem/lldir_linux.cpp b/indra/llfilesystem/lldir_linux.cpp
new file mode 100644
index 0000000000..80ad05345a
--- /dev/null
+++ b/indra/llfilesystem/lldir_linux.cpp
@@ -0,0 +1,269 @@
+/** 
+ * @file lldir_linux.cpp
+ * @brief Implementation of directory utilities for linux
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldir_linux.h"
+#include "llerror.h"
+#include "llrand.h"
+#include "llstring.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glob.h>
+#include <pwd.h>
+
+
+static std::string getCurrentUserHome(char* fallback)
+{
+	const uid_t uid = getuid();
+	struct passwd *pw;
+
+	pw = getpwuid(uid);
+	if ((pw != NULL) && (pw->pw_dir != NULL))
+	{
+		return pw->pw_dir;
+	}
+
+	LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL;
+	auto home_env = LLStringUtil::getoptenv("HOME");
+	if (home_env)
+	{
+		return *home_env;
+	}
+	else
+	{
+		LL_WARNS() << "Couldn't detect home directory!  Falling back to " << fallback << LL_ENDL;
+		return fallback;
+	}
+}
+
+
+LLDir_Linux::LLDir_Linux()
+{
+	mDirDelimiter = "/";
+	mCurrentDirIndex = -1;
+	mCurrentDirCount = -1;
+	mDirp = NULL;
+
+	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
+	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
+	{
+		strcpy(tmp_str, "/tmp");
+		LL_WARNS() << "Could not get current directory; changing to "
+				<< tmp_str << LL_ENDL;
+		if (chdir(tmp_str) == -1)
+		{
+			LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL;
+		}
+	}
+
+	mExecutableFilename = "";
+	mExecutablePathAndName = "";
+	mExecutableDir = tmp_str;
+	mWorkingDir = tmp_str;
+#ifdef APP_RO_DATA_DIR
+	mAppRODataDir = APP_RO_DATA_DIR;
+#else
+	mAppRODataDir = tmp_str;
+#endif
+    std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-linux-");
+    if (build_dir_pos != std::string::npos)
+    {
+		// ...we're in a dev checkout
+		mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins";
+		LL_INFOS() << "Running in dev checkout with mSkinBaseDir "
+		 << mSkinBaseDir << LL_ENDL;
+    }
+    else
+    {
+		// ...normal installation running
+		mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins";
+    }	
+
+	mOSUserDir = getCurrentUserHome(tmp_str);
+	mOSUserAppDir = "";
+	mLindenUserDir = "";
+
+	char path [32];	/* Flawfinder: ignore */ 
+
+	// *NOTE: /proc/%d/exe doesn't work on FreeBSD. But that's ok,
+	// because this is the linux implementation.
+
+	snprintf (path, sizeof(path), "/proc/%d/exe", (int) getpid ()); 
+	int rc = readlink (path, tmp_str, sizeof (tmp_str)-1);	/* Flawfinder: ignore */ 
+	if ( (rc != -1) && (rc <= ((int) sizeof (tmp_str)-1)) )
+	{
+		tmp_str[rc] = '\0'; //readlink() doesn't 0-terminate the buffer
+		mExecutablePathAndName = tmp_str;
+		char *path_end;
+		if ((path_end = strrchr(tmp_str,'/')))
+		{
+			*path_end = '\0';
+			mExecutableDir = tmp_str;
+			mWorkingDir = tmp_str;
+			mExecutableFilename = path_end+1;
+		}
+		else
+		{
+			mExecutableFilename = tmp_str;
+		}
+	}
+
+	mLLPluginDir = mExecutableDir + mDirDelimiter + "llplugin";
+
+	// *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something.
+	mTempDir = "/tmp";
+}
+
+LLDir_Linux::~LLDir_Linux()
+{
+}
+
+// Implementation
+
+
+void LLDir_Linux::initAppDirs(const std::string &app_name,
+							  const std::string& app_read_only_data_dir)
+{
+	// Allow override so test apps can read newview directory
+	if (!app_read_only_data_dir.empty())
+	{
+		mAppRODataDir = app_read_only_data_dir;
+		mSkinBaseDir = add(mAppRODataDir, "skins");
+	}
+	mAppName = app_name;
+
+	std::string upper_app_name(app_name);
+	LLStringUtil::toUpper(upper_app_name);
+
+	auto app_home_env(LLStringUtil::getoptenv(upper_app_name + "_USER_DIR"));
+	if (app_home_env)
+	{
+		// user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
+		mOSUserAppDir = *app_home_env;
+	}
+	else
+	{
+		// traditionally on unixoids, MyApp gets ~/.myapp dir for data
+		mOSUserAppDir = mOSUserDir;
+		mOSUserAppDir += "/";
+		mOSUserAppDir += ".";
+		std::string lower_app_name(app_name);
+		LLStringUtil::toLower(lower_app_name);
+		mOSUserAppDir += lower_app_name;
+	}
+
+	// create any directories we expect to write to.
+
+	int res = LLFile::mkdir(mOSUserAppDir);
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
+		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
+		mOSUserAppDir = mOSUserDir;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
+	}
+	
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
+	}
+
+	mCAFile = getExpandedFilename(LL_PATH_EXECUTABLE, "ca-bundle.crt");
+}
+
+U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+	U32 file_count = 0;
+	glob_t g;
+
+	std::string tmp_str;
+	tmp_str = dirname;
+	tmp_str += mask;
+	
+	if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+	{
+		file_count = g.gl_pathc;
+
+		globfree(&g);
+	}
+
+	return (file_count);
+}
+
+std::string LLDir_Linux::getCurPath()
+{
+	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
+	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
+	{
+		LL_WARNS() << "Could not get current directory" << LL_ENDL;
+		tmp_str[0] = '\0';
+	}
+	return tmp_str;
+}
+
+
+bool LLDir_Linux::fileExists(const std::string &filename) const
+{
+	struct stat stat_data;
+	// Check the age of the file
+	// Now, we see if the files we've gathered are recent...
+	int res = stat(filename.c_str(), &stat_data);
+	if (!res)
+	{
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+
+/*virtual*/ std::string LLDir_Linux::getLLPluginLauncher()
+{
+	return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
+		"SLPlugin";
+}
+
+/*virtual*/ std::string LLDir_Linux::getLLPluginFilename(std::string base_name)
+{
+	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
+		"lib" + base_name + ".so";
+}
diff --git a/indra/llfilesystem/lldir_linux.h b/indra/llfilesystem/lldir_linux.h
new file mode 100644
index 0000000000..e83a020ba4
--- /dev/null
+++ b/indra/llfilesystem/lldir_linux.h
@@ -0,0 +1,64 @@
+/** 
+ * @file lldir_linux.h
+ * @brief Definition of directory utilities class for linux
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#if !LL_LINUX
+#error This header must not be included when compiling for any target other than Linux. Consider including lldir.h instead.
+#endif // !LL_LINUX
+
+#ifndef LL_LLDIR_LINUX_H
+#define LL_LLDIR_LINUX_H
+
+#include "lldir.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+class LLDir_Linux : public LLDir
+{
+public:
+	LLDir_Linux();
+	virtual ~LLDir_Linux();
+
+	/*virtual*/ void initAppDirs(const std::string &app_name,
+		const std::string& app_read_only_data_dir);
+
+	virtual std::string getCurPath();
+	virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+	/*virtual*/ bool fileExists(const std::string &filename) const;
+
+	/*virtual*/ std::string getLLPluginLauncher();
+	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
+
+private:
+	DIR *mDirp;
+	int mCurrentDirIndex;
+	int mCurrentDirCount;
+	std::string mCurrentDir;
+};
+
+#endif // LL_LLDIR_LINUX_H
+
+
diff --git a/indra/llfilesystem/lldir_mac.cpp b/indra/llfilesystem/lldir_mac.cpp
new file mode 100644
index 0000000000..3bc4ee844e
--- /dev/null
+++ b/indra/llfilesystem/lldir_mac.cpp
@@ -0,0 +1,205 @@
+/** 
+ * @file lldir_mac.cpp
+ * @brief Implementation of directory utilities for Mac OS X
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */ 
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+
+#include "lldir_mac.h"
+#include "llerror.h"
+#include "llrand.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glob.h>
+#include <boost/filesystem.hpp>
+#include "lldir_utils_objc.h"
+
+// --------------------------------------------------------------------------------
+
+static bool CreateDirectory(const std::string &parent, 
+                            const std::string &child,
+                            std::string *fullname)
+{
+    
+    boost::filesystem::path p(parent);
+    p /= child;
+    
+    if (fullname)
+        *fullname = std::string(p.string());
+    
+    if (! boost::filesystem::create_directory(p))
+    {
+        return (boost::filesystem::is_directory(p));
+    }
+    return true;
+}
+
+// --------------------------------------------------------------------------------
+
+LLDir_Mac::LLDir_Mac()
+{
+	mDirDelimiter = "/";
+
+    const std::string     secondLifeString = "SecondLife";
+    
+    std::string *executablepathstr = getSystemExecutableFolder();
+
+    //NOTE:  LLINFOS/LLERRS will not output to log here.  The streams are not initialized.
+    
+	if (executablepathstr)
+	{
+		// mExecutablePathAndName
+		mExecutablePathAndName = *executablepathstr;
+        
+        boost::filesystem::path executablepath(*executablepathstr);
+        
+# ifndef BOOST_SYSTEM_NO_DEPRECATED
+#endif
+        mExecutableFilename = executablepath.filename().string();
+        mExecutableDir = executablepath.parent_path().string();
+		
+		// mAppRODataDir
+        std::string *resourcepath = getSystemResourceFolder();
+        mAppRODataDir = *resourcepath;
+		
+		// *NOTE: When running in a dev tree, use the copy of
+		// skins in indra/newview/ rather than in the application bundle.  This
+		// mirrors Windows dev environment behavior and allows direct checkin
+		// of edited skins/xui files. JC
+		
+		// MBW -- This keeps the mac application from finding other things.
+		// If this is really for skins, it should JUST apply to skins.
+        
+		std::string::size_type build_dir_pos = mExecutableDir.rfind("/build-darwin-");
+		if (build_dir_pos != std::string::npos)
+		{
+			// ...we're in a dev checkout
+			mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos)
+				+ "/indra/newview/skins";
+			LL_INFOS() << "Running in dev checkout with mSkinBaseDir "
+				<< mSkinBaseDir << LL_ENDL;
+		}
+		else
+		{
+			// ...normal installation running
+			mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins";
+		}
+		
+		// mOSUserDir
+        std::string *appdir = getSystemApplicationSupportFolder();
+        std::string rootdir;
+
+        //Create root directory
+        if (CreateDirectory(*appdir, secondLifeString, &rootdir))
+        {
+            
+            // Save the full path to the folder
+            mOSUserDir = rootdir;
+            
+            // Create our sub-dirs
+            CreateDirectory(rootdir, std::string("data"), NULL);
+            CreateDirectory(rootdir, std::string("logs"), NULL);
+            CreateDirectory(rootdir, std::string("user_settings"), NULL);
+            CreateDirectory(rootdir, std::string("browser_profile"), NULL);
+        }
+    
+		//mOSCacheDir
+        std::string *cachedir =  getSystemCacheFolder();
+
+        if (cachedir)
+		
+		{
+            mOSCacheDir = *cachedir;
+            //TODO:  This changes from ~/Library/Cache/Secondlife to ~/Library/Cache/com.app.secondlife/Secondlife.  Last dir level could go away.
+            CreateDirectory(mOSCacheDir, secondLifeString, NULL);
+		}
+		
+		// mOSUserAppDir
+		mOSUserAppDir = mOSUserDir;
+		
+		// mTempDir
+        //Aura 120920 boost::filesystem::temp_directory_path() not yet implemented on mac. :(
+        std::string *tmpdir = getSystemTempFolder();
+        if (tmpdir)
+        {
+            
+            CreateDirectory(*tmpdir, secondLifeString, &mTempDir);
+            if (tmpdir) delete tmpdir;
+        }
+		
+		mWorkingDir = getCurPath();
+
+		mLLPluginDir = mAppRODataDir + mDirDelimiter + "llplugin";
+	}
+}
+
+LLDir_Mac::~LLDir_Mac()
+{
+}
+
+// Implementation
+
+
+void LLDir_Mac::initAppDirs(const std::string &app_name,
+							const std::string& app_read_only_data_dir)
+{
+	// Allow override so test apps can read newview directory
+	if (!app_read_only_data_dir.empty())
+	{
+		mAppRODataDir = app_read_only_data_dir;
+		mSkinBaseDir = add(mAppRODataDir, "skins");
+	}
+	mCAFile = add(mAppRODataDir, "ca-bundle.crt");
+}
+
+std::string LLDir_Mac::getCurPath()
+{
+	return boost::filesystem::path( boost::filesystem::current_path() ).string();
+}
+
+
+
+bool LLDir_Mac::fileExists(const std::string &filename) const
+{
+    return boost::filesystem::exists(filename);
+}
+
+
+/*virtual*/ std::string LLDir_Mac::getLLPluginLauncher()
+{
+	return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() +
+		"SLPlugin.app/Contents/MacOS/SLPlugin";
+}
+
+/*virtual*/ std::string LLDir_Mac::getLLPluginFilename(std::string base_name)
+{
+	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
+		base_name + ".dylib";
+}
+
+
+#endif // LL_DARWIN
diff --git a/indra/llfilesystem/lldir_mac.h b/indra/llfilesystem/lldir_mac.h
new file mode 100644
index 0000000000..558727ebbc
--- /dev/null
+++ b/indra/llfilesystem/lldir_mac.h
@@ -0,0 +1,56 @@
+/** 
+ * @file lldir_mac.h
+ * @brief Definition of directory utilities class for Mac OS X
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */ 
+
+#if !LL_DARWIN
+#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead.
+#endif // !LL_DARWIN
+
+#ifndef LL_LLDIR_MAC_H
+#define LL_LLDIR_MAC_H
+
+#include "lldir.h"
+
+#include <dirent.h>
+
+class LLDir_Mac : public LLDir
+{
+public:
+	LLDir_Mac();
+	virtual ~LLDir_Mac();
+
+	/*virtual*/ void initAppDirs(const std::string &app_name,
+		const std::string& app_read_only_data_dir);
+
+	virtual std::string getCurPath();
+	virtual bool fileExists(const std::string &filename) const;
+
+	/*virtual*/ std::string getLLPluginLauncher();
+	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
+};
+
+#endif // LL_LLDIR_MAC_H
+
+
diff --git a/indra/llfilesystem/lldir_solaris.cpp b/indra/llfilesystem/lldir_solaris.cpp
new file mode 100644
index 0000000000..f18560ff20
--- /dev/null
+++ b/indra/llfilesystem/lldir_solaris.cpp
@@ -0,0 +1,266 @@
+/** 
+ * @file fmodwrapper.cpp
+ * @brief dummy source file for building a shared library to wrap libfmod.a
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldir_solaris.h"
+#include "llerror.h"
+#include "llrand.h"
+#include "llstring.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glob.h>
+#include <pwd.h>
+#include <sys/utsname.h>
+#define _STRUCTURED_PROC 1
+#include <sys/procfs.h>
+#include <fcntl.h>
+
+static std::string getCurrentUserHome(char* fallback)
+{
+	// fwiw this exactly duplicates getCurrentUserHome() in lldir_linux.cpp...
+	// we should either derive both from LLDir_Posix or just axe Solaris.
+	const uid_t uid = getuid();
+	struct passwd *pw;
+
+	pw = getpwuid(uid);
+	if ((pw != NULL) && (pw->pw_dir != NULL))
+	{
+		return pw->pw_dir;
+	}
+
+	LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL;
+	auto home_env = LLStringUtil::getoptenv("HOME");
+	if (home_env)
+	{
+		return *home_env;
+	}
+	else
+	{
+		LL_WARNS() << "Couldn't detect home directory!  Falling back to " << fallback << LL_ENDL;
+		return fallback;
+	}
+}
+
+
+LLDir_Solaris::LLDir_Solaris()
+{
+	mDirDelimiter = "/";
+	mCurrentDirIndex = -1;
+	mCurrentDirCount = -1;
+	mDirp = NULL;
+
+	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
+	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
+	{
+		strcpy(tmp_str, "/tmp");
+		LL_WARNS() << "Could not get current directory; changing to "
+				<< tmp_str << LL_ENDL;
+		if (chdir(tmp_str) == -1)
+		{
+			LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL;
+		}
+	}
+
+	mExecutableFilename = "";
+	mExecutablePathAndName = "";
+	mExecutableDir = strdup(tmp_str);
+	mWorkingDir = strdup(tmp_str);
+	mAppRODataDir = strdup(tmp_str);
+	mOSUserDir = getCurrentUserHome(tmp_str);
+	mOSUserAppDir = "";
+	mLindenUserDir = "";
+
+	char path [LL_MAX_PATH];	/* Flawfinder: ignore */ 
+
+	sprintf(path, "/proc/%d/psinfo", (int)getpid());
+	int proc_fd = -1;
+	if((proc_fd = open(path, O_RDONLY)) == -1){
+		LL_WARNS() << "unable to open " << path << LL_ENDL;
+		return;
+	}
+	psinfo_t proc_psinfo;
+	if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
+		LL_WARNS() << "Unable to read " << path << LL_ENDL;
+		close(proc_fd);
+		return;
+	}
+
+	close(proc_fd);
+
+	mExecutableFilename = strdup(proc_psinfo.pr_fname);
+	LL_INFOS() << "mExecutableFilename = [" << mExecutableFilename << "]" << LL_ENDL;
+
+	sprintf(path, "/proc/%d/path/a.out", (int)getpid());
+
+	char execpath[LL_MAX_PATH];
+	if(readlink(path, execpath, LL_MAX_PATH) == -1){
+		LL_WARNS() << "Unable to read link from " << path << LL_ENDL;
+		return;
+	}
+
+	char *p = execpath;			// nuke trash in link, if any exists
+	int i = 0;
+	while(*p != NULL && ++i < LL_MAX_PATH && isprint((int)(*p++)));
+	*p = NULL;
+
+	mExecutablePathAndName = strdup(execpath);
+	LL_INFOS() << "mExecutablePathAndName = [" << mExecutablePathAndName << "]" << LL_ENDL;
+
+	//NOTE: Why force people to cd into the package directory?
+	//      Look for SECONDLIFE env variable and use it, if set.
+
+	auto SECONDLIFE(LLDirUtil::getoptenv("SECONDLIFE"));
+	if(SECONDLIFE){
+		mExecutableDir = add(*SECONDLIFE, "bin"); //NOTE:  make sure we point at the bin
+	}else{
+		mExecutableDir = getDirName(execpath);
+		LL_INFOS() << "mExecutableDir = [" << mExecutableDir << "]" << LL_ENDL;
+	}
+
+	mLLPluginDir = add(mExecutableDir, "llplugin");
+
+	// *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something.
+	mTempDir = "/tmp";
+}
+
+LLDir_Solaris::~LLDir_Solaris()
+{
+}
+
+// Implementation
+
+
+void LLDir_Solaris::initAppDirs(const std::string &app_name,
+								const std::string& app_read_only_data_dir)
+{
+	// Allow override so test apps can read newview directory
+	if (!app_read_only_data_dir.empty())
+	{
+		mAppRODataDir = app_read_only_data_dir;
+		mSkinBaseDir = add(mAppRODataDir, "skins");
+	}
+	mAppName = app_name;
+
+	std::string upper_app_name(app_name);
+	LLStringUtil::toUpper(upper_app_name);
+
+	auto app_home_env(LLStringUtil::getoptenv(upper_app_name + "_USER_DIR"));
+	if (app_home_env)
+	{
+		// user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
+		mOSUserAppDir = *app_home_env;
+	}
+	else
+	{
+		// traditionally on unixoids, MyApp gets ~/.myapp dir for data
+		mOSUserAppDir = mOSUserDir;
+		mOSUserAppDir += "/";
+		mOSUserAppDir += ".";
+		std::string lower_app_name(app_name);
+		LLStringUtil::toLower(lower_app_name);
+		mOSUserAppDir += lower_app_name;
+	}
+
+	// create any directories we expect to write to.
+
+	int res = LLFile::mkdir(mOSUserAppDir);
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
+		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
+		mOSUserAppDir = mOSUserDir;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
+	}
+	
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
+	}
+
+	mCAFile = getExpandedFilename(LL_PATH_EXECUTABLE, "ca-bundle.crt");
+}
+
+U32 LLDir_Solaris::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+	U32 file_count = 0;
+	glob_t g;
+
+	std::string tmp_str;
+	tmp_str = dirname;
+	tmp_str += mask;
+	
+	if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+	{
+		file_count = g.gl_pathc;
+
+		globfree(&g);
+	}
+
+	return (file_count);
+}
+
+std::string LLDir_Solaris::getCurPath()
+{
+	char tmp_str[LL_MAX_PATH];	/* Flawfinder: ignore */ 
+	if (getcwd(tmp_str, LL_MAX_PATH) == NULL)
+	{
+		LL_WARNS() << "Could not get current directory" << LL_ENDL;
+		tmp_str[0] = '\0';
+	}
+	return tmp_str;
+}
+
+
+bool LLDir_Solaris::fileExists(const std::string &filename) const
+{
+	struct stat stat_data;
+	// Check the age of the file
+	// Now, we see if the files we've gathered are recent...
+	int res = stat(filename.c_str(), &stat_data);
+	if (!res)
+	{
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
diff --git a/indra/llfilesystem/lldir_solaris.h b/indra/llfilesystem/lldir_solaris.h
new file mode 100644
index 0000000000..c6dac57e14
--- /dev/null
+++ b/indra/llfilesystem/lldir_solaris.h
@@ -0,0 +1,61 @@
+/** 
+ * @file fmodwrapper.cpp
+ * @brief dummy source file for building a shared library to wrap libfmod.a
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#if !LL_SOLARIS
+#error This header must not be included when compiling for any target other than Solaris. Consider including lldir.h instead.
+#endif // !LL_SOLARIS
+
+#ifndef LL_LLDIR_SOLARIS_H
+#define LL_LLDIR_SOLARIS_H
+
+#include "lldir.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+class LLDir_Solaris : public LLDir
+{
+public:
+	LLDir_Solaris();
+	virtual ~LLDir_Solaris();
+
+	/*virtual*/ void initAppDirs(const std::string &app_name,
+		const std::string& app_read_only_data_dir);
+
+	virtual std::string getCurPath();
+	virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+	/*virtual*/ bool fileExists(const std::string &filename) const;
+
+private:
+	DIR *mDirp;
+	int mCurrentDirIndex;
+	int mCurrentDirCount;
+	std::string mCurrentDir;
+};
+
+#endif // LL_LLDIR_SOLARIS_H
+
+
diff --git a/indra/llfilesystem/lldir_utils_objc.h b/indra/llfilesystem/lldir_utils_objc.h
new file mode 100644
index 0000000000..12019c4284
--- /dev/null
+++ b/indra/llfilesystem/lldir_utils_objc.h
@@ -0,0 +1,43 @@
+/** 
+ * @file lldir_utils_objc.h
+ * @brief Definition of directory utilities class for Mac OS X
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */ 
+
+#if !LL_DARWIN
+#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead.
+#endif // !LL_DARWIN
+
+#ifndef LL_LLDIR_UTILS_OBJC_H
+#define LL_LLDIR_UTILS_OBJC_H
+
+#include <iostream>
+
+std::string* getSystemTempFolder();
+std::string* getSystemCacheFolder();
+std::string* getSystemApplicationSupportFolder();
+std::string* getSystemResourceFolder();
+std::string* getSystemExecutableFolder();
+
+
+#endif // LL_LLDIR_UTILS_OBJC_H
diff --git a/indra/llfilesystem/lldir_utils_objc.mm b/indra/llfilesystem/lldir_utils_objc.mm
new file mode 100644
index 0000000000..da55a2f897
--- /dev/null
+++ b/indra/llfilesystem/lldir_utils_objc.mm
@@ -0,0 +1,108 @@
+/** 
+ * @file lldir_utils_objc.mm
+ * @brief Cocoa implementation of directory utilities for Mac OS X
+ *
+ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2020, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */ 
+#if LL_DARWIN
+
+//WARNING:  This file CANNOT use standard linden includes due to conflicts between definitions of BOOL
+
+#include "lldir_utils_objc.h"
+#import <Cocoa/Cocoa.h>
+
+std::string* getSystemTempFolder()
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSString * tempDir = NSTemporaryDirectory();
+    if (tempDir == nil)
+        tempDir = @"/tmp";
+    std::string *result = ( new std::string([tempDir UTF8String]) );
+    [pool release];
+    
+    return result;
+}
+
+//findSystemDirectory scoped exclusively to this file. 
+std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
+                                   NSSearchPathDomainMask domainMask)
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    
+    std::string *result = nil;
+    NSString *path = nil;
+    
+    // Search for the path
+    NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
+                                                         domainMask,
+                                                         YES);
+    if ([paths count])
+    {
+        path = [paths objectAtIndex:0];
+        //HACK:  Always attempt to create directory, ignore errors.
+        NSError *error = nil;
+
+        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
+
+        
+        result = new std::string([path UTF8String]);        
+    }
+    [pool release];
+    return result;
+}
+
+std::string* getSystemExecutableFolder()
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    NSString *bundlePath = [[NSBundle mainBundle] executablePath];
+    std::string *result = (new std::string([bundlePath UTF8String]));  
+    [pool release];
+
+    return result;
+}
+
+std::string* getSystemResourceFolder()
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
+    std::string *result = (new std::string([bundlePath UTF8String]));
+    [pool release];
+    
+    return result;
+}
+
+std::string* getSystemCacheFolder()
+{
+    return findSystemDirectory (NSCachesDirectory,
+                                NSUserDomainMask);
+}
+
+std::string* getSystemApplicationSupportFolder()
+{
+    return findSystemDirectory (NSApplicationSupportDirectory,
+                                NSUserDomainMask);
+    
+}
+
+#endif // LL_DARWIN
diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp
new file mode 100644
index 0000000000..b3b3afb37e
--- /dev/null
+++ b/indra/llfilesystem/lldir_win32.cpp
@@ -0,0 +1,452 @@
+/** 
+ * @file lldir_win32.cpp
+ * @brief Implementation of directory utilities for windows
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#if LL_WINDOWS
+
+#include "linden_common.h"
+
+#include "lldir_win32.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "stringize.h"
+#include "llfile.h"
+#include <shlobj.h>
+#include <fstream>
+
+#include <direct.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// Utility stuff to get versions of the sh
+#define PACKVERSION(major,minor) MAKELONG(minor,major)
+DWORD GetDllVersion(LPCTSTR lpszDllName);
+
+namespace
+{ // anonymous
+    enum class prst { INIT, OPEN, SKIP } state = prst::INIT;
+    // This is called so early that we can't count on static objects being
+    // properly constructed yet, so declare a pointer instead of an instance.
+    std::ofstream* prelogf = nullptr;
+
+    void prelog(const std::string& message)
+    {
+        boost::optional<std::string> prelog_name;
+
+        switch (state)
+        {
+        case prst::INIT:
+            // assume we failed, until we succeed
+            state = prst::SKIP;
+
+            prelog_name = LLStringUtil::getoptenv("PRELOG");
+            if (! prelog_name)
+                // no PRELOG variable set, carry on
+                return;
+            prelogf = new llofstream(*prelog_name, std::ios_base::app);
+            if (! (prelogf && prelogf->is_open()))
+                // can't complain to anybody; how?
+                return;
+            // got the log file open, cool!
+            state = prst::OPEN;
+            (*prelogf) << "========================================================================"
+                       << std::endl;
+            // fall through, don't break
+
+        case prst::OPEN:
+            (*prelogf) << message << std::endl;
+            break;
+
+        case prst::SKIP:
+            // either PRELOG isn't set, or we failed to open that pathname
+            break;
+        }
+    }
+} // anonymous namespace
+
+#define PRELOG(expression) prelog(STRINGIZE(expression))
+
+LLDir_Win32::LLDir_Win32()
+{
+	// set this first: used by append() and add() methods
+	mDirDelimiter = "\\";
+
+	WCHAR w_str[MAX_PATH];
+	// Application Data is where user settings go. We rely on $APPDATA being
+	// correct.
+	auto APPDATA = LLStringUtil::getoptenv("APPDATA");
+	if (APPDATA)
+	{
+		mOSUserDir = *APPDATA;
+	}
+	PRELOG("APPDATA='" << mOSUserDir << "'");
+	// On Windows, we could have received a plain-ASCII pathname in which
+	// non-ASCII characters have been munged to '?', or the pathname could
+	// have been badly encoded and decoded such that we now have garbage
+	// instead of a valid path. Check that mOSUserDir actually exists.
+	if (mOSUserDir.empty() || ! fileExists(mOSUserDir))
+	{
+		PRELOG("APPDATA does not exist");
+		//HRESULT okay = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, w_str);
+		wchar_t *pwstr = NULL;
+		HRESULT okay = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &pwstr);
+		PRELOG("SHGetKnownFolderPath(FOLDERID_RoamingAppData) returned " << okay);
+		if (SUCCEEDED(okay) && pwstr)
+		{
+			// But of course, only update mOSUserDir if SHGetKnownFolderPath() works.
+			mOSUserDir = ll_convert_wide_to_string(pwstr);
+			// Not only that: update our environment so that child processes
+			// will see a reasonable value as well.
+			_wputenv_s(L"APPDATA", pwstr);
+			// SHGetKnownFolderPath() contract requires us to free pwstr
+			CoTaskMemFree(pwstr);
+			PRELOG("mOSUserDir='" << mOSUserDir << "'");
+		}
+	}
+
+	// We want cache files to go on the local disk, even if the
+	// user is on a network with a "roaming profile".
+	//
+	// On Vista this is:
+	//   C:\Users\James\AppData\Local
+	//
+	// We used to store the cache in AppData\Roaming, and the installer
+	// cleans up that version on upgrade.  JC
+	auto LOCALAPPDATA = LLStringUtil::getoptenv("LOCALAPPDATA");
+	if (LOCALAPPDATA)
+	{
+		mOSCacheDir = *LOCALAPPDATA;
+	}
+	PRELOG("LOCALAPPDATA='" << mOSCacheDir << "'");
+	// Windows really does not deal well with pathnames containing non-ASCII
+	// characters. See above remarks about APPDATA.
+	if (mOSCacheDir.empty() || ! fileExists(mOSCacheDir))
+	{
+		PRELOG("LOCALAPPDATA does not exist");
+		//HRESULT okay = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, w_str);
+		wchar_t *pwstr = NULL;
+		HRESULT okay = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &pwstr);
+		PRELOG("SHGetKnownFolderPath(FOLDERID_LocalAppData) returned " << okay);
+		if (SUCCEEDED(okay) && pwstr)
+		{
+			// But of course, only update mOSCacheDir if SHGetKnownFolderPath() works.
+			mOSCacheDir = ll_convert_wide_to_string(pwstr);
+			// Update our environment so that child processes will see a
+			// reasonable value as well.
+			_wputenv_s(L"LOCALAPPDATA", pwstr);
+			// SHGetKnownFolderPath() contract requires us to free pwstr
+			CoTaskMemFree(pwstr);
+			PRELOG("mOSCacheDir='" << mOSCacheDir << "'");
+		}
+	}
+
+	if (GetTempPath(MAX_PATH, w_str))
+	{
+		if (wcslen(w_str))	/* Flawfinder: ignore */ 
+		{
+			w_str[wcslen(w_str)-1] = '\0'; /* Flawfinder: ignore */ // remove trailing slash
+		}
+		mTempDir = utf16str_to_utf8str(llutf16string(w_str));
+
+		if (mOSUserDir.empty())
+		{
+			mOSUserDir = mTempDir;
+		}
+
+		if (mOSCacheDir.empty())
+		{
+			mOSCacheDir = mTempDir;
+		}
+	}
+	else
+	{
+		mTempDir = mOSUserDir;
+	}
+
+/*==========================================================================*|
+	// Now that we've got mOSUserDir, one way or another, let's see how we did
+	// with our environment variables.
+	{
+		auto report = [this](std::ostream& out){
+			out << "mOSUserDir  = '" << mOSUserDir  << "'\n"
+				<< "mOSCacheDir = '" << mOSCacheDir << "'\n"
+				<< "mTempDir    = '" << mTempDir    << "'" << std::endl;
+		};
+		int res = LLFile::mkdir(mOSUserDir);
+		if (res == -1)
+		{
+			// If we couldn't even create the directory, just blurt to stderr
+			report(std::cerr);
+		}
+		else
+		{
+			// successfully created logdir, plunk a log file there
+			std::string logfilename(add(mOSUserDir, "lldir.log"));
+			std::ofstream logfile(logfilename.c_str());
+			if (! logfile.is_open())
+			{
+				report(std::cerr);
+			}
+			else
+			{
+				report(logfile);
+			}
+		}
+	}
+|*==========================================================================*/
+
+//	fprintf(stderr, "mTempDir = <%s>",mTempDir);
+
+	// Set working directory, for LLDir::getWorkingDir()
+	GetCurrentDirectory(MAX_PATH, w_str);
+	mWorkingDir = utf16str_to_utf8str(llutf16string(w_str));
+
+	// Set the executable directory
+	S32 size = GetModuleFileName(NULL, w_str, MAX_PATH);
+	if (size)
+	{
+		w_str[size] = '\0';
+		mExecutablePathAndName = utf16str_to_utf8str(llutf16string(w_str));
+		S32 path_end = mExecutablePathAndName.find_last_of('\\');
+		if (path_end != std::string::npos)
+		{
+			mExecutableDir = mExecutablePathAndName.substr(0, path_end);
+			mExecutableFilename = mExecutablePathAndName.substr(path_end+1, std::string::npos);
+		}
+		else
+		{
+			mExecutableFilename = mExecutablePathAndName;
+		}
+
+	}
+	else
+	{
+		fprintf(stderr, "Couldn't get APP path, assuming current directory!");
+		mExecutableDir = mWorkingDir;
+		// Assume it's the current directory
+	}
+
+	// mAppRODataDir = ".";	
+
+	// Determine the location of the App-Read-Only-Data
+	// Try the working directory then the exe's dir.
+	mAppRODataDir = mWorkingDir;	
+
+
+//	if (mExecutableDir.find("indra") == std::string::npos)
+	
+	// *NOTE:Mani - It is a mistake to put viewer specific code in
+	// the LLDir implementation. The references to 'skins' and 
+	// 'llplugin' need to go somewhere else.
+	// alas... this also gets called during static initialization 
+	// time due to the construction of gDirUtil in lldir.cpp.
+	if(! LLFile::isdir(add(mAppRODataDir, "skins")))
+	{
+		// What? No skins in the working dir?
+		// Try the executable's directory.
+		mAppRODataDir = mExecutableDir;
+	}
+
+//	LL_INFOS() << "mAppRODataDir = " << mAppRODataDir << LL_ENDL;
+
+	mSkinBaseDir = add(mAppRODataDir, "skins");
+
+	// Build the default cache directory
+	mDefaultCacheDir = buildSLOSCacheDir();
+	
+	// Make sure it exists
+	int res = LLFile::mkdir(mDefaultCacheDir);
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << mDefaultCacheDir << LL_ENDL;
+	}
+
+	mLLPluginDir = add(mExecutableDir, "llplugin");
+}
+
+LLDir_Win32::~LLDir_Win32()
+{
+}
+
+// Implementation
+
+void LLDir_Win32::initAppDirs(const std::string &app_name,
+							  const std::string& app_read_only_data_dir)
+{
+	// Allow override so test apps can read newview directory
+	if (!app_read_only_data_dir.empty())
+	{
+		mAppRODataDir = app_read_only_data_dir;
+		mSkinBaseDir = add(mAppRODataDir, "skins");
+	}
+	mAppName = app_name;
+	mOSUserAppDir = add(mOSUserDir, app_name);
+
+	int res = LLFile::mkdir(mOSUserAppDir);
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL;
+		LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL;
+		mOSUserAppDir = mOSUserDir;
+	}
+	//dumpCurrentDirectories();
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL;
+	}
+
+	res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,""));
+	if (res == -1)
+	{
+		LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL;
+	}
+
+	mCAFile = getExpandedFilename( LL_PATH_EXECUTABLE, "ca-bundle.crt" );
+}
+
+U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+	HANDLE count_search_h;
+	U32 file_count;
+
+	file_count = 0;
+
+	WIN32_FIND_DATA FileData;
+
+	llutf16string pathname = utf8str_to_utf16str(dirname);
+	pathname += utf8str_to_utf16str(mask);
+	
+	if ((count_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)   
+	{
+		file_count++;
+
+		while (FindNextFile(count_search_h, &FileData))
+		{
+			file_count++;
+		}
+		   
+		FindClose(count_search_h);
+	}
+
+	return (file_count);
+}
+
+std::string LLDir_Win32::getCurPath()
+{
+	WCHAR w_str[MAX_PATH];
+	GetCurrentDirectory(MAX_PATH, w_str);
+
+	return utf16str_to_utf8str(llutf16string(w_str));
+}
+
+
+bool LLDir_Win32::fileExists(const std::string &filename) const
+{
+	llstat stat_data;
+	// Check the age of the file
+	// Now, we see if the files we've gathered are recent...
+	int res = LLFile::stat(filename, &stat_data);
+	if (!res)
+	{
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+
+/*virtual*/ std::string LLDir_Win32::getLLPluginLauncher()
+{
+	return gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() +
+		"SLPlugin.exe";
+}
+
+/*virtual*/ std::string LLDir_Win32::getLLPluginFilename(std::string base_name)
+{
+	return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() +
+		base_name + ".dll";
+}
+
+
+#if 0
+// Utility function to get version number of a DLL
+
+#define PACKVERSION(major,minor) MAKELONG(minor,major)
+
+DWORD GetDllVersion(LPCTSTR lpszDllName)
+{
+
+    HINSTANCE hinstDll;
+    DWORD dwVersion = 0;
+
+    hinstDll = LoadLibrary(lpszDllName);	/* Flawfinder: ignore */ 
+	
+    if(hinstDll)
+    {
+        DLLGETVERSIONPROC pDllGetVersion;
+
+        pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");
+
+/*Because some DLLs might not implement this function, you
+  must test for it explicitly. Depending on the particular 
+  DLL, the lack of a DllGetVersion function can be a useful
+  indicator of the version.
+*/
+        if(pDllGetVersion)
+        {
+            DLLVERSIONINFO dvi;
+            HRESULT hr;
+
+            ZeroMemory(&dvi, sizeof(dvi));
+            dvi.cbSize = sizeof(dvi);
+
+            hr = (*pDllGetVersion)(&dvi);
+
+            if(SUCCEEDED(hr))
+            {
+                dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
+            }
+        }
+        
+        FreeLibrary(hinstDll);
+    }
+    return dwVersion;
+}
+#endif
+
+#endif
+
+
diff --git a/indra/llfilesystem/lldir_win32.h b/indra/llfilesystem/lldir_win32.h
new file mode 100644
index 0000000000..450efaf9da
--- /dev/null
+++ b/indra/llfilesystem/lldir_win32.h
@@ -0,0 +1,59 @@
+/** 
+ * @file lldir_win32.h
+ * @brief Definition of directory utilities class for windows
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#if !LL_WINDOWS
+#error This header must not be included when compiling for any target other than Windows. Consider including lldir.h instead.
+#endif // !LL_WINDOWS
+
+#ifndef LL_LLDIR_WIN32_H
+#define LL_LLDIR_WIN32_H
+
+#include "lldir.h"
+
+class LLDir_Win32 : public LLDir
+{
+public:
+	LLDir_Win32();
+	virtual ~LLDir_Win32();
+
+	/*virtual*/ void initAppDirs(const std::string &app_name,
+		const std::string& app_read_only_data_dir);
+
+	/*virtual*/ std::string getCurPath();
+	/*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+	/*virtual*/ bool fileExists(const std::string &filename) const;
+
+	/*virtual*/ std::string getLLPluginLauncher();
+	/*virtual*/ std::string getLLPluginFilename(std::string base_name);
+
+private:
+	void* mDirSearch_h;
+	llutf16string mCurrentDir;
+};
+
+#endif // LL_LLDIR_WIN32_H
+
+
diff --git a/indra/llfilesystem/lldirguard.h b/indra/llfilesystem/lldirguard.h
new file mode 100644
index 0000000000..37b9e9b83e
--- /dev/null
+++ b/indra/llfilesystem/lldirguard.h
@@ -0,0 +1,72 @@
+/** 
+ * @file lldirguard.h
+ * @brief Protect working directory from being changed in scope.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_DIRGUARD_H
+#define LL_DIRGUARD_H
+
+#include "linden_common.h"
+#include "llerror.h"
+
+#if LL_WINDOWS
+class LLDirectoryGuard
+{
+public:
+	LLDirectoryGuard()
+	{
+		mOrigDirLen = GetCurrentDirectory(MAX_PATH, mOrigDir);
+	}
+
+	~LLDirectoryGuard()
+	{
+		mFinalDirLen = GetCurrentDirectory(MAX_PATH, mFinalDir);
+		if ((mOrigDirLen!=mFinalDirLen) ||
+			(wcsncmp(mOrigDir,mFinalDir,mOrigDirLen)!=0))
+		{
+			// Dir has changed
+			std::string mOrigDirUtf8 = utf16str_to_utf8str(llutf16string(mOrigDir));
+			std::string mFinalDirUtf8 = utf16str_to_utf8str(llutf16string(mFinalDir));
+			LL_INFOS() << "Resetting working dir from " << mFinalDirUtf8 << " to " << mOrigDirUtf8 << LL_ENDL;
+			SetCurrentDirectory(mOrigDir);
+		}
+	}
+
+private:
+	TCHAR mOrigDir[MAX_PATH];
+	DWORD mOrigDirLen;
+	TCHAR mFinalDir[MAX_PATH];
+	DWORD mFinalDirLen;
+};
+#else // No-op outside Windows.
+class LLDirectoryGuard
+{
+public:
+	LLDirectoryGuard() {}
+	~LLDirectoryGuard() {}
+};
+#endif 
+
+
+#endif
diff --git a/indra/llfilesystem/lldiriterator.cpp b/indra/llfilesystem/lldiriterator.cpp
new file mode 100644
index 0000000000..3eb64e69d9
--- /dev/null
+++ b/indra/llfilesystem/lldiriterator.cpp
@@ -0,0 +1,243 @@
+/**
+ * @file lldiriterator.cpp
+ * @brief Iterator through directory entries matching the search pattern.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "lldiriterator.h"
+
+#include "fix_macros.h"
+#include <boost/filesystem.hpp>
+#include <boost/regex.hpp>
+
+namespace fs = boost::filesystem;
+
+static std::string glob_to_regex(const std::string& glob);
+
+class LLDirIterator::Impl
+{
+public:
+	Impl(const std::string &dirname, const std::string &mask);
+	~Impl();
+
+	bool next(std::string &fname);
+
+private:
+	boost::regex			mFilterExp;
+	fs::directory_iterator	mIter;
+	bool					mIsValid;
+};
+
+LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask)
+	: mIsValid(false)
+{
+#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
+	fs::path dir_path(utf8str_to_utf16str(dirname));
+#else
+	fs::path dir_path(dirname);
+#endif
+
+	bool is_dir = false;
+
+	// Check if path is a directory.
+	try
+	{
+		is_dir = fs::is_directory(dir_path);
+	}
+	catch (const fs::filesystem_error& e)
+	{
+		LL_WARNS() << e.what() << LL_ENDL;
+		return;
+	}
+
+	if (!is_dir)
+	{
+		LL_WARNS() << "Invalid path: \"" << dir_path.string() << "\"" << LL_ENDL;
+		return;
+	}
+
+	// Initialize the directory iterator for the given path.
+	try
+	{
+		mIter = fs::directory_iterator(dir_path);
+	}
+	catch (const fs::filesystem_error& e)
+	{
+		LL_WARNS() << e.what() << LL_ENDL;
+		return;
+	}
+
+	// Convert the glob mask to a regular expression
+	std::string exp = glob_to_regex(mask);
+
+	// Initialize boost::regex with the expression converted from
+	// the glob mask.
+	// An exception is thrown if the expression is not valid.
+	try
+	{
+		mFilterExp.assign(exp);
+	}
+	catch (boost::regex_error& e)
+	{
+		LL_WARNS() << "\"" << exp << "\" is not a valid regular expression: "
+				<< e.what() << LL_ENDL;
+		return;
+	}
+
+	mIsValid = true;
+}
+
+LLDirIterator::Impl::~Impl()
+{
+}
+
+bool LLDirIterator::Impl::next(std::string &fname)
+{
+	fname = "";
+
+	if (!mIsValid)
+	{
+		LL_WARNS() << "The iterator is not correctly initialized." << LL_ENDL;
+		return false;
+	}
+
+	fs::directory_iterator end_itr; // default construction yields past-the-end
+	bool found = false;
+
+	// Check if path is a directory.
+	try
+	{
+		while (mIter != end_itr && !found)
+		{
+			boost::smatch match;
+			std::string name = mIter->path().filename().string();
+			found = boost::regex_match(name, match, mFilterExp);
+			if (found)
+			{
+				fname = name;
+			}
+
+			++mIter;
+		}
+	}
+	catch (const fs::filesystem_error& e)
+	{
+		LL_WARNS() << e.what() << LL_ENDL;
+	}
+
+	return found;
+}
+
+/**
+Converts the incoming glob into a regex. This involves
+converting incoming glob expressions to regex equivilents and
+at the same time, escaping any regex meaningful characters which
+do not have glob meaning, i.e.
+            .()+|^$ 
+in the input.
+*/
+std::string glob_to_regex(const std::string& glob)
+{
+	std::string regex;
+	regex.reserve(glob.size()<<1);
+	S32 braces = 0;
+	bool escaped = false;
+	bool square_brace_open = false;
+
+	for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i)
+	{
+		char c = *i;
+
+		switch (c)
+		{
+			case '*':
+				if (glob.begin() == i)
+				{
+					regex+="[^.].*";
+				}
+				else
+				{
+					regex+= escaped ? "*" : ".*";
+				}
+				break;
+			case '?':
+				regex+= escaped ? '?' : '.';
+				break;
+			case '{':
+				braces++;
+				regex+='(';
+				break;
+			case '}':
+				if (!braces)
+				{
+					LL_ERRS() << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << LL_ENDL;
+				}
+
+				regex+=')';
+				braces--;
+				break;
+			case ',':
+				regex+= braces ? '|' : c;
+				break;
+			case '!':
+				regex+= square_brace_open ? '^' : c;
+				break;
+			case '.': // This collection have different regex meaning
+			case '^': // and so need escaping.
+			case '(': 
+			case ')':
+			case '+':
+			case '|':
+			case '$':
+				regex += '\\'; 
+			default:
+				regex += c;
+				break;
+		}
+
+		escaped = ('\\' == c);
+		square_brace_open = ('[' == c);
+	}
+
+	if (braces)
+	{
+		LL_ERRS() << "glob_to_regex: Unterminated brace expression: " << glob << LL_ENDL;
+	}
+
+	return regex;
+}
+
+LLDirIterator::LLDirIterator(const std::string &dirname, const std::string &mask)
+{
+	mImpl = new Impl(dirname, mask);
+}
+
+LLDirIterator::~LLDirIterator()
+{
+	delete mImpl;
+}
+
+bool LLDirIterator::next(std::string &fname)
+{
+	return mImpl->next(fname);
+}
diff --git a/indra/llfilesystem/lldiriterator.h b/indra/llfilesystem/lldiriterator.h
new file mode 100644
index 0000000000..0b48be41b3
--- /dev/null
+++ b/indra/llfilesystem/lldiriterator.h
@@ -0,0 +1,87 @@
+/**
+ * @file lldiriterator.h
+ * @brief Iterator through directory entries matching the search pattern.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDIRITERATOR_H
+#define LL_LLDIRITERATOR_H
+
+#include "linden_common.h"
+
+/**
+ * Class LLDirIterator
+ *
+ * Iterates through directory entries matching the search pattern.
+ */
+class LLDirIterator
+{
+public:
+	/**
+	 * Constructs LLDirIterator object to search for glob pattern
+	 * matches in a directory.
+	 *
+	 * @param dirname - name of a directory to search in.
+	 * @param mask - search pattern, a glob expression
+	 *
+	 * Wildcards supported in glob expressions:
+	 * --------------------------------------------------------------
+	 * | Wildcard 	| Matches										|
+	 * --------------------------------------------------------------
+	 * | 	* 		|zero or more characters						|
+	 * | 	?		|exactly one character							|
+	 * | [abcde]	|exactly one character listed					|
+	 * | [a-e]		|exactly one character in the given range		|
+	 * | [!abcde]	|any character that is not listed				|
+	 * | [!a-e]		|any character that is not in the given range	|
+	 * | {abc,xyz}	|exactly one entire word in the options given	|
+	 * --------------------------------------------------------------
+	 */
+	LLDirIterator(const std::string &dirname, const std::string &mask);
+
+	~LLDirIterator();
+
+	/**
+	 * Searches for the next directory entry matching the glob mask
+	 * specified upon iterator construction.
+	 * Returns true if a match is found, sets fname
+	 * parameter to the name of the matched directory entry and
+	 * increments the iterator position.
+	 *
+	 * Typical usage:
+	 * <code>
+	 * LLDirIterator iter(directory, pattern);
+	 * if ( iter.next(scanResult) )
+	 * </code>
+	 *
+	 * @param fname - name of the matched directory entry.
+	 * @return true if a match is found, false otherwise.
+	 */
+	bool next(std::string &fname);
+
+protected:
+	class Impl;
+	Impl* mImpl;
+};
+
+#endif //LL_LLDIRITERATOR_H
diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp
new file mode 100644
index 0000000000..af93049e07
--- /dev/null
+++ b/indra/llfilesystem/lldiskcache.cpp
@@ -0,0 +1,387 @@
+/** 
+ * @file lldiskcache.cpp
+ * @brief Implementation of virtual file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lldiskcache.h"
+
+#include "llerror.h"
+#include "llthread.h"
+#include "lltimer.h"
+#include "llfasttimer.h"
+#include "llmemory.h"
+
+#include <fstream>
+#include "lldir.h"
+
+const S32 LLDiskCache::READ			= 0x00000001;
+const S32 LLDiskCache::WRITE		= 0x00000002;
+const S32 LLDiskCache::READ_WRITE	= 0x00000003;  // LLDiskCache::READ & LLDiskCache::WRITE
+const S32 LLDiskCache::APPEND		= 0x00000006;  // 0x00000004 & LLDiskCache::WRITE
+
+static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait");
+
+LLDiskCache::LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
+{
+	mFileType =	file_type;
+	mFileID = file_id;
+	mPosition = 0;
+    mBytesRead = 0;
+    mReadComplete = FALSE;
+	mMode = mode;
+}
+
+LLDiskCache::~LLDiskCache()
+{
+}
+
+const std::string assetTypeToString(LLAssetType::EType at)
+{
+    /**
+     * Make use of the C++17 (or is it 14) feature that allows
+     * for inline initialization of an std::map<>
+     */
+    typedef std::map<LLAssetType::EType, std::string> asset_type_to_name_t;
+    asset_type_to_name_t asset_type_to_name =
+    {
+        { LLAssetType::AT_TEXTURE, "TEXTURE" },
+        { LLAssetType::AT_SOUND, "SOUND" },
+        { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" },
+        { LLAssetType::AT_LANDMARK, "LANDMARK" },
+        { LLAssetType::AT_SCRIPT, "SCRIPT" },
+        { LLAssetType::AT_CLOTHING, "CLOTHING" },
+        { LLAssetType::AT_OBJECT, "OBJECT" },
+        { LLAssetType::AT_NOTECARD, "NOTECARD" },
+        { LLAssetType::AT_CATEGORY, "CATEGORY" },
+        { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" },
+        { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" },
+        { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" },
+        { LLAssetType::AT_BODYPART, "BODYPART" },
+        { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" },
+        { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" },
+        { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" },
+        { LLAssetType::AT_ANIMATION, "ANIMATION" },
+        { LLAssetType::AT_GESTURE, "GESTURE" },
+        { LLAssetType::AT_SIMSTATE, "SIMSTATE" },
+        { LLAssetType::AT_LINK, "LINK" },
+        { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" },
+        { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" },
+        { LLAssetType::AT_WIDGET, "WIDGET" },
+        { LLAssetType::AT_PERSON, "PERSON" },
+        { LLAssetType::AT_MESH, "MESH" },
+        { LLAssetType::AT_SETTINGS, "SETTINGS" },
+        { LLAssetType::AT_UNKNOWN, "UNKNOWN" }
+    };
+
+    asset_type_to_name_t::iterator iter = asset_type_to_name.find(at);
+    if (iter != asset_type_to_name.end())
+    {
+        return iter->second;
+    }
+
+    return std::string("UNKNOWN");
+}
+
+const std::string idToFilepath(const std::string id, LLAssetType::EType at)
+{
+    /**
+     * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of
+     * course,  will be greatly expanded upon
+     */
+    std::ostringstream ss;
+    ss << "00cache_";
+    ss << id;
+    ss << "_";
+    ss << assetTypeToString(at);
+    ss << ".txt";
+
+    const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str());
+
+    return filepath;
+}
+
+// static
+bool LLDiskCache::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+	std::string id_str;
+	file_id.toString(id_str);
+	const std::string filename = idToFilepath(id_str, file_type);
+
+	std::ifstream file(filename, std::ios::binary);
+	if (file.is_open())
+	{
+		file.seekg(0, std::ios::end);
+		return file.tellg() > 0;
+	}
+	return false;
+}
+
+// static
+bool LLDiskCache::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+    std::string id_str;
+    file_id.toString(id_str);
+    const std::string filename = idToFilepath(id_str, file_type);
+    
+    std::remove(filename.c_str());
+
+    return true;
+}
+
+// static
+bool LLDiskCache::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type,
+                         const LLUUID &new_file_id, const LLAssetType::EType new_file_type)
+{
+    std::string old_id_str;
+    old_file_id.toString(old_id_str);
+    const std::string old_filename = idToFilepath(old_id_str, old_file_type);
+
+    std::string new_id_str;
+    new_file_id.toString(new_id_str);
+    const std::string new_filename = idToFilepath(new_id_str, new_file_type);
+
+    if (std::rename(old_filename.c_str(), new_filename.c_str()))
+    {
+        // We would like to return FALSE here indicating the operation
+        // failed but the original code does not and doing so seems to
+        // break a lot of things so we go with the flow... 
+        //return FALSE;
+    }
+
+    return TRUE;
+}
+
+// static
+S32 LLDiskCache::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+    std::string id_str;
+    file_id.toString(id_str);
+    const std::string filename = idToFilepath(id_str, file_type);
+
+    S32 file_size = 0;
+    std::ifstream file(filename, std::ios::binary);
+    if (file.is_open())
+    {
+        file.seekg(0, std::ios::end);
+        file_size = file.tellg();
+    }
+
+    return file_size;
+}
+
+BOOL LLDiskCache::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
+{
+	BOOL success = TRUE;
+
+    mReadComplete = FALSE;
+
+    std::string id;
+    mFileID.toString(id);
+    const std::string filename = idToFilepath(id, mFileType);
+
+    std::ifstream file(filename, std::ios::binary);
+    if (file.is_open())
+    {
+        file.seekg(mPosition, std::ios::beg);
+
+        file.read((char*)buffer, bytes);
+
+        if (file)
+        {
+            mBytesRead = bytes;
+        }
+        else
+        {
+            mBytesRead = file.gcount();
+        }
+
+        file.close();
+
+        mPosition += mBytesRead;
+        if (!mBytesRead)
+        {
+            success = FALSE;
+        }
+
+        mReadComplete = TRUE;
+    }
+
+    return success;
+}
+
+BOOL LLDiskCache::isReadComplete()
+{
+    if (mReadComplete)
+    {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+S32 LLDiskCache::getLastBytesRead()
+{
+	return mBytesRead;
+}
+
+BOOL LLDiskCache::eof()
+{
+	return mPosition >= getSize();
+}
+
+BOOL LLDiskCache::write(const U8 *buffer, S32 bytes)
+{
+    std::string id_str;
+    mFileID.toString(id_str);
+    const std::string filename = idToFilepath(id_str, mFileType);
+
+    BOOL success = FALSE;
+
+    if (mMode == APPEND)
+    {
+        std::ofstream ofs(filename, std::ios::app | std::ios::binary);
+        if (ofs)
+        {
+            ofs.write((const char*)buffer, bytes);
+
+            success = TRUE;
+        }
+    }
+    else
+    {
+        std::ofstream ofs(filename, std::ios::binary);
+        if (ofs)
+        {
+            ofs.write((const char*)buffer, bytes);
+
+            mPosition += bytes;
+
+            success = TRUE;
+        }
+    }
+
+    return success;
+}
+
+//static
+BOOL LLDiskCache::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type)
+{
+	LLDiskCache file(uuid, type, LLDiskCache::WRITE);
+	file.setMaxSize(bytes);
+	return file.write(buffer, bytes);
+}
+
+BOOL LLDiskCache::seek(S32 offset, S32 origin)
+{
+	if (-1 == origin)
+	{
+		origin = mPosition;
+	}
+
+	S32 new_pos = origin + offset;
+
+	S32 size = getSize();
+
+	if (new_pos > size)
+	{
+		LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL;
+
+		mPosition = size;
+		return FALSE;
+	}
+	else if (new_pos < 0)
+	{
+		LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL;
+
+		mPosition = 0;
+		return FALSE;
+	}
+
+	mPosition = new_pos;
+	return TRUE;
+}
+
+S32 LLDiskCache::tell() const
+{
+	return mPosition;
+}
+
+S32 LLDiskCache::getSize()
+{
+    return LLDiskCache::getFileSize(mFileID, mFileType);
+}
+
+S32 LLDiskCache::getMaxSize()
+{
+    // offer up a huge size since we don't care what the max is 
+    return INT_MAX;
+}
+
+BOOL LLDiskCache::setMaxSize(S32 size)
+{
+    // we don't care what the max size is so we do nothing
+    // and return true to indicate all was okay
+    return TRUE;
+}
+
+BOOL LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
+{
+    LLDiskCache::renameFile(mFileID, mFileType, new_id, new_type);
+
+    mFileID = new_id;
+    mFileType = new_type;
+
+    return TRUE;
+}
+
+BOOL LLDiskCache::remove()
+{
+    LLDiskCache::removeFile(mFileID, mFileType);
+
+    return TRUE;
+}
+
+// static
+void LLDiskCache::initClass()
+{
+}
+
+// static
+void LLDiskCache::cleanupClass()
+{
+}
+
+bool LLDiskCache::isLocked()
+{
+    // I don't think we care about this test since there is no locking
+    // TODO: remove this function and calling sites?
+    return FALSE;
+}
+
+void LLDiskCache::waitForLock()
+{
+    // TODO: remove this function and calling sites?
+}
diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h
new file mode 100644
index 0000000000..7ad06a8689
--- /dev/null
+++ b/indra/llfilesystem/lldiskcache.h
@@ -0,0 +1,82 @@
+/** 
+ * @file lldiskcacke.h
+ * @brief Definition of virtual file
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDISKCACHE_H
+#define LL_LLDISKCACHE_H
+
+#include "lluuid.h"
+#include "llassettype.h"
+
+class LLDiskCache
+{
+public:
+	LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLDiskCache::READ);
+	~LLDiskCache();
+
+	BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f);	/* Flawfinder: ignore */ 
+	BOOL isReadComplete();
+	S32  getLastBytesRead();
+	BOOL eof();
+
+	BOOL write(const U8 *buffer, S32 bytes);
+	static BOOL writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type);
+	BOOL seek(S32 offset, S32 origin = -1);
+	S32  tell() const;
+
+	S32 getSize();
+	S32 getMaxSize();
+	BOOL setMaxSize(S32 size);
+	BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type);
+	BOOL remove();
+
+	bool isLocked();
+	void waitForLock();
+
+    static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type);
+    static bool removeFile(const LLUUID &file_id, const LLAssetType::EType file_type);
+    static bool renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type,
+                           const LLUUID &new_file_id, const LLAssetType::EType new_file_type);
+    static S32 getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type);
+	
+	static void initClass();
+	static void cleanupClass();
+
+public:
+	static const S32 READ;
+	static const S32 WRITE;
+	static const S32 READ_WRITE;
+	static const S32 APPEND;
+	
+protected:
+	LLAssetType::EType mFileType;
+    BOOL    mReadComplete;
+	LLUUID	mFileID;
+	S32		mPosition;
+	S32		mMode;
+	S32		mBytesRead;
+};
+
+#endif  // LL_LLDISKCACHE_H
diff --git a/indra/llfilesystem/lllfsthread.cpp b/indra/llfilesystem/lllfsthread.cpp
new file mode 100644
index 0000000000..be8e83a56f
--- /dev/null
+++ b/indra/llfilesystem/lllfsthread.cpp
@@ -0,0 +1,245 @@
+/** 
+ * @file lllfsthread.cpp
+ * @brief LLLFSThread base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "lllfsthread.h"
+#include "llstl.h"
+#include "llapr.h"
+
+//============================================================================
+
+/*static*/ LLLFSThread* LLLFSThread::sLocal = NULL;
+
+//============================================================================
+// Run on MAIN thread
+//static
+void LLLFSThread::initClass(bool local_is_threaded)
+{
+	llassert(sLocal == NULL);
+	sLocal = new LLLFSThread(local_is_threaded);
+}
+
+//static
+S32 LLLFSThread::updateClass(U32 ms_elapsed)
+{
+	sLocal->update((F32)ms_elapsed);
+	return sLocal->getPending();
+}
+
+//static
+void LLLFSThread::cleanupClass()
+{
+	llassert(sLocal != NULL);
+	sLocal->setQuitting();
+	while (sLocal->getPending())
+	{
+		sLocal->update(0);
+	}
+	delete sLocal;
+	sLocal = NULL;
+}
+
+//----------------------------------------------------------------------------
+
+LLLFSThread::LLLFSThread(bool threaded) :
+	LLQueuedThread("LFS", threaded),
+	mPriorityCounter(PRIORITY_LOWBITS)
+{
+	if(!mLocalAPRFilePoolp)
+	{
+		mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
+	}
+}
+
+LLLFSThread::~LLLFSThread()
+{
+	// mLocalAPRFilePoolp cleanup in LLThread
+	// ~LLQueuedThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+LLLFSThread::handle_t LLLFSThread::read(const std::string& filename,	/* Flawfinder: ignore */ 
+										U8* buffer, S32 offset, S32 numbytes,
+										Responder* responder, U32 priority)
+{
+	handle_t handle = generateHandle();
+
+	if (priority == 0) priority = PRIORITY_NORMAL | priorityCounter();
+	else if (priority < PRIORITY_LOW) priority |= PRIORITY_LOW; // All reads are at least PRIORITY_LOW
+
+	Request* req = new Request(this, handle, priority,
+							   FILE_READ, filename,
+							   buffer, offset, numbytes,
+							   responder);
+
+	bool res = addRequest(req);
+	if (!res)
+	{
+		LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL;
+	}
+
+	return handle;
+}
+
+LLLFSThread::handle_t LLLFSThread::write(const std::string& filename,
+										 U8* buffer, S32 offset, S32 numbytes,
+										 Responder* responder, U32 priority)
+{
+	handle_t handle = generateHandle();
+
+	if (priority == 0) priority = PRIORITY_LOW | priorityCounter();
+	
+	Request* req = new Request(this, handle, priority,
+							   FILE_WRITE, filename,
+							   buffer, offset, numbytes,
+							   responder);
+
+	bool res = addRequest(req);
+	if (!res)
+	{
+		LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL;
+	}
+	
+	return handle;
+}
+
+//============================================================================
+
+LLLFSThread::Request::Request(LLLFSThread* thread,
+							  handle_t handle, U32 priority,
+							  operation_t op, const std::string& filename,
+							  U8* buffer, S32 offset, S32 numbytes,
+							  Responder* responder) :
+	QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
+	mThread(thread),
+	mOperation(op),
+	mFileName(filename),
+	mBuffer(buffer),
+	mOffset(offset),
+	mBytes(numbytes),
+	mBytesRead(0),
+	mResponder(responder)
+{
+	if (numbytes <= 0)
+	{
+		LL_WARNS() << "LLLFSThread: Request with numbytes = " << numbytes << LL_ENDL;
+	}
+}
+
+LLLFSThread::Request::~Request()
+{
+}
+
+// virtual, called from own thread
+void LLLFSThread::Request::finishRequest(bool completed)
+{
+	if (mResponder.notNull())
+	{
+		mResponder->completed(completed ? mBytesRead : 0);
+		mResponder = NULL;
+	}
+}
+
+void LLLFSThread::Request::deleteRequest()
+{
+	if (getStatus() == STATUS_QUEUED)
+	{
+		LL_ERRS() << "Attempt to delete a queued LLLFSThread::Request!" << LL_ENDL;
+	}	
+	if (mResponder.notNull())
+	{
+		mResponder->completed(0);
+		mResponder = NULL;
+	}
+	LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+bool LLLFSThread::Request::processRequest()
+{
+	bool complete = false;
+	if (mOperation ==  FILE_READ)
+	{
+		llassert(mOffset >= 0);
+		LLAPRFile infile ; // auto-closes
+		infile.open(mFileName, LL_APR_RB, mThread->getLocalAPRFilePool());
+		if (!infile.getFileHandle())
+		{
+			LL_WARNS() << "LLLFS: Unable to read file: " << mFileName << LL_ENDL;
+			mBytesRead = 0; // fail
+			return true;
+		}
+		S32 off;
+		if (mOffset < 0)
+			off = infile.seek(APR_END, 0);
+		else
+			off = infile.seek(APR_SET, mOffset);
+		llassert_always(off >= 0);
+		mBytesRead = infile.read(mBuffer, mBytes );
+		complete = true;
+// 		LL_INFOS() << "LLLFSThread::READ:" << mFileName << " Bytes: " << mBytesRead << LL_ENDL;
+	}
+	else if (mOperation ==  FILE_WRITE)
+	{
+		apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
+		if (mOffset < 0)
+			flags |= APR_APPEND;
+		LLAPRFile outfile ; // auto-closes
+		outfile.open(mFileName, flags, mThread->getLocalAPRFilePool());
+		if (!outfile.getFileHandle())
+		{
+			LL_WARNS() << "LLLFS: Unable to write file: " << mFileName << LL_ENDL;
+			mBytesRead = 0; // fail
+			return true;
+		}
+		if (mOffset >= 0)
+		{
+			S32 seek = outfile.seek(APR_SET, mOffset);
+			if (seek < 0)
+			{
+				LL_WARNS() << "LLLFS: Unable to write file (seek failed): " << mFileName << LL_ENDL;
+				mBytesRead = 0; // fail
+				return true;
+			}
+		}
+		mBytesRead = outfile.write(mBuffer, mBytes );
+		complete = true;
+// 		LL_INFOS() << "LLLFSThread::WRITE:" << mFileName << " Bytes: " << mBytesRead << "/" << mBytes << " Offset:" << mOffset << LL_ENDL;
+	}
+	else
+	{
+		LL_ERRS() << "LLLFSThread::unknown operation: " << (S32)mOperation << LL_ENDL;
+	}
+	return complete;
+}
+
+//============================================================================
+
+LLLFSThread::Responder::~Responder()
+{
+}
+
+//============================================================================
diff --git a/indra/llfilesystem/lllfsthread.h b/indra/llfilesystem/lllfsthread.h
new file mode 100644
index 0000000000..58f658f7ba
--- /dev/null
+++ b/indra/llfilesystem/lllfsthread.h
@@ -0,0 +1,147 @@
+/** 
+ * @file lllfsthread.h
+ * @brief LLLFSThread base class
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLLFSTHREAD_H
+#define LL_LLLFSTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llpointer.h"
+#include "llqueuedthread.h"
+
+//============================================================================
+// Threaded Local File System
+//============================================================================
+
+class LLLFSThread : public LLQueuedThread
+{
+	//------------------------------------------------------------------------
+public:
+	enum operation_t {
+		FILE_READ,
+		FILE_WRITE,
+		FILE_RENAME,
+		FILE_REMOVE
+	};
+
+	//------------------------------------------------------------------------
+public:
+
+	class Responder : public LLThreadSafeRefCount
+	{
+	protected:
+		~Responder();
+	public:
+		virtual void completed(S32 bytes) = 0;
+	};
+
+	class Request : public QueuedRequest
+	{
+	protected:
+		virtual ~Request(); // use deleteRequest()
+		
+	public:
+		Request(LLLFSThread* thread,
+				handle_t handle, U32 priority, 
+				operation_t op, const std::string& filename,
+				U8* buffer, S32 offset, S32 numbytes,
+				Responder* responder);
+
+		S32 getBytes()
+		{
+			return mBytes;
+		}
+		S32 getBytesRead()
+		{
+			return mBytesRead;
+		}
+		S32 getOperation()
+		{
+			return mOperation;
+		}
+		U8* getBuffer()
+		{
+			return mBuffer;
+		}
+		const std::string& getFilename()
+		{
+			return mFileName;
+		}
+		
+		/*virtual*/ bool processRequest();
+		/*virtual*/ void finishRequest(bool completed);
+		/*virtual*/ void deleteRequest();
+		
+	private:
+		LLLFSThread* mThread;
+		operation_t mOperation;
+		
+		std::string mFileName;
+		
+		U8* mBuffer;	// dest for reads, source for writes, new UUID for rename
+		S32 mOffset;	// offset into file, -1 = append (WRITE only)
+		S32 mBytes;		// bytes to read from file, -1 = all
+		S32 mBytesRead;	// bytes read from file
+
+		LLPointer<Responder> mResponder;
+	};
+
+	//------------------------------------------------------------------------
+public:
+	LLLFSThread(bool threaded = TRUE);
+	~LLLFSThread();	
+
+	// Return a Request handle
+	handle_t read(const std::string& filename,	/* Flawfinder: ignore */ 
+				  U8* buffer, S32 offset, S32 numbytes,
+				  Responder* responder, U32 pri=0);
+	handle_t write(const std::string& filename,
+				   U8* buffer, S32 offset, S32 numbytes,
+				   Responder* responder, U32 pri=0);
+	
+	// Misc
+	U32 priorityCounter() { return mPriorityCounter-- & PRIORITY_LOWBITS; } // Use to order IO operations
+	
+	// static initializers
+	static void initClass(bool local_is_threaded = TRUE); // Setup sLocal
+	static S32 updateClass(U32 ms_elapsed);
+	static void cleanupClass();		// Delete sLocal
+
+	
+private:
+	U32 mPriorityCounter;
+	
+public:
+	static LLLFSThread* sLocal;		// Default local file thread
+};
+
+//============================================================================
+
+
+#endif // LL_LLLFSTHREAD_H
diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp
new file mode 100644
index 0000000000..3cff622a4b
--- /dev/null
+++ b/indra/llfilesystem/tests/lldir_test.cpp
@@ -0,0 +1,767 @@
+/**
+ * @file lldir_test.cpp
+ * @date 2008-05
+ * @brief LLDir test cases.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llstring.h"
+#include "tests/StringVec.h"
+#include "../lldir.h"
+#include "../lldiriterator.h"
+
+#include "../test/lltut.h"
+#include "stringize.h"
+#include <boost/foreach.hpp>
+#include <boost/assign/list_of.hpp>
+
+using boost::assign::list_of;
+
+// We use ensure_equals(..., vec(list_of(...))) not because it's functionally
+// required, but because ensure_equals() knows how to format a StringVec.
+// Turns out that when ensure_equals() displays a test failure with just
+// list_of("string")("another"), you see 'stringanother' vs. '("string",
+// "another")'.
+StringVec vec(const StringVec& v)
+{
+    return v;
+}
+
+// For some tests, use a dummy LLDir that uses memory data instead of touching
+// the filesystem
+struct LLDir_Dummy: public LLDir
+{
+    /*----------------------------- LLDir API ------------------------------*/
+    LLDir_Dummy()
+    {
+        // Initialize important LLDir data members based on the filesystem
+        // data below.
+        mDirDelimiter = "/";
+        mExecutableDir = "install";
+        mExecutableFilename = "test";
+        mExecutablePathAndName = add(mExecutableDir, mExecutableFilename);
+        mWorkingDir = mExecutableDir;
+        mAppRODataDir = "install";
+        mSkinBaseDir = add(mAppRODataDir, "skins");
+        mOSUserDir = "user";
+        mOSUserAppDir = mOSUserDir;
+        mLindenUserDir = "";
+
+        // Make the dummy filesystem look more or less like what we expect in
+        // the real one.
+        static const char* preload[] =
+        {
+            // We group these fixture-data pathnames by basename, rather than
+            // sorting by full path as you might expect, because the outcome
+            // of each test strongly depends on which skins/languages provide
+            // a given basename.
+            "install/skins/default/colors.xml",
+            "install/skins/steam/colors.xml",
+            "user/skins/default/colors.xml",
+            "user/skins/steam/colors.xml",
+
+            "install/skins/default/xui/en/strings.xml",
+            "install/skins/default/xui/fr/strings.xml",
+            "install/skins/steam/xui/en/strings.xml",
+            "install/skins/steam/xui/fr/strings.xml",
+            "user/skins/default/xui/en/strings.xml",
+            "user/skins/default/xui/fr/strings.xml",
+            "user/skins/steam/xui/en/strings.xml",
+            "user/skins/steam/xui/fr/strings.xml",
+
+            "install/skins/default/xui/en/floater.xml",
+            "install/skins/default/xui/fr/floater.xml",
+            "user/skins/default/xui/fr/floater.xml",
+
+            "install/skins/default/xui/en/newfile.xml",
+            "install/skins/default/xui/fr/newfile.xml",
+            "user/skins/default/xui/en/newfile.xml",
+
+            "install/skins/default/html/en-us/welcome.html",
+            "install/skins/default/html/fr/welcome.html",
+
+            "install/skins/default/textures/only_default.jpeg",
+            "install/skins/steam/textures/only_steam.jpeg",
+            "user/skins/default/textures/only_user_default.jpeg",
+            "user/skins/steam/textures/only_user_steam.jpeg",
+
+            "install/skins/default/future/somefile.txt"
+        };
+        BOOST_FOREACH(const char* path, preload)
+        {
+            buildFilesystem(path);
+        }
+    }
+
+    virtual ~LLDir_Dummy() {}
+
+    virtual void initAppDirs(const std::string& app_name, const std::string& app_read_only_data_dir)
+    {
+        // Implement this when we write a test that needs it
+    }
+
+    virtual std::string getCurPath()
+    {
+        // Implement this when we write a test that needs it
+        return "";
+    }
+
+    virtual U32 countFilesInDir(const std::string& dirname, const std::string& mask)
+    {
+        // Implement this when we write a test that needs it
+        return 0;
+    }
+
+    virtual bool fileExists(const std::string& pathname) const
+    {
+        // Record fileExists() calls so we can check whether caching is
+        // working right. Certain LLDir calls should be able to make decisions
+        // without calling fileExists() again, having already checked existence.
+        mChecked.insert(pathname);
+        // For our simple flat set of strings, see whether the identical
+        // pathname exists in our set.
+        return (mFilesystem.find(pathname) != mFilesystem.end());
+    }
+
+    virtual std::string getLLPluginLauncher()
+    {
+        // Implement this when we write a test that needs it
+        return "";
+    }
+
+    virtual std::string getLLPluginFilename(std::string base_name)
+    {
+        // Implement this when we write a test that needs it
+        return "";
+    }
+
+    /*----------------------------- Dummy data -----------------------------*/
+    void clearFilesystem() { mFilesystem.clear(); }
+    void buildFilesystem(const std::string& path)
+    {
+        // Split the pathname on slashes, ignoring leading, trailing, doubles
+        StringVec components;
+        LLStringUtil::getTokens(path, components, "/");
+        // Ensure we have an entry representing every level of this path
+        std::string partial;
+        BOOST_FOREACH(std::string component, components)
+        {
+            append(partial, component);
+            mFilesystem.insert(partial);
+        }
+    }
+
+    void clear_checked() { mChecked.clear(); }
+    void ensure_checked(const std::string& pathname) const
+    {
+        tut::ensure(STRINGIZE(pathname << " was not checked but should have been"),
+                    mChecked.find(pathname) != mChecked.end());
+    }
+    void ensure_not_checked(const std::string& pathname) const
+    {
+        tut::ensure(STRINGIZE(pathname << " was checked but should not have been"),
+                    mChecked.find(pathname) == mChecked.end());
+    }
+
+    std::set<std::string> mFilesystem;
+    mutable std::set<std::string> mChecked;
+};
+
+namespace tut
+{
+	struct LLDirTest
+        {
+        };
+        typedef test_group<LLDirTest> LLDirTest_t;
+        typedef LLDirTest_t::object LLDirTest_object_t;
+        tut::LLDirTest_t tut_LLDirTest("LLDir");
+
+	template<> template<>
+	void LLDirTest_object_t::test<1>()
+		// getDirDelimiter
+	{
+		ensure("getDirDelimiter", !gDirUtilp->getDirDelimiter().empty());
+	}
+
+	template<> template<>
+	void LLDirTest_object_t::test<2>()
+		// getBaseFileName
+	{
+		std::string delim = gDirUtilp->getDirDelimiter();
+		std::string rawFile = "foo";
+		std::string rawFileExt = "foo.bAr";
+		std::string rawFileNullExt = "foo.";
+		std::string rawExt = ".bAr";
+		std::string rawDot = ".";
+		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
+		std::string pathExt = pathNoExt + ".eXt";
+		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
+		std::string dottedPathExt = dottedPathNoExt + ".eXt";
+
+		// foo[.bAr]
+
+		ensure_equals("getBaseFileName/r-no-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(rawFile, false),
+			      "foo");
+
+		ensure_equals("getBaseFileName/r-no-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(rawFile, true),
+			      "foo");
+
+		ensure_equals("getBaseFileName/r-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(rawFileExt, false),
+			      "foo.bAr");
+
+		ensure_equals("getBaseFileName/r-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(rawFileExt, true),
+			      "foo");
+
+		// foo.
+
+		ensure_equals("getBaseFileName/rn-no-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(rawFileNullExt, false),
+			      "foo.");
+
+		ensure_equals("getBaseFileName/rn-no-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(rawFileNullExt, true),
+			      "foo");
+
+		// .bAr
+		// interesting case - with no basename, this IS the basename, not the extension.
+
+		ensure_equals("getBaseFileName/e-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(rawExt, false),
+			      ".bAr");
+
+		ensure_equals("getBaseFileName/e-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(rawExt, true),
+			      ".bAr");
+
+		// .
+
+		ensure_equals("getBaseFileName/d/no-strip-exten",
+			      gDirUtilp->getBaseFileName(rawDot, false),
+			      ".");
+
+		ensure_equals("getBaseFileName/d/strip-exten",
+			      gDirUtilp->getBaseFileName(rawDot, true),
+			      ".");
+
+		// aa/bb/cc/dd/ee[.eXt]
+
+		ensure_equals("getBaseFileName/no-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(pathNoExt, false),
+			      "ee");
+
+		ensure_equals("getBaseFileName/no-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(pathNoExt, true),
+			      "ee");
+
+		ensure_equals("getBaseFileName/ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(pathExt, false),
+			      "ee.eXt");
+
+		ensure_equals("getBaseFileName/ext/strip-exten",
+			      gDirUtilp->getBaseFileName(pathExt, true),
+			      "ee");
+
+		// aa/bb/cc.dd/ee[.eXt]
+
+		ensure_equals("getBaseFileName/d-no-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(dottedPathNoExt, false),
+			      "ee");
+
+		ensure_equals("getBaseFileName/d-no-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(dottedPathNoExt, true),
+			      "ee");
+
+		ensure_equals("getBaseFileName/d-ext/no-strip-exten",
+			      gDirUtilp->getBaseFileName(dottedPathExt, false),
+			      "ee.eXt");
+
+		ensure_equals("getBaseFileName/d-ext/strip-exten",
+			      gDirUtilp->getBaseFileName(dottedPathExt, true),
+			      "ee");
+	}
+
+	template<> template<>
+	void LLDirTest_object_t::test<3>()
+		// getDirName
+	{
+		std::string delim = gDirUtilp->getDirDelimiter();
+		std::string rawFile = "foo";
+		std::string rawFileExt = "foo.bAr";
+		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
+		std::string pathExt = pathNoExt + ".eXt";
+		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
+		std::string dottedPathExt = dottedPathNoExt + ".eXt";
+
+		// foo[.bAr]
+
+		ensure_equals("getDirName/r-no-ext",
+			      gDirUtilp->getDirName(rawFile),
+			      "");
+
+		ensure_equals("getDirName/r-ext",
+			      gDirUtilp->getDirName(rawFileExt),
+			      "");
+
+		// aa/bb/cc/dd/ee[.eXt]
+
+		ensure_equals("getDirName/no-ext",
+			      gDirUtilp->getDirName(pathNoExt),
+			      "aa" + delim + "bb" + delim + "cc" + delim + "dd");
+
+		ensure_equals("getDirName/ext",
+			      gDirUtilp->getDirName(pathExt),
+			      "aa" + delim + "bb" + delim + "cc" + delim + "dd");
+
+		// aa/bb/cc.dd/ee[.eXt]
+
+		ensure_equals("getDirName/d-no-ext",
+			      gDirUtilp->getDirName(dottedPathNoExt),
+			      "aa" + delim + "bb" + delim + "cc.dd");
+
+		ensure_equals("getDirName/d-ext",
+			      gDirUtilp->getDirName(dottedPathExt),
+			      "aa" + delim + "bb" + delim + "cc.dd");
+	}
+
+	template<> template<>
+	void LLDirTest_object_t::test<4>()
+		// getExtension
+	{
+		std::string delim = gDirUtilp->getDirDelimiter();
+		std::string rawFile = "foo";
+		std::string rawFileExt = "foo.bAr";
+		std::string rawFileNullExt = "foo.";
+		std::string rawExt = ".bAr";
+		std::string rawDot = ".";
+		std::string pathNoExt = "aa" + delim + "bb" + delim + "cc" + delim + "dd" + delim + "ee";
+		std::string pathExt = pathNoExt + ".eXt";
+		std::string dottedPathNoExt = "aa" + delim + "bb" + delim + "cc.dd" + delim + "ee";
+		std::string dottedPathExt = dottedPathNoExt + ".eXt";
+
+		// foo[.bAr]
+
+		ensure_equals("getExtension/r-no-ext",
+			      gDirUtilp->getExtension(rawFile),
+			      "");
+
+		ensure_equals("getExtension/r-ext",
+			      gDirUtilp->getExtension(rawFileExt),
+			      "bar");
+
+		// foo.
+
+		ensure_equals("getExtension/rn-no-ext",
+			      gDirUtilp->getExtension(rawFileNullExt),
+			      "");
+
+		// .bAr
+		// interesting case - with no basename, this IS the basename, not the extension.
+
+		ensure_equals("getExtension/e-ext",
+			      gDirUtilp->getExtension(rawExt),
+			      "");
+
+		// .
+
+		ensure_equals("getExtension/d",
+			      gDirUtilp->getExtension(rawDot),
+			      "");
+
+		// aa/bb/cc/dd/ee[.eXt]
+
+		ensure_equals("getExtension/no-ext",
+			      gDirUtilp->getExtension(pathNoExt),
+			      "");
+
+		ensure_equals("getExtension/ext",
+			      gDirUtilp->getExtension(pathExt),
+			      "ext");
+
+		// aa/bb/cc.dd/ee[.eXt]
+
+		ensure_equals("getExtension/d-no-ext",
+			      gDirUtilp->getExtension(dottedPathNoExt),
+			      "");
+
+		ensure_equals("getExtension/d-ext",
+			      gDirUtilp->getExtension(dottedPathExt),
+			      "ext");
+	}
+
+   std::string makeTestFile( const std::string& dir, const std::string& file )
+   {
+      std::string path = dir + file;
+      LLFILE* handle = LLFile::fopen( path, "w" );
+      ensure("failed to open test file '"+path+"'", handle != NULL );
+      // Harbison & Steele, 4th ed., p. 366: "If an error occurs, fputs
+      // returns EOF; otherwise, it returns some other, nonnegative value."
+      ensure("failed to write to test file '"+path+"'", EOF != fputs("test file", handle) );
+      fclose(handle);
+      return path;
+   }
+
+   std::string makeTestDir( const std::string& dirbase )
+   {
+      int counter;
+      std::string uniqueDir;
+      bool foundUnused;
+      std::string delim = gDirUtilp->getDirDelimiter();
+      
+      for (counter=0, foundUnused=false; !foundUnused; counter++ )
+      {
+         char counterStr[3];
+         sprintf(counterStr, "%02d", counter);
+         uniqueDir = dirbase + counterStr;
+         foundUnused = ! ( LLFile::isdir(uniqueDir) || LLFile::isfile(uniqueDir) );
+      }
+      ensure("test directory '" + uniqueDir + "' creation failed", !LLFile::mkdir(uniqueDir));
+      
+      return uniqueDir + delim; // HACK - apparently, the trailing delimiter is needed...
+   }
+
+   static const char* DirScanFilename[5] = { "file1.abc", "file2.abc", "file1.xyz", "file2.xyz", "file1.mno" };
+
+   void scanTest(const std::string& directory, const std::string& pattern, bool correctResult[5])
+   {
+
+      // Scan directory and see if any file1.* files are found
+      std::string scanResult;
+      int   found = 0;
+      bool  filesFound[5] = { false, false, false, false, false };
+      //std::cerr << "searching '"+directory+"' for '"+pattern+"'\n";
+
+      LLDirIterator iter(directory, pattern);
+      while ( found <= 5 && iter.next(scanResult) )
+      {
+         found++;
+         //std::cerr << "  found '"+scanResult+"'\n";
+         int check;
+         for (check=0; check < 5 && ! ( scanResult == DirScanFilename[check] ); check++)
+         {
+         }
+         // check is now either 5 (not found) or the index of the matching name
+         if (check < 5)
+         {
+            ensure( "found file '"+(std::string)DirScanFilename[check]+"' twice", ! filesFound[check] );
+            filesFound[check] = true;
+         }
+         else // check is 5 - should not happen
+         {
+            fail( "found unknown file '"+scanResult+"'");
+         }
+      }
+      for (int i=0; i<5; i++)
+      {
+         if (correctResult[i])
+         {
+            ensure("scan of '"+directory+"' using '"+pattern+"' did not return '"+DirScanFilename[i]+"'", filesFound[i]);
+         }
+         else
+         {
+            ensure("scan of '"+directory+"' using '"+pattern+"' incorrectly returned '"+DirScanFilename[i]+"'", !filesFound[i]);
+         }
+      }
+   }
+   
+   template<> template<>
+   void LLDirTest_object_t::test<5>()
+      // LLDirIterator::next
+   {
+      std::string delim = gDirUtilp->getDirDelimiter();
+      std::string dirTemp = LLFile::tmpdir();
+
+      // Create the same 5 file names of the two directories
+
+      std::string dir1 = makeTestDir(dirTemp + "LLDirIterator");
+      std::string dir2 = makeTestDir(dirTemp + "LLDirIterator");
+      std::string dir1files[5];
+      std::string dir2files[5];
+      for (int i=0; i<5; i++)
+      {
+         dir1files[i] = makeTestFile(dir1, DirScanFilename[i]);
+         dir2files[i] = makeTestFile(dir2, DirScanFilename[i]);
+      }
+
+      // Scan dir1 and see if each of the 5 files is found exactly once
+      bool expected1[5] = { true, true, true, true, true };
+      scanTest(dir1, "*", expected1);
+
+      // Scan dir2 and see if only the 2 *.xyz files are found
+      bool  expected2[5] = { false, false, true, true, false };
+      scanTest(dir1, "*.xyz", expected2);
+
+      // Scan dir2 and see if only the 1 *.mno file is found
+      bool  expected3[5] = { false, false, false, false, true };
+      scanTest(dir2, "*.mno", expected3);
+
+      // Scan dir1 and see if any *.foo files are found
+      bool  expected4[5] = { false, false, false, false, false };
+      scanTest(dir1, "*.foo", expected4);
+
+      // Scan dir1 and see if any file1.* files are found
+      bool  expected5[5] = { true, false, true, false, true };
+      scanTest(dir1, "file1.*", expected5);
+
+      // Scan dir1 and see if any file1.* files are found
+      bool  expected6[5] = { true, true, false, false, false };
+      scanTest(dir1, "file?.abc", expected6);
+
+      // Scan dir2 and see if any file?.x?z files are found
+      bool  expected7[5] = { false, false, true, true, false };
+      scanTest(dir2, "file?.x?z", expected7);
+
+      // Scan dir2 and see if any file?.??c files are found
+      bool  expected8[5] = { true, true, false, false, false };
+      scanTest(dir2, "file?.??c", expected8);
+      scanTest(dir2, "*.??c", expected8);
+
+      // Scan dir1 and see if any *.?n? files are found
+      bool  expected9[5] = { false, false, false, false, true };
+      scanTest(dir1, "*.?n?", expected9);
+
+      // Scan dir1 and see if any *.???? files are found
+      bool  expected10[5] = { false, false, false, false, false };
+      scanTest(dir1, "*.????", expected10);
+
+      // Scan dir1 and see if any ?????.* files are found
+      bool  expected11[5] = { true, true, true, true, true };
+      scanTest(dir1, "?????.*", expected11);
+
+      // Scan dir1 and see if any ??l??.xyz files are found
+      bool  expected12[5] = { false, false, true, true, false };
+      scanTest(dir1, "??l??.xyz", expected12);
+
+      bool expected13[5] = { true, false, true, false, false };
+      scanTest(dir1, "file1.{abc,xyz}", expected13);
+
+      bool expected14[5] = { true, true, false, false, false };
+      scanTest(dir1, "file[0-9].abc", expected14);
+
+      bool expected15[5] = { true, true, false, false, false };
+      scanTest(dir1, "file[!a-z].abc", expected15);
+
+      // clean up all test files and directories
+      for (int i=0; i<5; i++)
+      {
+         LLFile::remove(dir1files[i]);
+         LLFile::remove(dir2files[i]);
+      }
+      LLFile::rmdir(dir1);
+      LLFile::rmdir(dir2);
+   }
+
+    template<> template<>
+    void LLDirTest_object_t::test<6>()
+    {
+        set_test_name("findSkinnedFilenames()");
+        LLDir_Dummy lldir;
+        /*------------------------ "default", "en" -------------------------*/
+        // Setting "default" means we shouldn't consider any "*/skins/steam"
+        // directories; setting "en" means we shouldn't consider any "xui/fr"
+        // directories.
+        lldir.setSkinFolder("default", "en");
+        ensure_equals(lldir.getSkinFolder(), "default");
+        ensure_equals(lldir.getLanguage(), "en");
+
+        // top-level directory of a skin isn't localized
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS),
+                      vec(list_of("install/skins/default/colors.xml")
+                                 ("user/skins/default/colors.xml")));
+        // We should not have needed to check for skins/default/en. We should
+        // just "know" that SKINBASE is not localized.
+        lldir.ensure_not_checked("install/skins/default/en");
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"),
+                      vec(list_of("install/skins/default/textures/only_default.jpeg")));
+        // Nor should we have needed to check skins/default/textures/en
+        // because textures is known not to be localized.
+        lldir.ensure_not_checked("install/skins/default/textures/en");
+
+        StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml")
+                               ("user/skins/default/xui/en/strings.xml")));
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
+                      expected);
+        // The first time, we had to probe to find out whether xui was localized.
+        lldir.ensure_checked("install/skins/default/xui/en");
+        lldir.clear_checked();
+        // Now make the same call again -- should return same result --
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
+                      expected);
+        // but this time it should remember that xui is localized.
+        lldir.ensure_not_checked("install/skins/default/xui/en");
+
+        // localized subdir with "en-us" instead of "en"
+        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
+                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
+        lldir.ensure_checked("install/skins/default/html/en");
+        lldir.ensure_checked("install/skins/default/html/en-us");
+        lldir.clear_checked();
+        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
+                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
+        lldir.ensure_not_checked("install/skins/default/html/en");
+        lldir.ensure_not_checked("install/skins/default/html/en-us");
+
+        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"),
+                      vec(list_of("install/skins/default/future/somefile.txt")));
+        // Test probing for an unrecognized unlocalized future subdir.
+        lldir.ensure_checked("install/skins/default/future/en");
+        lldir.clear_checked();
+        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"),
+                      vec(list_of("install/skins/default/future/somefile.txt")));
+        // Second time it should remember that future is unlocalized.
+        lldir.ensure_not_checked("install/skins/default/future/en");
+
+        // When language is set to "en", requesting an html file pulls up the
+        // "en-us" version -- not because it magically matches those strings,
+        // but because there's no "en" localization and it falls back on the
+        // default "en-us"! Note that it would probably still be better to
+        // make the default localization be "en" and allow "en-gb" (or
+        // whatever) localizations, which would work much more the way you'd
+        // expect.
+        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
+                      vec(list_of("install/skins/default/html/en-us/welcome.html")));
+
+        /*------------------------ "default", "fr" -------------------------*/
+        // We start being able to distinguish localized subdirs from
+        // unlocalized when we ask for a non-English language.
+        lldir.setSkinFolder("default", "fr");
+        ensure_equals(lldir.getLanguage(), "fr");
+
+        // pass merge=true to request this filename in all relevant skins
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
+                      vec(list_of
+                          ("install/skins/default/xui/en/strings.xml")
+                          ("install/skins/default/xui/fr/strings.xml")
+                          ("user/skins/default/xui/en/strings.xml")
+                          ("user/skins/default/xui/fr/strings.xml")));
+
+        // pass (or default) merge=false to request only most specific skin
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
+                      vec(list_of
+                          ("user/skins/default/xui/en/strings.xml")
+                          ("user/skins/default/xui/fr/strings.xml")));
+
+        // Our dummy floater.xml has a user localization (for "fr") but no
+        // English override. This is a case in which CURRENT_SKIN nonetheless
+        // returns paths from two different skins.
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"),
+                      vec(list_of
+                          ("install/skins/default/xui/en/floater.xml")
+                          ("user/skins/default/xui/fr/floater.xml")));
+
+        // Our dummy newfile.xml has an English override but no user
+        // localization. This is another case in which CURRENT_SKIN
+        // nonetheless returns paths from two different skins.
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"),
+                      vec(list_of
+                          ("user/skins/default/xui/en/newfile.xml")
+                          ("install/skins/default/xui/fr/newfile.xml")));
+
+        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"),
+                      vec(list_of
+                          ("install/skins/default/html/en-us/welcome.html")
+                          ("install/skins/default/html/fr/welcome.html")));
+
+        /*------------------------ "default", "zh" -------------------------*/
+        lldir.setSkinFolder("default", "zh");
+        // Because strings.xml has only a "fr" override but no "zh" override
+        // in any skin, the most localized version we can find is "en".
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
+                      vec(list_of("user/skins/default/xui/en/strings.xml")));
+
+        /*------------------------- "steam", "en" --------------------------*/
+        lldir.setSkinFolder("steam", "en");
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS),
+                      vec(list_of
+                          ("install/skins/default/colors.xml")
+                          ("install/skins/steam/colors.xml")
+                          ("user/skins/default/colors.xml")
+                          ("user/skins/steam/colors.xml")));
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"),
+                      vec(list_of("install/skins/default/textures/only_default.jpeg")));
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"),
+                      vec(list_of("install/skins/steam/textures/only_steam.jpeg")));
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"),
+                      vec(list_of("user/skins/default/textures/only_user_default.jpeg")));
+
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"),
+                      vec(list_of("user/skins/steam/textures/only_user_steam.jpeg")));
+
+        // CURRENT_SKIN
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
+                      vec(list_of("user/skins/steam/xui/en/strings.xml")));
+
+        // pass constraint=ALL_SKINS to request this filename in all relevant skins
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
+                      vec(list_of
+                          ("install/skins/default/xui/en/strings.xml")
+                          ("install/skins/steam/xui/en/strings.xml")
+                          ("user/skins/default/xui/en/strings.xml")
+                          ("user/skins/steam/xui/en/strings.xml")));
+
+        /*------------------------- "steam", "fr" --------------------------*/
+        lldir.setSkinFolder("steam", "fr");
+
+        // pass CURRENT_SKIN to request only the most specialized files
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
+                      vec(list_of
+                          ("user/skins/steam/xui/en/strings.xml")
+                          ("user/skins/steam/xui/fr/strings.xml")));
+
+        // pass ALL_SKINS to request this filename in all relevant skins
+        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS),
+                      vec(list_of
+                          ("install/skins/default/xui/en/strings.xml")
+                          ("install/skins/default/xui/fr/strings.xml")
+                          ("install/skins/steam/xui/en/strings.xml")
+                          ("install/skins/steam/xui/fr/strings.xml")
+                          ("user/skins/default/xui/en/strings.xml")
+                          ("user/skins/default/xui/fr/strings.xml")
+                          ("user/skins/steam/xui/en/strings.xml")
+                          ("user/skins/steam/xui/fr/strings.xml")));
+    }
+
+    template<> template<>
+    void LLDirTest_object_t::test<7>()
+    {
+        set_test_name("add()");
+        LLDir_Dummy lldir;
+        ensure_equals("both empty", lldir.add("", ""), "");
+        ensure_equals("path empty", lldir.add("", "b"), "b");
+        ensure_equals("name empty", lldir.add("a", ""), "a");
+        ensure_equals("both simple", lldir.add("a", "b"), "a/b");
+        ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b");
+        ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b");
+        ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b");
+    }
+}
diff --git a/indra/llfilesystem/tests/lldiriterator_test.cpp b/indra/llfilesystem/tests/lldiriterator_test.cpp
new file mode 100644
index 0000000000..a65e3dada5
--- /dev/null
+++ b/indra/llfilesystem/tests/lldiriterator_test.cpp
@@ -0,0 +1,65 @@
+/**
+ * @file lldiriterator_test.cpp
+ * @date 2011-06
+ * @brief LLDirIterator test cases.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.,
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+#include "../lldiriterator.h"
+
+
+namespace tut
+{
+    
+    struct LLDirIteratorFixture
+    {
+        LLDirIteratorFixture()
+        {
+        }
+    };
+    typedef test_group<LLDirIteratorFixture> LLDirIteratorTest_factory;
+    typedef LLDirIteratorTest_factory::object LLDirIteratorTest_t;
+    LLDirIteratorTest_factory tf("LLDirIterator");
+
+    /*
+    CHOP-662 was originally introduced to deal with crashes deleting files from
+    a directory (VWR-25500). However, this introduced a crash looking for 
+    old chat logs as the glob_to_regex function in lldiriterator wasn't escaping lots of regexp characters
+    */
+    void test_chop_662(void)
+    {
+        //  Check a selection of bad group names from the crash reports 
+        LLDirIterator iter(".","+bad-group-name]+?\?-??.*");
+        LLDirIterator iter1(".","))--@---bad-group-name2((?\?-??.*\\.txt");
+        LLDirIterator iter2(".","__^v--x)Cuide d sua vida(x--v^__?\?-??.*"); 
+    }
+
+    template<> template<>
+	void LLDirIteratorTest_t::test<1>()
+    {
+       test_chop_662();
+    }
+
+}
diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt
index 1f6b981346..dc8e9f7c2f 100644
--- a/indra/llimage/CMakeLists.txt
+++ b/indra/llimage/CMakeLists.txt
@@ -6,7 +6,7 @@ include(00-Common)
 include(LLCommon)
 include(LLImage)
 include(LLMath)
-include(LLCache)
+include(LLFileSystem)
 include(LLKDU)
 include(LLImageJ2COJ)
 include(ZLIB)
@@ -17,7 +17,7 @@ include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${PNG_INCLUDE_DIRS}
     ${ZLIB_INCLUDE_DIRS}
     )
@@ -68,7 +68,7 @@ else (USE_KDU)
 endif (USE_KDU)
 
 target_link_libraries(llimage
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     ${JPEG_LIBRARIES}
diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt
index ba9f8af390..04975940aa 100644
--- a/indra/llinventory/CMakeLists.txt
+++ b/indra/llinventory/CMakeLists.txt
@@ -7,7 +7,7 @@ include(LLCommon)
 include(LLCoreHttp)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 
 include_directories(
@@ -81,7 +81,7 @@ if (LL_TESTS)
     LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}")
 
     #set(TEST_DEBUG on)
-    set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLCACHE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+    set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
     LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}")
     LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}")
 endif (LL_TESTS)
diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt
index 66a7d3ea04..f0a1dfe940 100644
--- a/indra/llmessage/CMakeLists.txt
+++ b/indra/llmessage/CMakeLists.txt
@@ -9,7 +9,7 @@ include(LLCommon)
 include(LLCoreHttp)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLAddBuildTest)
 include(Python)
 include(Tut)
@@ -23,7 +23,7 @@ include_directories(
     ${LLCOREHTTP_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${JSONCPP_INCLUDE_DIR}
     )
 
@@ -209,7 +209,7 @@ target_link_libraries(
   llmessage
   ${CURL_LIBRARIES}
   ${LLCOMMON_LIBRARIES}
-  ${LLCACHE_LIBRARIES}
+  ${LLFILESYSTEM_LIBRARIES}
   ${LLMATH_LIBRARIES}
   ${JSONCPP_LIBRARIES}
   ${OPENSSL_LIBRARIES}
@@ -227,7 +227,7 @@ target_link_libraries(
   llmessage
   ${CURL_LIBRARIES}
   ${LLCOMMON_LIBRARIES}
-  ${LLCACHE_LIBRARIES}
+  ${LLFILESYSTEM_LIBRARIES}
   ${LLMATH_LIBRARIES}
   ${JSONCPP_LIBRARIES}
   ${OPENSSL_LIBRARIES}
@@ -257,7 +257,7 @@ if (LL_TESTS)
 if (LINUX)
   set(test_libs
     ${WINDOWS_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${CURL_LIBRARIES}
     ${NGHTTP2_LIBRARIES}
@@ -273,7 +273,7 @@ if (LINUX)
 else (LINUX)
   set(test_libs
     ${WINDOWS_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${CURL_LIBRARIES}
     ${NGHTTP2_LIBRARIES}
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt
index 524e2a1729..baab09a104 100644
--- a/indra/llrender/CMakeLists.txt
+++ b/indra/llrender/CMakeLists.txt
@@ -11,7 +11,7 @@ include(LLMath)
 include(LLRender)
 include(LLWindow)
 include(LLXML)
-include(LLCache)
+include(LLFileSystem)
 
 include_directories(
     ${FREETYPE_INCLUDE_DIRS}
@@ -19,10 +19,9 @@ include_directories(
     ${LLIMAGE_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
     )
 include_directories(SYSTEM
     ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -103,9 +102,8 @@ if (BUILD_HEADLESS)
     ${LLIMAGE_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLRENDER_HEADLESS_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
     ${LLXML_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLWINDOW_HEADLESS_LIBRARIES}
     ${OPENGL_HEADLESS_LIBRARIES})
 
@@ -125,7 +123,7 @@ target_link_libraries(llrender
     ${LLCOMMON_LIBRARIES}
     ${LLIMAGE_LIBRARIES}
     ${LLMATH_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
     ${FREETYPE_LIBRARIES}
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 02020b19c6..7401e6130a 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -13,7 +13,7 @@ include(LLCoreHttp)
 include(LLRender)
 include(LLWindow)
 include(LLCoreHttp)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 
 include_directories(
@@ -25,7 +25,7 @@ include_directories(
     ${LLMESSAGE_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     ${LIBS_PREBUILD_DIR}/include/hunspell
     )
@@ -283,7 +283,7 @@ target_link_libraries(llui
     ${LLINVENTORY_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
     ${LLCOREHTTP_LIBRARIES}
-    ${LLCACHE_LIBRARIES}    # ugh, just for LLDir
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLXUIXML_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLMATH_LIBRARIES}
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 5603c2f322..70eb99c86c 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -16,7 +16,7 @@ include(LLCommon)
 include(LLImage)
 include(LLMath)
 include(LLRender)
-include(LLCache)
+include(LLFileSystem)
 include(LLWindow)
 include(LLXML)
 include(UI)
@@ -26,7 +26,7 @@ include_directories(
     ${LLIMAGE_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     )
@@ -72,7 +72,7 @@ if (LINUX)
       ${LLIMAGE_LIBRARIES}
       ${LLMATH_LIBRARIES}
       ${LLRENDER_LIBRARIES}
-      ${LLCACHE_LIBRARIES}
+      ${LLFILESYSTEM_LIBRARIES}
       ${LLWINDOW_LIBRARIES}
       ${LLXML_LIBRARIES}
       ${UI_LIBRARIES}     # for GTK
@@ -95,7 +95,7 @@ if (LINUX)
         ${LLIMAGE_LIBRARIES}
         ${LLMATH_LIBRARIES}
         ${LLRENDER_HEADLESS_LIBRARIES}
-        ${LLCACHE_LIBRARIES}
+        ${LLFILESYSTEM_LIBRARIES}
         ${LLWINDOW_HEADLESS_LIBRARIES}
         ${LLXML_LIBRARIES}
         fontconfig          # For FCInit and other FC* functions.
diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt
index e2c6026496..3a7a54e51d 100644
--- a/indra/llxml/CMakeLists.txt
+++ b/indra/llxml/CMakeLists.txt
@@ -5,13 +5,13 @@ project(llxml)
 include(00-Common)
 include(LLCommon)
 include(LLMath)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 
 include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     )
 include_directories(
     ${LLCOMMON_SYSTEM_INCLUDE_DIRS}
@@ -42,7 +42,7 @@ add_library (llxml ${llxml_SOURCE_FILES})
 # Libraries on which this library depends, needed for Linux builds
 # Sort by high-level to low-level
 target_link_libraries( llxml
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     ${EXPAT_LIBRARIES}
diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt
index 4a10076e6e..75621d7b75 100644
--- a/indra/mac_crash_logger/CMakeLists.txt
+++ b/indra/mac_crash_logger/CMakeLists.txt
@@ -8,7 +8,7 @@ include(LLCoreHttp)
 include(LLCrashLogger)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFilesystem)
 include(LLXML)
 include(Linking)
 include(LLSharedLibs)
@@ -19,7 +19,7 @@ include_directories(
     ${LLCOMMON_INCLUDE_DIRS}
     ${LLCRASHLOGGER_INCLUDE_DIRS}
     ${LLMATH_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     )
 include_directories(SYSTEM
@@ -68,7 +68,7 @@ find_library(COCOA_LIBRARY Cocoa)
 
 target_link_libraries(mac-crash-logger
     ${LLCRASHLOGGER_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${COCOA_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ccf7ad675f..cda2672e12 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -39,7 +39,7 @@ include(LLPlugin)
 include(LLPrimitive)
 include(LLRender)
 include(LLUI)
-include(LLCache)
+include(LLFileSystem)
 include(LLWindow)
 include(LLXML)
 include(NDOF)
@@ -84,7 +84,7 @@ include_directories(
     ${LLPRIMITIVE_INCLUDE_DIRS}
     ${LLRENDER_INCLUDE_DIRS}
     ${LLUI_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     ${LLLOGIN_INCLUDE_DIRS}
@@ -1995,7 +1995,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
     ${LLRENDER_LIBRARIES}
     ${FREETYPE_LIBRARIES}
     ${LLUI_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLMATH_LIBRARIES}
@@ -2467,7 +2467,7 @@ if (LL_TESTS)
   set(test_libs
     ${LLMESSAGE_LIBRARIES}
     ${WINDOWS_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     ${GOOGLEMOCK_LIBRARIES}
@@ -2482,7 +2482,7 @@ if (LL_TESTS)
 
   set(test_libs
     ${WINDOWS_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLMATH_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index f8e701794c..b3bff3611a 100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -8,12 +8,11 @@ include(LLCoreHttp)
 include(LLInventory)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLXML)
 include(Linking)
 include(Tut)
 include(LLAddBuildTest)
-
 include(GoogleMock)
 
 include_directories(
@@ -23,7 +22,7 @@ include_directories(
     ${LLMATH_INCLUDE_DIRS}
     ${LLMESSAGE_INCLUDE_DIRS}
     ${LLINVENTORY_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
     ${LSCRIPT_INCLUDE_DIRS}
     ${GOOGLEMOCK_INCLUDE_DIRS}
@@ -89,7 +88,7 @@ target_link_libraries(lltest
     ${LLINVENTORY_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
     ${LLMATH_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LSCRIPT_LIBRARIES}
     ${LLCOMMON_LIBRARIES}
diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt
index 811ec00a3d..1b187d624a 100644
--- a/indra/win_crash_logger/CMakeLists.txt
+++ b/indra/win_crash_logger/CMakeLists.txt
@@ -8,7 +8,7 @@ include(LLCoreHttp)
 include(LLCrashLogger)
 include(LLMath)
 include(LLMessage)
-include(LLCache)
+include(LLFileSystem)
 include(LLWindow)
 include(LLXML)
 include(Linking)
@@ -23,7 +23,7 @@ include_directories(
     ${LLMATH_INCLUDE_DIRS}
     ${LLWINDOW_INCLUDE_DIRS}
     ${LLXML_INCLUDE_DIRS}
-    ${LLCACHE_INCLUDE_DIRS}
+    ${LLFILESYSTEM_INCLUDE_DIRS}
     ${BREAKPAD_INCLUDE_DIRECTORIES}
     )
 include_directories(SYSTEM
@@ -76,7 +76,7 @@ target_link_libraries(windows-crash-logger
     ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
     ${LLCRASHLOGGER_LIBRARIES}
     ${LLWINDOW_LIBRARIES}
-    ${LLCACHE_LIBRARIES}
+    ${LLFILESYSTEM_LIBRARIES}
     ${LLXML_LIBRARIES}
     ${LLMESSAGE_LIBRARIES}
     ${LLMATH_LIBRARIES}
-- 
cgit v1.2.3