diff options
Diffstat (limited to 'indra/llvfs')
28 files changed, 2443 insertions, 1581 deletions
diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt index 05c7c7860c..67dce8c073 100644..100755 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llvfs/CMakeLists.txt @@ -8,10 +8,12 @@ include(UnixInstall) include_directories( ${LLCOMMON_INCLUDE_DIRS} + ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ) set(llvfs_SOURCE_FILES lldir.cpp + lldiriterator.cpp lllfsthread.cpp llpidlock.cpp llvfile.cpp @@ -24,6 +26,7 @@ set(llvfs_HEADER_FILES lldir.h lldirguard.h + lldiriterator.h lllfsthread.h llpidlock.h llvfile.h @@ -34,18 +37,20 @@ set(llvfs_HEADER_FILES if (DARWIN) LIST(APPEND llvfs_SOURCE_FILES lldir_mac.cpp) LIST(APPEND llvfs_HEADER_FILES lldir_mac.h) + LIST(APPEND llvfs_SOURCE_FILES llvfs_objc.mm) + LIST(APPEND llvfs_HEADER_FILES llvfs_objc.h) endif (DARWIN) if (LINUX) LIST(APPEND llvfs_SOURCE_FILES lldir_linux.cpp) LIST(APPEND llvfs_HEADER_FILES lldir_linux.h) - if (VIEWER AND INSTALL) + if (INSTALL) set_source_files_properties(lldir_linux.cpp PROPERTIES COMPILE_FLAGS "-DAPP_RO_DATA_DIR=\\\"${APP_SHARE_DIR}\\\"" ) - endif (VIEWER AND INSTALL) + endif (INSTALL) endif (LINUX) if (WINDOWS) @@ -60,22 +65,40 @@ list(APPEND llvfs_SOURCE_FILES ${llvfs_HEADER_FILES}) add_library (llvfs ${llvfs_SOURCE_FILES}) +set(vfs_BOOST_LIBRARIES + ${BOOST_FILESYSTEM_LIBRARY} + ${BOOST_SYSTEM_LIBRARY} + ) + +target_link_libraries(llvfs + ${LLCOMMON_LIBRARIES} + ${vfs_BOOST_LIBRARIES} + ) + if (DARWIN) include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) - target_link_libraries(llvfs ${CARBON_LIBRARY}) + find_library(COCOA_LIBRARY Cocoa) + target_link_libraries(llvfs ${COCOA_LIBRARY}) endif (DARWIN) # Add tests -include(LLAddBuildTest) -# UNIT TESTS -SET(llvfs_TEST_SOURCE_FILES - # none so far - ) -LL_ADD_PROJECT_UNIT_TESTS(llvfs "${llvfs_TEST_SOURCE_FILES}") - -# INTEGRATION TESTS -set(test_libs llmath llcommon llvfs ${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}") +if (LL_TESTS) + include(LLAddBuildTest) + # UNIT TESTS + SET(llvfs_TEST_SOURCE_FILES + lldiriterator.cpp + ) + + set_source_files_properties(lldiriterator.cpp + PROPERTIES + LL_TEST_ADDITIONAL_LIBRARIES "${vfs_BOOST_LIBRARIES}" + ) + LL_ADD_PROJECT_UNIT_TESTS(llvfs "${llvfs_TEST_SOURCE_FILES}") + + # INTEGRATION TESTS + set(test_libs llmath llcommon llvfs ${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/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index da4abde451..5f4fb8f4a0 100644..100755 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -2,31 +2,25 @@ * @file lldir.cpp * @brief implementation of directory utilities base class * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -46,6 +40,20 @@ #include "lltimer.h" // ms_sleep() #include "lluuid.h" +#include "lldiriterator.h" +#include "stringize.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; @@ -62,6 +70,15 @@ LLDir_Linux gDirUtil; 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(""), @@ -74,7 +91,9 @@ LLDir::LLDir() mOSCacheDir(""), mCAFile(""), mTempDir(""), - mDirDelimiter("/") // fallback to forward slash if not overridden + mDirDelimiter("/"), // fallback to forward slash if not overridden + mLanguage("en"), + mUserName("undefined") { } @@ -82,50 +101,118 @@ LLDir::~LLDir() { } - +std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname) +{ + //Returns a vector of fullpath filenames. + + boost::filesystem::path p (dirname); + 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; - while (getNextFileInDir(dirname, mask, filename, FALSE)) + + // File masks starting with "/" will match nothing, so we consider them invalid. + if (LLStringUtil::startsWith(mask, getDirDelimiter())) { - if ((filename == ".") || (filename == "..")) + 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; } - fullpath = dirname; - fullpath += getDirDelimiter(); - fullpath += filename; S32 retry_count = 0; while (retry_count < 5) { if (0 != LLFile::remove(fullpath)) { + retry_count++; result = errno; - llwarns << "Problem removing " << fullpath << " - errorcode: " - << result << " attempt " << retry_count << llendl; - ms_sleep(1000); + 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) { - llwarns << "Successfully removed " << fullpath << llendl; + LL_WARNS() << "Successfully removed " << fullpath << LL_ENDL; } break; - } - retry_count++; + } } 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 + { + boost::filesystem::path dir_path(dir_name); + 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, @@ -147,7 +234,11 @@ const std::string LLDir::findFile(const std::string& filename, const std::vector { if (!search_path_iter->empty()) { - std::string filename_and_path = (*search_path_iter) + getDirDelimiter() + filename; + 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; @@ -202,17 +293,42 @@ const std::string &LLDir::getLindenUserDir() const { if (mLindenUserDir.empty()) { - lldebugs << "getLindenUserDir() called early, we don't have the user name yet - returning empty string to caller" << llendl; + 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 +const std::string& LLDir::getChatLogsDir() const { return mChatLogsDir; } +void LLDir::setDumpDir( const std::string& path ) +{ + LLDir::sDumpDir = path; + if (! sDumpDir.empty() && sDumpDir.rbegin() == mDirDelimiter.rbegin() ) + { + sDumpDir.erase(sDumpDir.size() -1); + } +} + +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; @@ -253,12 +369,12 @@ std::string LLDir::buildSLOSCacheDir() const } else { - res = getOSUserAppDir() + mDirDelimiter + "cache"; + res = add(getOSUserAppDir(), "cache"); } } else { - res = getOSCacheDir() + mDirDelimiter + "SecondLife"; + res = add(getOSCacheDir(), "SecondLife"); } return res; } @@ -281,19 +397,24 @@ 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::getUserSkinDir() const +const std::string &LLDir::getUserDefaultSkinDir() const { - return mUserSkinDir; + return mUserDefaultSkinDir; } -const std::string& LLDir::getDefaultSkinDir() const +const std::string &LLDir::getUserSkinDir() const { - return mDefaultSkinDir; + return mUserSkinDir; } const std::string LLDir::getSkinBaseDir() const @@ -306,6 +427,44 @@ 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); @@ -326,15 +485,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_APP_SETTINGS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "app_settings"; + prefix = add(getAppRODataDir(), "app_settings"); break; case LL_PATH_CHARACTER: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "character"; + prefix = add(getAppRODataDir(), "character"); break; case LL_PATH_HELP: @@ -345,17 +500,27 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd prefix = getCacheDir(); break; + case LL_PATH_DUMP: + prefix=getDumpDir(); + break; + case LL_PATH_USER_SETTINGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "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. + // 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; @@ -369,9 +534,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "logs"; + prefix = add(getOSUserAppDir(), "logs"); break; case LL_PATH_TEMP: @@ -387,11 +550,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SKIN: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; - prefix += mDirDelimiter; - prefix += "skins"; + prefix = getUserSkinDir(); break; case LL_PATH_SKINS: @@ -399,9 +558,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOCAL_ASSETS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "local_assets"; + prefix = add(getAppRODataDir(), "local_assets"); break; case LL_PATH_EXECUTABLE: @@ -409,57 +566,36 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_FONTS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "fonts"; + prefix = add(getAppRODataDir(), "fonts"); break; default: llassert(0); } - std::string filename = in_filename; - if (!subdir2.empty()) - { - filename = subdir2 + mDirDelimiter + filename; - } - - if (!subdir1.empty()) - { - filename = subdir1 + mDirDelimiter + filename; - } - if (prefix.empty()) { - llwarns << "prefix is empty, possible bad filename" << llendl; + LL_WARNS() << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "': prefix is empty, possible bad filename" << LL_ENDL; } - - std::string expanded_filename; - if (!filename.empty()) - { - if (!prefix.empty()) - { - expanded_filename += prefix; - expanded_filename += mDirDelimiter; - expanded_filename += filename; - } - else - { - expanded_filename = filename; - } - } - else if (!prefix.empty()) - { - // Directory only, no file name. - expanded_filename = prefix; - } - else + + std::string expanded_filename = add(add(prefix, subdir1), subdir2); + if (expanded_filename.empty() && in_filename.empty()) { - expanded_filename.assign(""); + return ""; } - - //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; - + // 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; } @@ -499,31 +635,207 @@ std::string LLDir::getExtension(const std::string& filepath) const return exten; } -std::string LLDir::findSkinnedFilename(const std::string &filename) const +std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint) const { - return findSkinnedFilename("", "", filename); + // 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) const +std::string LLDir::findSkinnedFilename(const std::string &subdir, + const std::string &filename, + ESkinConstraint constraint) const { - return findSkinnedFilename("", subdir, filename); + // 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(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const +// 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 { - // generate subdirectory path fragment, e.g. "/foo/bar", "/foo", "" - std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1) - + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2); + BOOST_FOREACH(std::string skindir, mSearchSkinDirs) + { + std::string subdir_path(add(skindir, subdir)); + BOOST_FOREACH(std::string subsubdir, subsubdirs) + { + std::string full_path(add(add(subdir_path, subsubdir), filename)); + if (fileExists(full_path)) + { + function(subsubdir, full_path); + } + } + } +} - std::vector<std::string> search_paths; - - search_paths.push_back(getUserSkinDir() + subdirs); // first look in user skin override - search_paths.push_back(getSkinDir() + subdirs); // then in current skin - search_paths.push_back(getDefaultSkinDir() + subdirs); // then default skin - search_paths.push_back(getCacheDir() + subdirs); // and last in preload directory +// 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); +} - std::string found_file = findFile(filename, search_paths); - return found_file; +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; + + // 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); + } + } + + // Build results vector. + std::vector<std::string> results; + // 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 @@ -534,12 +846,7 @@ std::string LLDir::getTempFilename() const random_uuid.generate(); random_uuid.toString(uuid_str); - std::string temp_filename = getTempDir(); - temp_filename += mDirDelimiter; - temp_filename += uuid_str; - temp_filename += ".tmp"; - - return temp_filename; + return add(getTempDir(), uuid_str + ".tmp"); } // static @@ -565,27 +872,21 @@ std::string LLDir::getForbiddenFileChars() return "\\/:*?\"<>|"; } -void LLDir::setLindenUserDir(const std::string &first, const std::string &last) +void LLDir::setLindenUserDir(const std::string &username) { - // if both first and last aren't set, that's bad. - if (!first.empty() && !last.empty()) + // 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 firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += firstlower; - mLindenUserDir += "_"; - mLindenUserDir += lastlower; - llinfos << "Got name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; + std::string userlower(username); + LLStringUtil::toLower(userlower); + LLStringUtil::replaceChar(userlower, ' ', '_'); + mLindenUserDir = add(getOSUserAppDir(), userlower); } else { - llerrs << "Invalid name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; + LL_ERRS() << "NULL name for LLDir::setLindenUserDir" << LL_ENDL; } dumpCurrentDirectories(); @@ -599,52 +900,88 @@ void LLDir::setChatLogsDir(const std::string &path) } else { - llwarns << "Invalid name for LLDir::setChatLogsDir" << llendl; + LL_WARNS() << "Invalid name for LLDir::setChatLogsDir" << LL_ENDL; } } -void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) +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 (!first.empty() && !last.empty()) + if (!username.empty()) { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += firstlower; - mPerAccountChatLogsDir += "_"; - mPerAccountChatLogsDir += lastlower; + std::string userlower(username); + LLStringUtil::toLower(userlower); + LLStringUtil::replaceChar(userlower, ' ', '_'); + + mUserName = userlower; + updatePerAccountChatLogsDir(); } else { - llwarns << "Invalid name for LLDir::setPerAccountChatLogsDir" << llendl; + LL_ERRS() << "NULL name for LLDir::setPerAccountChatLogsDir" << LL_ENDL; } } -void LLDir::setSkinFolder(const std::string &skin_folder) +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) { - mSkinDir = getSkinBaseDir(); - mSkinDir += mDirDelimiter; - mSkinDir += skin_folder; + LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'" + << LL_ENDL; + mSkinName = skin_folder; + mLanguage = language; - // user modifications to current skin - // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle - mUserSkinDir = getOSUserAppDir(); - mUserSkinDir += mDirDelimiter; - mUserSkinDir += "skins"; - mUserSkinDir += mDirDelimiter; - mUserSkinDir += skin_folder; + // 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(); - mDefaultSkinDir += mDirDelimiter; - mDefaultSkinDir += "default"; + 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) @@ -658,7 +995,7 @@ bool LLDir::setCacheDir(const std::string &path) else { LLFile::mkdir(path); - std::string tempname = path + mDirDelimiter + "temp"; + std::string tempname = add(path, "temp"); LLFILE* file = LLFile::fopen(tempname,"wt"); if (file) { @@ -673,24 +1010,75 @@ bool LLDir::setCacheDir(const std::string &path) void LLDir::dumpCurrentDirectories() { - LL_DEBUGS2("AppInit","Directories") << "Current Directories:" << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << "Current Directories:" << LL_ENDL; + + LL_DEBUGS("AppInit","Directories") << " CurPath: " << getCurPath() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " AppName: " << getAppName() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " ExecutableFilename: " << getExecutableFilename() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " ExecutableDir: " << getExecutableDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " ExecutablePathAndName: " << getExecutablePathAndName() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " WorkingDir: " << getWorkingDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " AppRODataDir: " << getAppRODataDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " OSUserDir: " << getOSUserDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " OSUserAppDir: " << getOSUserAppDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " LindenUserDir: " << getLindenUserDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " TempDir: " << getTempDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " CAFile: " << getCAFile() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " SkinBaseDir: " << getSkinBaseDir() << LL_ENDL; + LL_DEBUGS("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; +} - LL_DEBUGS2("AppInit","Directories") << " CurPath: " << getCurPath() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " AppName: " << getAppName() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " ExecutableFilename: " << getExecutableFilename() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " ExecutableDir: " << getExecutableDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " ExecutablePathAndName: " << getExecutablePathAndName() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " WorkingDir: " << getWorkingDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " AppRODataDir: " << getAppRODataDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " OSUserDir: " << getOSUserDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " OSUserAppDir: " << getOSUserAppDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " LindenUserDir: " << getLindenUserDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " TempDir: " << getTempDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " CAFile: " << getCAFile() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " SkinBaseDir: " << getSkinBaseDir() << LL_ENDL; - LL_DEBUGS2("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; +std::string LLDir::add(const std::string& path, const std::string& name) const +{ + std::string destpath(path); + append(destpath, name); + return destpath; +} + +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, 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) { @@ -707,13 +1095,13 @@ void dir_exists_or_crash(const std::string &dir_name) { if(0 != LLFile::mkdir(dir_name, 0700)) // octal { - llerrs << "Unable to create directory: " << dir_name << llendl; + LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL; } } else { - llerrs << "Unable to stat: " << dir_name << " errno = " << stat_rv - << llendl; + LL_ERRS() << "Unable to stat: " << dir_name << " errno = " << stat_rv + << LL_ENDL; } } else @@ -721,7 +1109,7 @@ void dir_exists_or_crash(const std::string &dir_name) // data_dir exists, make sure it's a directory. if(!S_ISDIR(dir_stat.st_mode)) { - llerrs << "Data directory collision: " << dir_name << llendl; + LL_ERRS() << "Data directory collision: " << dir_name << LL_ENDL; } } #endif diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 9067d75bac..b219c6e29f 100644..100755 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -1,32 +1,26 @@ -/** +/** * @file lldir.h * @brief Definition of directory utilities class * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -38,7 +32,7 @@ #define MAX_PATH MAXPATHLEN #endif -// these numbers *may* get serialized (really??), so we need to be explicit +// these numbers are read from settings_files.xml, so we need to be explicit typedef enum ELLPath { LL_PATH_NONE = 0, @@ -59,10 +53,11 @@ typedef enum ELLPath 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: @@ -77,13 +72,11 @@ class LLDir 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 U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0; - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) = 0; - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) = 0; virtual std::string getCurPath() = 0; - virtual BOOL fileExists(const std::string &filename) const = 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; @@ -101,17 +94,20 @@ class LLDir 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 &getDefaultSkinDir() const; // folder for default skin. e.g. c:\program files\second life\skins\default 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; @@ -124,10 +120,61 @@ class LLDir 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(), getSkinDir(), getDefaultSkinDir() - std::string findSkinnedFilename(const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir, const std::string &filename) const; - std::string findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const; + // 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; @@ -135,19 +182,44 @@ class LLDir // 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 &first, const std::string &last); // Set the per user chat log directory. - virtual void setLindenUserDir(const std::string &first, const std::string &last); // Set the linden user dir to this user's dir - virtual void setSkinFolder(const std::string &skin_folder); + 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(); - + // 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; + /// Append specified @a name to @a path, separated by getDirDelimiter() + /// if both are non-empty. Return result, leaving @a path unmodified. + std::string add(const std::string& path, const std::string& name) const; + 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 @@ -165,11 +237,21 @@ protected: 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 mSkinDir; // Location for current skin info. 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); diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index a9736560ec..7a4034c228 100644..100755 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -2,31 +2,25 @@ * @file lldir_linux.cpp * @brief Implementation of directory utilities for linux * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -55,7 +49,7 @@ static std::string getCurrentUserHome(char* fallback) } else { - llinfos << "Couldn't detect home directory from passwd - trying $HOME" << llendl; + LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL; const char *const home_env = getenv("HOME"); /* Flawfinder: ignore */ if (home_env) { @@ -63,7 +57,7 @@ static std::string getCurrentUserHome(char* fallback) } else { - llwarns << "Couldn't detect home directory! Falling back to " << fallback << llendl; + LL_WARNS() << "Couldn't detect home directory! Falling back to " << fallback << LL_ENDL; } } @@ -82,11 +76,11 @@ LLDir_Linux::LLDir_Linux() if (getcwd(tmp_str, LL_MAX_PATH) == NULL) { strcpy(tmp_str, "/tmp"); - llwarns << "Could not get current directory; changing to " - << tmp_str << llendl; + LL_WARNS() << "Could not get current directory; changing to " + << tmp_str << LL_ENDL; if (chdir(tmp_str) == -1) { - llerrs << "Could not change directory to " << tmp_str << llendl; + LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL; } } @@ -99,13 +93,13 @@ LLDir_Linux::LLDir_Linux() #else mAppRODataDir = tmp_str; #endif - U32 indra_pos = mExecutableDir.find("/indra"); - if (indra_pos != std::string::npos) + 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, indra_pos) + "/indra/newview/skins"; - llinfos << "Running in dev checkout with mSkinBaseDir " - << mSkinBaseDir << llendl; + mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins"; + LL_INFOS() << "Running in dev checkout with mSkinBaseDir " + << mSkinBaseDir << LL_ENDL; } else { @@ -193,8 +187,8 @@ void LLDir_Linux::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl; - llwarns << "Default to base dir" << mOSUserDir << llendl; + LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL; + LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL; mOSUserAppDir = mOSUserDir; } } @@ -204,7 +198,7 @@ void LLDir_Linux::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL; } } @@ -213,7 +207,7 @@ void LLDir_Linux::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL; } } @@ -222,7 +216,7 @@ void LLDir_Linux::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL; } } @@ -248,127 +242,19 @@ U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string & return (file_count); } -// get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ - glob_t g; - BOOL result = FALSE; - fname = ""; - - if(!(dirname == mCurrentDir)) - { - // different dir specified, close old search - mCurrentDirIndex = -1; - mCurrentDirCount = -1; - mCurrentDir = dirname; - } - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - if(g.gl_pathc > 0) - { - if((int)g.gl_pathc != mCurrentDirCount) - { - // Number of matches has changed since the last search, meaning a file has been added or deleted. - // Reset the index. - mCurrentDirIndex = -1; - mCurrentDirCount = g.gl_pathc; - } - - mCurrentDirIndex++; - - if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - - if(mCurrentDirIndex < (int)g.gl_pathc) - { -// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; - - // The API wants just the filename, not the full path. - //fname = g.gl_pathv[mCurrentDirIndex]; - - char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/'); - - if(s == NULL) - s = g.gl_pathv[mCurrentDirIndex]; - else if(s[0] == '/') - s++; - - fname = s; - - result = TRUE; - } - } - - globfree(&g); - } - - return(result); -} - - -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Linux::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - DIR *dirp; - dirent *entryp = NULL; - - fname = ""; - - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select file #" << which_file << llendl; - - // which_file now indicates the (zero-based) index to which file to play - - if (!((dirp = opendir(dirname.c_str())))) - { - while (which_file--) - { - if (!((entryp = readdir(dirp)))) - { - return; - } - } - - if ((!which_file) && entryp) - { - fname = entryp->d_name; - } - - closedir(dirp); - } -} - std::string LLDir_Linux::getCurPath() { char tmp_str[LL_MAX_PATH]; /* Flawfinder: ignore */ if (getcwd(tmp_str, LL_MAX_PATH) == NULL) { - llwarns << "Could not get current directory" << llendl; + 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 +bool LLDir_Linux::fileExists(const std::string &filename) const { struct stat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h index 1fec15c5c6..e83a020ba4 100644..100755 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -1,35 +1,33 @@ /** * @file lldir_linux.h - * @brief Definition of directory utilities class for linux + * @brief Definition of directory utilities class for linux * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 @@ -46,12 +44,10 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: + virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); @@ -60,7 +56,7 @@ private: DIR *mDirp; int mCurrentDirIndex; int mCurrentDirCount; - std::string mCurrentDir; + std::string mCurrentDir; }; #endif // LL_LLDIR_LINUX_H diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 7bc6f63e1f..4038c92465 100644..100755 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -2,33 +2,27 @@ * @file lldir_mac.cpp * @brief Implementation of directory utilities for Mac OS X * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if LL_DARWIN @@ -41,73 +35,27 @@ #include <sys/stat.h> #include <unistd.h> #include <glob.h> - -#include <Carbon/Carbon.h> - -// -------------------------------------------------------------------------------- - -static OSStatus CFCreateDirectory(FSRef *parentRef, CFStringRef name, FSRef *newRef) -{ - OSStatus result = noErr; - HFSUniStr255 uniStr; - - uniStr.length = CFStringGetLength(name); - CFStringGetCharacters(name, CFRangeMake(0, uniStr.length), uniStr.unicode); - result = FSMakeFSRefUnicode(parentRef, uniStr.length, uniStr.unicode, kTextEncodingMacRoman, newRef); - if (result != noErr) - { - result = FSCreateDirectoryUnicode(parentRef, uniStr.length, uniStr.unicode, 0, NULL, newRef, NULL, NULL); - } - - return result; -} +#include <boost/filesystem.hpp> +#include "llvfs_objc.h" // -------------------------------------------------------------------------------- -static void CFStringRefToLLString(CFStringRef stringRef, std::string &llString, bool releaseWhenDone) +static bool CreateDirectory(const std::string &parent, + const std::string &child, + std::string *fullname) { - if (stringRef) - { - long stringSize = CFStringGetLength(stringRef) + 1; - long bufferSize = CFStringGetMaximumSizeForEncoding(stringSize,kCFStringEncodingUTF8); - char* buffer = new char[bufferSize]; - memset(buffer, 0, bufferSize); - if (CFStringGetCString(stringRef, buffer, bufferSize, kCFStringEncodingUTF8)) - llString = buffer; - delete[] buffer; - if (releaseWhenDone) - CFRelease(stringRef); - } -} - -// -------------------------------------------------------------------------------- - -static void CFURLRefToLLString(CFURLRef urlRef, std::string &llString, bool releaseWhenDone) -{ - if (urlRef) - { - CFURLRef absoluteURLRef = CFURLCopyAbsoluteURL(urlRef); - if (absoluteURLRef) - { - CFStringRef stringRef = CFURLCopyFileSystemPath(absoluteURLRef, kCFURLPOSIXPathStyle); - CFStringRefToLLString(stringRef, llString, true); - CFRelease(absoluteURLRef); - } - if (releaseWhenDone) - CFRelease(urlRef); - } -} - -// -------------------------------------------------------------------------------- - -static void FSRefToLLString(FSRef *fsRef, std::string &llString) -{ - OSStatus error = noErr; - char path[MAX_PATH]; - - error = FSRefMakePath(fsRef, (UInt8*) path, sizeof(path)); - if (error == noErr) - llString = path; + + 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; } // -------------------------------------------------------------------------------- @@ -115,35 +63,28 @@ static void FSRefToLLString(FSRef *fsRef, std::string &llString) LLDir_Mac::LLDir_Mac() { mDirDelimiter = "/"; - mCurrentDirIndex = -1; - mCurrentDirCount = -1; - - CFBundleRef mainBundleRef = NULL; - CFURLRef executableURLRef = NULL; - CFStringRef stringRef = NULL; - OSStatus error = noErr; - FSRef fileRef; - CFStringRef secondLifeString = CFSTR("SecondLife"); - - mainBundleRef = CFBundleGetMainBundle(); - - executableURLRef = CFBundleCopyExecutableURL(mainBundleRef); - - if (executableURLRef != NULL) + + 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 - CFURLRefToLLString(executableURLRef, mExecutablePathAndName, false); - - // mExecutableFilename - stringRef = CFURLCopyLastPathComponent(executableURLRef); - CFStringRefToLLString(stringRef, mExecutableFilename, true); - - // mExecutableDir - CFURLRef executableParentURLRef = CFURLCreateCopyDeletingLastPathComponent(NULL, executableURLRef); - CFURLRefToLLString(executableParentURLRef, mExecutableDir, true); + 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 @@ -152,18 +93,15 @@ LLDir_Mac::LLDir_Mac() // MBW -- This keeps the mac application from finding other things. // If this is really for skins, it should JUST apply to skins. - - CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef); - CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true); - - U32 indra_pos = mExecutableDir.find("/indra"); - if (indra_pos != std::string::npos) + + U32 build_dir_pos = mExecutableDir.rfind("/build-darwin-"); + if (build_dir_pos != std::string::npos) { // ...we're in a dev checkout - mSkinBaseDir = mExecutableDir.substr(0, indra_pos) + mSkinBaseDir = mExecutableDir.substr(0, build_dir_pos) + "/indra/newview/skins"; - llinfos << "Running in dev checkout with mSkinBaseDir " - << mSkinBaseDir << llendl; + LL_INFOS() << "Running in dev checkout with mSkinBaseDir " + << mSkinBaseDir << LL_ENDL; } else { @@ -172,55 +110,50 @@ LLDir_Mac::LLDir_Mac() } // mOSUserDir - error = FSFindFolder(kUserDomain, kApplicationSupportFolderType, true, &fileRef); - if (error == noErr) - { - FSRef newFileRef; - - // Create the directory - error = CFCreateDirectory(&fileRef, secondLifeString, &newFileRef); - if (error == noErr) - { - // Save the full path to the folder - FSRefToLLString(&newFileRef, mOSUserDir); - - // Create our sub-dirs - (void) CFCreateDirectory(&newFileRef, CFSTR("data"), NULL); - //(void) CFCreateDirectory(&newFileRef, CFSTR("cache"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("logs"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("user_settings"), NULL); - (void) CFCreateDirectory(&newFileRef, CFSTR("browser_profile"), NULL); - } - } - + 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 - FSRef cacheDirRef; - error = FSFindFolder(kUserDomain, kCachedDataFolderType, true, &cacheDirRef); - if (error == noErr) + std::string *cachedir = getSystemCacheFolder(); + + if (cachedir) + { - FSRefToLLString(&cacheDirRef, mOSCacheDir); - (void)CFCreateDirectory(&cacheDirRef, CFSTR("SecondLife"),NULL); + 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 - error = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, true, &fileRef); - if (error == noErr) - { - FSRef tempRef; - error = CFCreateDirectory(&fileRef, secondLifeString, &tempRef); - if (error == noErr) - FSRefToLLString(&tempRef, 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"; - - CFRelease(executableURLRef); - executableURLRef = NULL; } } @@ -241,192 +174,25 @@ void LLDir_Mac::initAppDirs(const std::string &app_name, mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; } mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); - - //dumpCurrentDirectories(); -} - -U32 LLDir_Mac::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); -} - -// get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ - glob_t g; - BOOL result = FALSE; - fname = ""; - - if(!(dirname == mCurrentDir)) - { - // different dir specified, close old search - mCurrentDirIndex = -1; - mCurrentDirCount = -1; - mCurrentDir = dirname; - } - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - if(g.gl_pathc > 0) - { - if(g.gl_pathc != mCurrentDirCount) - { - // Number of matches has changed since the last search, meaning a file has been added or deleted. - // Reset the index. - mCurrentDirIndex = -1; - mCurrentDirCount = g.gl_pathc; - } - - mCurrentDirIndex++; - - if((mCurrentDirIndex >= g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - - if(mCurrentDirIndex < g.gl_pathc) - { -// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; - - // The API wants just the filename, not the full path. - //fname = g.gl_pathv[mCurrentDirIndex]; - - char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/'); - - if(s == NULL) - s = g.gl_pathv[mCurrentDirIndex]; - else if(s[0] == '/') - s++; - - fname = s; - - result = TRUE; - } - } - - globfree(&g); - } - - return(result); -} - -// get a random file in the directory -void LLDir_Mac::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 which_file; - glob_t g; - fname = ""; - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - if(g.gl_pathc > 0) - { - - which_file = ll_rand(g.gl_pathc); - -// llinfos << "getRandomFileInDir: returning number " << which_file << ", path is " << g.gl_pathv[which_file] << llendl; - // The API wants just the filename, not the full path. - //fname = g.gl_pathv[which_file]; - - char *s = strrchr(g.gl_pathv[which_file], '/'); - - if(s == NULL) - s = g.gl_pathv[which_file]; - else if(s[0] == '/') - s++; - - fname = s; - } - - globfree(&g); - } -} - -S32 LLDir_Mac::deleteFilesInDir(const std::string &dirname, const std::string &mask) -{ - glob_t g; - S32 result = 0; - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - int i; - - for(i = 0; i < g.gl_pathc; i++) - { -// llinfos << "deleteFilesInDir: deleting number " << i << ", path is " << g.gl_pathv[i] << llendl; - - if(unlink(g.gl_pathv[i]) != 0) - { - result = errno; - - llwarns << "Problem removing " << g.gl_pathv[i] << " - errorcode: " - << result << llendl; - } - } - - globfree(&g); - } - - return(result); } std::string LLDir_Mac::getCurPath() { - char tmp_str[LL_MAX_PATH]; /* Flawfinder: ignore */ - getcwd(tmp_str, LL_MAX_PATH); - return tmp_str; + return boost::filesystem::path( boost::filesystem::current_path() ).string(); } -BOOL LLDir_Mac::fileExists(const std::string &filename) const +bool LLDir_Mac::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; - } + return boost::filesystem::exists(filename); } /*virtual*/ std::string LLDir_Mac::getLLPluginLauncher() { return gDirUtilp->getAppRODataDir() + gDirUtilp->getDirDelimiter() + - "SLPlugin"; + "SLPlugin.app/Contents/MacOS/SLPlugin"; } /*virtual*/ std::string LLDir_Mac::getLLPluginFilename(std::string base_name) diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index 834acc7262..558727ebbc 100644..100755 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -2,33 +2,31 @@ * @file lldir_mac.h * @brief Definition of directory utilities class for Mac OS X * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 @@ -45,21 +43,12 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: - virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask); + virtual std::string getCurPath(); - virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &ask, std::string &fname); - virtual BOOL fileExists(const std::string &filename) const; + virtual bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); - -private: - int mCurrentDirIndex; - int mCurrentDirCount; - std::string mCurrentDir; }; #endif // LL_LLDIR_MAC_H diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index 8ac5a41e93..b43b2f27ce 100644..100755 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -2,31 +2,25 @@ * @file fmodwrapper.cpp * @brief dummy source file for building a shared library to wrap libfmod.a * - * $LicenseInfo:firstyear=2005&license=viewergpl$ - * - * Copyright (c) 2005-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -58,7 +52,7 @@ static std::string getCurrentUserHome(char* fallback) } else { - llinfos << "Couldn't detect home directory from passwd - trying $HOME" << llendl; + LL_INFOS() << "Couldn't detect home directory from passwd - trying $HOME" << LL_ENDL; const char *const home_env = getenv("HOME"); /* Flawfinder: ignore */ if (home_env) { @@ -66,7 +60,7 @@ static std::string getCurrentUserHome(char* fallback) } else { - llwarns << "Couldn't detect home directory! Falling back to " << fallback << llendl; + LL_WARNS() << "Couldn't detect home directory! Falling back to " << fallback << LL_ENDL; } } @@ -85,11 +79,11 @@ LLDir_Solaris::LLDir_Solaris() if (getcwd(tmp_str, LL_MAX_PATH) == NULL) { strcpy(tmp_str, "/tmp"); - llwarns << "Could not get current directory; changing to " - << tmp_str << llendl; + LL_WARNS() << "Could not get current directory; changing to " + << tmp_str << LL_ENDL; if (chdir(tmp_str) == -1) { - llerrs << "Could not change directory to " << tmp_str << llendl; + LL_ERRS() << "Could not change directory to " << tmp_str << LL_ENDL; } } @@ -107,12 +101,12 @@ LLDir_Solaris::LLDir_Solaris() sprintf(path, "/proc/%d/psinfo", (int)getpid()); int proc_fd = -1; if((proc_fd = open(path, O_RDONLY)) == -1){ - llwarns << "unable to open " << path << llendl; + 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)){ - llwarns << "Unable to read " << path << llendl; + LL_WARNS() << "Unable to read " << path << LL_ENDL; close(proc_fd); return; } @@ -120,13 +114,13 @@ LLDir_Solaris::LLDir_Solaris() close(proc_fd); mExecutableFilename = strdup(proc_psinfo.pr_fname); - llinfos << "mExecutableFilename = [" << mExecutableFilename << "]" << llendl; + 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){ - llwarns << "Unable to read link from " << path << llendl; + LL_WARNS() << "Unable to read link from " << path << LL_ENDL; return; } @@ -136,7 +130,7 @@ LLDir_Solaris::LLDir_Solaris() *p = NULL; mExecutablePathAndName = strdup(execpath); - llinfos << "mExecutablePathAndName = [" << mExecutablePathAndName << "]" << llendl; + 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. @@ -157,7 +151,7 @@ LLDir_Solaris::LLDir_Solaris() *s = (char)NULL; mExecutableDir = strdup(execpath); - llinfos << "mExecutableDir = [" << mExecutableDir << "]" << llendl; + LL_INFOS() << "mExecutableDir = [" << mExecutableDir << "]" << LL_ENDL; } } @@ -211,8 +205,8 @@ void LLDir_Solaris::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl; - llwarns << "Default to base dir" << mOSUserDir << llendl; + LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL; + LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL; mOSUserAppDir = mOSUserDir; } } @@ -222,7 +216,7 @@ void LLDir_Solaris::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL; } } @@ -231,7 +225,7 @@ void LLDir_Solaris::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL; } } @@ -240,7 +234,7 @@ void LLDir_Solaris::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL; } } @@ -266,127 +260,19 @@ U32 LLDir_Solaris::countFilesInDir(const std::string &dirname, const std::string return (file_count); } -// get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Solaris::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ - glob_t g; - BOOL result = FALSE; - fname = ""; - - if(!(dirname == mCurrentDir)) - { - // different dir specified, close old search - mCurrentDirIndex = -1; - mCurrentDirCount = -1; - mCurrentDir = dirname; - } - - std::string tmp_str; - tmp_str = dirname; - tmp_str += mask; - - if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0) - { - if(g.gl_pathc > 0) - { - if((int)g.gl_pathc != mCurrentDirCount) - { - // Number of matches has changed since the last search, meaning a file has been added or deleted. - // Reset the index. - mCurrentDirIndex = -1; - mCurrentDirCount = g.gl_pathc; - } - - mCurrentDirIndex++; - - if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap) - { - mCurrentDirIndex = 0; - } - - if(mCurrentDirIndex < (int)g.gl_pathc) - { -// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl; - - // The API wants just the filename, not the full path. - //fname = g.gl_pathv[mCurrentDirIndex]; - - char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/'); - - if(s == NULL) - s = g.gl_pathv[mCurrentDirIndex]; - else if(s[0] == '/') - s++; - - fname = s; - - result = TRUE; - } - } - - globfree(&g); - } - - return(result); -} - - -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Solaris::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - DIR *dirp; - dirent *entryp = NULL; - - fname = ""; - - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select file #" << which_file << llendl; - - // which_file now indicates the (zero-based) index to which file to play - - if (!((dirp = opendir(dirname.c_str())))) - { - while (which_file--) - { - if (!((entryp = readdir(dirp)))) - { - return; - } - } - - if ((!which_file) && entryp) - { - fname = entryp->d_name; - } - - closedir(dirp); - } -} - std::string LLDir_Solaris::getCurPath() { char tmp_str[LL_MAX_PATH]; /* Flawfinder: ignore */ if (getcwd(tmp_str, LL_MAX_PATH) == NULL) { - llwarns << "Could not get current directory" << llendl; + 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 +bool LLDir_Solaris::fileExists(const std::string &filename) const { struct stat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_solaris.h b/indra/llvfs/lldir_solaris.h index 1fa8348355..c6dac57e14 100644..100755 --- a/indra/llvfs/lldir_solaris.h +++ b/indra/llvfs/lldir_solaris.h @@ -2,34 +2,32 @@ * @file fmodwrapper.cpp * @brief dummy source file for building a shared library to wrap libfmod.a * - * $LicenseInfo:firstyear=2005&license=viewergpl$ - * - * Copyright (c) 2005-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 @@ -46,18 +44,16 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: + virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; private: DIR *mDirp; int mCurrentDirIndex; int mCurrentDirCount; - std::string mCurrentDir; + std::string mCurrentDir; }; #endif // LL_LLDIR_SOLARIS_H diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 4eb10c842b..ebc8fdca33 100644..100755 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -2,31 +2,25 @@ * @file lldir_win32.cpp * @brief Implementation of directory utilities for windows * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -87,10 +81,11 @@ LLDir_Win32::LLDir_Win32() // fprintf(stderr, "mTempDir = <%s>",mTempDir); -#if 1 - // Don't use the real app path for now, as we'll have to add parsing to detect if - // we're in a developer tree, which has a different structure from the installed product. + // 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) { @@ -106,36 +101,39 @@ LLDir_Win32::LLDir_Win32() { mExecutableFilename = mExecutablePathAndName; } - GetCurrentDirectory(MAX_PATH, w_str); - mWorkingDir = utf16str_to_utf8str(llutf16string(w_str)); } else { fprintf(stderr, "Couldn't get APP path, assuming current directory!"); - GetCurrentDirectory(MAX_PATH, w_str); - mExecutableDir = utf16str_to_utf8str(llutf16string(w_str)); + mExecutableDir = mWorkingDir; // Assume it's the current directory } -#else - GetCurrentDirectory(MAX_PATH, w_str); - mExecutableDir = utf16str_to_utf8str(llutf16string(w_str)); -#endif - mAppRODataDir = "."; + // mAppRODataDir = "."; + + // Determine the location of the App-Read-Only-Data + // Try the working directory then the exe's dir. + mAppRODataDir = mWorkingDir; - mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; - if (mExecutableDir.find("indra") == std::string::npos) +// 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(mAppRODataDir + mDirDelimiter + "skins")) { - // Running from installed directory. Make sure current - // directory isn't something crazy (e.g. if invoking from - // command line). - SetCurrentDirectory(utf8str_to_utf16str(mExecutableDir).c_str()); - GetCurrentDirectory(MAX_PATH, w_str); - mWorkingDir = utf16str_to_utf8str(llutf16string(w_str)); + // What? No skins in the working dir? + // Try the executable's directory. + mAppRODataDir = mExecutableDir; } - llinfos << "mAppRODataDir = " << mAppRODataDir << llendl; + +// LL_INFOS() << "mAppRODataDir = " << mAppRODataDir << LL_ENDL; + + mSkinBaseDir = mAppRODataDir + mDirDelimiter + "skins"; // Build the default cache directory mDefaultCacheDir = buildSLOSCacheDir(); @@ -146,7 +144,7 @@ LLDir_Win32::LLDir_Win32() { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_CACHE dir " << mDefaultCacheDir << llendl; + LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << mDefaultCacheDir << LL_ENDL; } } @@ -178,8 +176,8 @@ void LLDir_Win32::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl; - llwarns << "Default to base dir" << mOSUserDir << llendl; + LL_WARNS() << "Couldn't create app user dir " << mOSUserAppDir << LL_ENDL; + LL_WARNS() << "Default to base dir" << mOSUserDir << LL_ENDL; mOSUserAppDir = mOSUserDir; } } @@ -190,7 +188,7 @@ void LLDir_Win32::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << LL_ENDL; } } @@ -199,7 +197,7 @@ void LLDir_Win32::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << LL_ENDL; } } @@ -208,18 +206,10 @@ void LLDir_Win32::initAppDirs(const std::string &app_name, { if (errno != EEXIST) { - llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl; + LL_WARNS() << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << LL_ENDL; } } - res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SKIN,"")); - if (res == -1) - { - if (errno != EEXIST) - { - llwarns << "Couldn't create LL_PATH_SKINS dir " << getExpandedFilename(LL_PATH_USER_SKIN,"") << llendl; - } - } mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); } @@ -250,122 +240,6 @@ U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string & return (file_count); } - -// get the next file in the directory -// automatically wrap if we've hit the end -BOOL LLDir_Win32::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ - llutf16string dirnamew = utf8str_to_utf16str(dirname); - return getNextFileInDir(dirnamew, mask, fname, wrap); - -} - -BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap) -{ - WIN32_FIND_DATAW FileData; - - fname = ""; - llutf16string pathname = dirname; - pathname += utf8str_to_utf16str(mask); - - if (pathname != mCurrentDir) - { - // different dir specified, close old search - if (mCurrentDir[0]) - { - FindClose(mDirSearch_h); - } - mCurrentDir = pathname; - - // and open new one - // Check error opening Directory structure - if ((mDirSearch_h = FindFirstFile(pathname.c_str(), &FileData)) == INVALID_HANDLE_VALUE) - { -// llinfos << "Unable to locate first file" << llendl; - return(FALSE); - } - } - else // get next file in list - { - // Find next entry - if (!FindNextFile(mDirSearch_h, &FileData)) - { - if (GetLastError() == ERROR_NO_MORE_FILES) - { - // No more files, so reset to beginning of directory - FindClose(mDirSearch_h); - mCurrentDir[0] = NULL; - - if (wrap) - { - return(getNextFileInDir(pathname,"",fname,TRUE)); - } - else - { - fname[0] = 0; - return(FALSE); - } - } - else - { - // Error -// llinfos << "Unable to locate next file" << llendl; - return(FALSE); - } - } - } - - // convert from TCHAR to char - fname = utf16str_to_utf8str(FileData.cFileName); - - // fname now first name in list - return(TRUE); -} - - -// get a random file in the directory -// automatically wrap if we've hit the end -void LLDir_Win32::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) -{ - S32 num_files; - S32 which_file; - HANDLE random_search_h; - - fname = ""; - - llutf16string pathname = utf8str_to_utf16str(dirname); - pathname += utf8str_to_utf16str(mask); - - WIN32_FIND_DATA FileData; - fname[0] = NULL; - - num_files = countFilesInDir(dirname,mask); - if (!num_files) - { - return; - } - - which_file = ll_rand(num_files); - -// llinfos << "Random select mp3 #" << which_file << llendl; - - // which_file now indicates the (zero-based) index to which file to play - - if ((random_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE) - { - while (which_file--) - { - if (!FindNextFile(random_search_h, &FileData)) - { - return; - } - } - FindClose(random_search_h); - - fname = utf16str_to_utf8str(llutf16string(FileData.cFileName)); - } -} - std::string LLDir_Win32::getCurPath() { WCHAR w_str[MAX_PATH]; @@ -375,7 +249,7 @@ std::string LLDir_Win32::getCurPath() } -BOOL LLDir_Win32::fileExists(const std::string &filename) const +bool LLDir_Win32::fileExists(const std::string &filename) const { llstat stat_data; // Check the age of the file diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h index c2acfa6bd4..450efaf9da 100644..100755 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -2,34 +2,32 @@ * @file lldir_win32.h * @brief Definition of directory utilities class for windows * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 @@ -46,16 +44,12 @@ public: /*virtual*/ std::string getCurPath(); /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - /*virtual*/ void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; /*virtual*/ std::string getLLPluginLauncher(); /*virtual*/ std::string getLLPluginFilename(std::string base_name); private: - BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap); - void* mDirSearch_h; llutf16string mCurrentDir; }; diff --git a/indra/llvfs/lldirguard.h b/indra/llvfs/lldirguard.h index 85366120d8..37b9e9b83e 100644..100755 --- a/indra/llvfs/lldirguard.h +++ b/indra/llvfs/lldirguard.h @@ -2,31 +2,25 @@ * @file lldirguard.h * @brief Protect working directory from being changed in scope. * - * $LicenseInfo:firstyear=2009&license=viewergpl$ - * - * Copyright (c) 2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -54,7 +48,7 @@ public: // Dir has changed std::string mOrigDirUtf8 = utf16str_to_utf8str(llutf16string(mOrigDir)); std::string mFinalDirUtf8 = utf16str_to_utf8str(llutf16string(mFinalDir)); - llinfos << "Resetting working dir from " << mFinalDirUtf8 << " to " << mOrigDirUtf8 << llendl; + LL_INFOS() << "Resetting working dir from " << mFinalDirUtf8 << " to " << mOrigDirUtf8 << LL_ENDL; SetCurrentDirectory(mOrigDir); } } diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp new file mode 100755 index 0000000000..76296ff877 --- /dev/null +++ b/indra/llvfs/lldiriterator.cpp @@ -0,0 +1,239 @@ +/** + * @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) +{ + fs::path dir_path(dirname); + + 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/llvfs/lldiriterator.h b/indra/llvfs/lldiriterator.h new file mode 100755 index 0000000000..0b48be41b3 --- /dev/null +++ b/indra/llvfs/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/llvfs/lllfsthread.cpp b/indra/llvfs/lllfsthread.cpp index e85cc437f4..2fd2614cce 100644..100755 --- a/indra/llvfs/lllfsthread.cpp +++ b/indra/llvfs/lllfsthread.cpp @@ -2,31 +2,25 @@ * @file lllfsthread.cpp * @brief LLLFSThread base class * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -51,7 +45,7 @@ void LLLFSThread::initClass(bool local_is_threaded) //static S32 LLLFSThread::updateClass(U32 ms_elapsed) { - sLocal->update(ms_elapsed); + sLocal->update((F32)ms_elapsed); return sLocal->getPending(); } @@ -103,7 +97,7 @@ LLLFSThread::handle_t LLLFSThread::read(const std::string& filename, /* Flawfind bool res = addRequest(req); if (!res) { - llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL; } return handle; @@ -125,7 +119,7 @@ LLLFSThread::handle_t LLLFSThread::write(const std::string& filename, bool res = addRequest(req); if (!res) { - llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << LL_ENDL; } return handle; @@ -150,7 +144,7 @@ LLLFSThread::Request::Request(LLLFSThread* thread, { if (numbytes <= 0) { - llwarns << "LLLFSThread: Request with numbytes = " << numbytes << llendl; + LL_WARNS() << "LLLFSThread: Request with numbytes = " << numbytes << LL_ENDL; } } @@ -172,7 +166,7 @@ void LLLFSThread::Request::deleteRequest() { if (getStatus() == STATUS_QUEUED) { - llerrs << "Attempt to delete a queued LLLFSThread::Request!" << llendl; + LL_ERRS() << "Attempt to delete a queued LLLFSThread::Request!" << LL_ENDL; } if (mResponder.notNull()) { @@ -188,11 +182,11 @@ bool LLLFSThread::Request::processRequest() if (mOperation == FILE_READ) { llassert(mOffset >= 0); - LLAPRFile infile ; + LLAPRFile infile ; // auto-closes infile.open(mFileName, LL_APR_RB, mThread->getLocalAPRFilePool()); if (!infile.getFileHandle()) { - llwarns << "LLLFS: Unable to read file: " << mFileName << llendl; + LL_WARNS() << "LLLFS: Unable to read file: " << mFileName << LL_ENDL; mBytesRead = 0; // fail return true; } @@ -204,19 +198,18 @@ bool LLLFSThread::Request::processRequest() llassert_always(off >= 0); mBytesRead = infile.read(mBuffer, mBytes ); complete = true; - infile.close() ; -// llinfos << "LLLFSThread::READ:" << mFileName << " Bytes: " << mBytesRead << llendl; +// 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 ; + LLAPRFile outfile ; // auto-closes outfile.open(mFileName, flags, mThread->getLocalAPRFilePool()); if (!outfile.getFileHandle()) { - llwarns << "LLLFS: Unable to write file: " << mFileName << llendl; + LL_WARNS() << "LLLFS: Unable to write file: " << mFileName << LL_ENDL; mBytesRead = 0; // fail return true; } @@ -225,19 +218,18 @@ bool LLLFSThread::Request::processRequest() S32 seek = outfile.seek(APR_SET, mOffset); if (seek < 0) { - llwarns << "LLLFS: Unable to write file (seek failed): " << mFileName << llendl; + LL_WARNS() << "LLLFS: Unable to write file (seek failed): " << mFileName << LL_ENDL; mBytesRead = 0; // fail return true; } } mBytesRead = outfile.write(mBuffer, mBytes ); complete = true; - -// llinfos << "LLLFSThread::WRITE:" << mFileName << " Bytes: " << mBytesRead << "/" << mBytes << " Offset:" << mOffset << llendl; +// LL_INFOS() << "LLLFSThread::WRITE:" << mFileName << " Bytes: " << mBytesRead << "/" << mBytes << " Offset:" << mOffset << LL_ENDL; } else { - llerrs << "LLLFSThread::unknown operation: " << (S32)mOperation << llendl; + LL_ERRS() << "LLLFSThread::unknown operation: " << (S32)mOperation << LL_ENDL; } return complete; } diff --git a/indra/llvfs/lllfsthread.h b/indra/llvfs/lllfsthread.h index 3139693302..cdb5c75946 100644..100755 --- a/indra/llvfs/lllfsthread.h +++ b/indra/llvfs/lllfsthread.h @@ -2,31 +2,25 @@ * @file lllfsthread.h * @brief LLLFSThread base class * - * $LicenseInfo:firstyear=2000&license=viewergpl$ - * - * Copyright (c) 2000-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llvfs/llpidlock.cpp b/indra/llvfs/llpidlock.cpp index 28cee29405..6572edead3 100755..100644 --- a/indra/llvfs/llpidlock.cpp +++ b/indra/llvfs/llpidlock.cpp @@ -3,31 +3,25 @@ * @date January 2007 * @brief string formatting utility * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -44,12 +38,6 @@ #include <windows.h> -namespace { - inline DWORD getpid() { - return GetCurrentProcessId(); - } -} - bool isProcessAlive(U32 pid) { return (bool) GetProcessVersion((DWORD)pid); @@ -107,11 +95,11 @@ LLPidLockFile& LLPidLockFile::instance() void LLPidLockFile::writeLockFile(LLSD pids) { - llofstream ofile(mLockName); + llofstream ofile(mLockName.c_str()); if (!LLSDSerialize::toXML(pids,ofile)) { - llwarns << "Unable to write concurrent save lock file." << llendl; + LL_WARNS() << "Unable to write concurrent save lock file." << LL_ENDL; } ofile.close(); } @@ -131,7 +119,7 @@ bool LLPidLockFile::requestLock(LLNameTable<void *> *name_table, bool autosave, LLSD out_pids; out_pids.append( (LLSD::Integer)mPID ); - llifstream ifile(mLockName); + llifstream ifile(mLockName.c_str()); if (ifile.is_open()) { //If file exists, we need to decide whether or not to continue. @@ -187,7 +175,7 @@ bool LLPidLockFile::checkLock() void LLPidLockFile::releaseLock() { - llifstream ifile(mLockName); + llifstream ifile(mLockName.c_str()); LLSD in_pids; LLSD out_pids; bool write_file=FALSE; @@ -280,3 +268,8 @@ void LLPidLock::setSaveName(std::string savename) { LLPidLockFile::instance().mSaveName=savename; } + +S32 LLPidLock::getPID() +{ + return (S32)getpid(); +} diff --git a/indra/llvfs/llpidlock.h b/indra/llvfs/llpidlock.h index 496e99cf5a..334f26bb29 100755..100644 --- a/indra/llvfs/llpidlock.h +++ b/indra/llvfs/llpidlock.h @@ -2,31 +2,25 @@ * @file llpidlock.h * @brief System information debugging classes. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -45,21 +39,22 @@ namespace LLPidLock { void initClass(); // { (void) LLPidLockFile::instance(); } - bool requestLock( LLNameTable<void *> *name_table=NULL, bool autosave=TRUE, - bool force_immediate=FALSE, F32 timeout=300.0); - bool checkLock(); - void releaseLock(); - bool isClean(); - - //getters - LLNameTable<void *> * getNameTable(); - bool getAutosave(); - bool getClean(); - std::string getSaveName(); - - //setters - void setClean(bool clean); - void setSaveName(std::string savename); + bool requestLock( LLNameTable<void *> *name_table=NULL, bool autosave=TRUE, + bool force_immediate=FALSE, F32 timeout=300.0); + bool checkLock(); + void releaseLock(); + bool isClean(); + + //getters + LLNameTable<void *> * getNameTable(); + bool getAutosave(); + bool getClean(); + std::string getSaveName(); + S32 getPID(); + + //setters + void setClean(bool clean); + void setSaveName(std::string savename); }; #endif // LL_PIDLOCK_H diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp index 5fdf41188d..b8588e99f4 100644..100755 --- a/indra/llvfs/llvfile.cpp +++ b/indra/llvfs/llvfile.cpp @@ -2,31 +2,25 @@ * @file llvfile.cpp * @brief Implementation of virtual file * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -36,7 +30,9 @@ #include "llerror.h" #include "llthread.h" -#include "llstat.h" +#include "lltimer.h" +#include "llfasttimer.h" +#include "llmemory.h" #include "llvfs.h" const S32 LLVFile::READ = 0x00000001; @@ -44,7 +40,7 @@ const S32 LLVFile::WRITE = 0x00000002; const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE -static LLFastTimer::DeclareTimer FTM_VFILE_WAIT("VFile Wait"); +static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); //---------------------------------------------------------------------------- LLVFSThread* LLVFile::sVFSThread = NULL; @@ -77,7 +73,7 @@ LLVFile::~LLVFile() { if (!(mMode & LLVFile::WRITE)) { - //llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl; + //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL; sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT); } else // WRITE @@ -93,13 +89,13 @@ BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { if (! (mMode & READ)) { - llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl; + LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; return FALSE; } if (mHandle != LLVFSThread::nullHandle()) { - llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl; + LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL; return FALSE; } mPriority = priority; @@ -109,7 +105,7 @@ BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) // We can't do a read while there are pending async writes waitForLock(VFSLOCK_APPEND); - // *FIX: (???) + // *FIX: (?) if (async) { mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri()); @@ -140,13 +136,13 @@ U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S data = NULL; } else - { - data = new U8[file_size]; + { + data = (U8*) ll_aligned_malloc<16>(file_size); file.read(data, file_size); /* Flawfinder: ignore */ if (file.getLastBytesRead() != (S32)file_size) { - delete[] data; + ll_aligned_free<16>(data); data = NULL; file_size = 0; } @@ -203,11 +199,11 @@ BOOL LLVFile::write(const U8 *buffer, S32 bytes) { if (! (mMode & WRITE)) { - llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl; + LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; } if (mHandle != LLVFSThread::nullHandle()) { - llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl; + LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL; return FALSE; } BOOL success = TRUE; @@ -237,7 +233,7 @@ BOOL LLVFile::write(const U8 *buffer, S32 bytes) if (wrote < bytes) { - llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl; + LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL; success = FALSE; } @@ -257,7 +253,7 @@ BOOL LLVFile::seek(S32 offset, S32 origin) { if (mMode == APPEND) { - llwarns << "Attempt to seek on append-only file" << llendl; + LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL; return FALSE; } @@ -272,14 +268,14 @@ BOOL LLVFile::seek(S32 offset, S32 origin) if (new_pos > size) { - llwarns << "Attempt to seek past end of file" << llendl; + LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; mPosition = size; return FALSE; } else if (new_pos < 0) { - llwarns << "Attempt to seek past beginning of file" << llendl; + LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; mPosition = 0; return FALSE; @@ -313,20 +309,20 @@ BOOL LLVFile::setMaxSize(S32 size) { if (! (mMode & WRITE)) { - llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl; + LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; return FALSE; } if (!mVFS->checkAvailable(size)) { - LLFastTimer t(FTM_VFILE_WAIT); + //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); S32 count = 0; while (sVFSThread->getPending() > 1000) { if (count % 100 == 0) { - llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl; + LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL; } if (sVFSThread->isPaused()) { @@ -342,14 +338,14 @@ BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) { if (! (mMode & WRITE)) { - llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl; + LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; return FALSE; } if (mHandle != LLVFSThread::nullHandle()) { - llwarns << "Renaming file with pending async read" << llendl; + LL_WARNS() << "Renaming file with pending async read" << LL_ENDL; } waitForLock(VFSLOCK_READ); @@ -369,18 +365,18 @@ BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) BOOL LLVFile::remove() { -// llinfos << "Removing file " << mFileID << llendl; +// LL_INFOS() << "Removing file " << mFileID << LL_ENDL; if (! (mMode & WRITE)) { // Leaving paranoia warning just because this should be a very infrequent // operation. - llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl; + LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; } if (mHandle != LLVFSThread::nullHandle()) { - llwarns << "Removing file with pending async read" << llendl; + LL_WARNS() << "Removing file with pending async read" << LL_ENDL; } // why not seek back to the beginning of the file too? @@ -428,7 +424,7 @@ bool LLVFile::isLocked(EVFSLock lock) void LLVFile::waitForLock(EVFSLock lock) { - LLFastTimer t(FTM_VFILE_WAIT); + //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); // spin until the lock clears while (isLocked(lock)) { diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h index c3bca8c737..7e9d9f73e5 100644..100755 --- a/indra/llvfs/llvfile.h +++ b/indra/llvfs/llvfile.h @@ -2,31 +2,25 @@ * @file llvfile.h * @brief Definition of virtual file * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp index 9ce1e75d06..1cc0e819db 100644..100755 --- a/indra/llvfs/llvfs.cpp +++ b/indra/llvfs/llvfs.cpp @@ -2,36 +2,32 @@ * @file llvfs.cpp * @brief Implementation of virtual file system * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 "llvfs.h" + #include <sys/stat.h> #include <set> #include <map> @@ -45,8 +41,6 @@ #include <sys/file.h> #endif -#include "llvfs.h" - #include "llstl.h" #include "lltimer.h" @@ -584,6 +578,7 @@ LLVFS::~LLVFS() mFreeBlocksByLength.clear(); for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer()); + mFreeBlocksByLocation.clear(); unlockAndClose(mDataFP); mDataFP = NULL; @@ -643,7 +638,7 @@ void LLVFS::presizeDataFile(const U32 size) { if (!mDataFP) { - llerrs << "LLVFS::presizeDataFile() with no data file open" << llendl; + LL_ERRS() << "LLVFS::presizeDataFile() with no data file open" << LL_ENDL; return; } @@ -658,11 +653,11 @@ void LLVFS::presizeDataFile(const U32 size) if (tmp) { - llinfos << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << llendl; + LL_INFOS() << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << LL_ENDL; } else { - llwarns << "Failed to pre-size VFS data file" << llendl; + LL_WARNS() << "Failed to pre-size VFS data file" << LL_ENDL; } } @@ -672,7 +667,7 @@ BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } lockData(); @@ -698,7 +693,7 @@ S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type) if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } @@ -725,7 +720,7 @@ S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } lockData(); @@ -761,15 +756,15 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } if (mReadOnly) { - llerrs << "Attempt to write to read-only VFS" << llendl; + LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; } if (max_size <= 0) { - llwarns << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << llendl; + LL_WARNS() << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << LL_ENDL; return FALSE; } @@ -816,7 +811,7 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type if (block->mLength < block->mSize) { // JC: Was a warning, but Ian says it's bad. - llerrs << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << llendl; + LL_ERRS() << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << LL_ENDL; block->mSize = block->mLength; } @@ -884,10 +879,10 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type fseek(mDataFP, new_data_location, SEEK_SET); if (fwrite(&buffer[0], block->mSize, 1, mDataFP) != 1) { - llwarns << "Short write" << llendl; + LL_WARNS() << "Short write" << LL_ENDL; } } else { - llwarns << "Short read" << llendl; + LL_WARNS() << "Short read" << LL_ENDL; } } } @@ -904,7 +899,7 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type } else { - llwarns << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << llendl; + LL_WARNS() << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << LL_ENDL; //dumpMap(); unlockData(); dumpStatistics(); @@ -940,7 +935,7 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type } else { - llwarns << "VFS: No space (" << max_size << ") for new virtual file " << file_id << llendl; + LL_WARNS() << "VFS: No space (" << max_size << ") for new virtual file " << file_id << LL_ENDL; //dumpMap(); unlockData(); dumpStatistics(); @@ -959,11 +954,11 @@ void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } if (mReadOnly) { - llerrs << "Attempt to write to read-only VFS" << llendl; + LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; } lockData(); @@ -995,7 +990,7 @@ void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type { if(dest_block->mLocks[i]) { - llerrs << "Renaming VFS block to a locked file." << llendl; + LL_ERRS() << "Renaming VFS block to a locked file." << LL_ENDL; } dest_block->mLocks[i] = src_block->mLocks[i]; } @@ -1015,7 +1010,7 @@ void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type } else { - llwarns << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << llendl; + LL_WARNS() << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << LL_ENDL; } unlockData(); } @@ -1047,11 +1042,11 @@ void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } if (mReadOnly) { - llerrs << "Attempt to write to read-only VFS" << llendl; + LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; } lockData(); @@ -1065,7 +1060,7 @@ void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type } else { - llwarns << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << llendl; + LL_WARNS() << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << LL_ENDL; } unlockData(); @@ -1078,7 +1073,7 @@ S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } llassert(location >= 0); llassert(length >= 0); @@ -1097,7 +1092,7 @@ S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 if (location > block->mSize) { - llwarns << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << llendl; + LL_WARNS() << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << LL_ENDL; } else { @@ -1125,11 +1120,11 @@ S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } if (mReadOnly) { - llerrs << "Attempt to write to read-only VFS" << llendl; + LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; } llassert(length > 0); @@ -1154,22 +1149,22 @@ S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, if (block->mLength == BLOCK_LENGTH_INVALID) { // Block was removed, ignore write - llwarns << "VFS: Attempt to write to invalid block" + LL_WARNS() << "VFS: Attempt to write to invalid block" << " in file " << file_id << " location: " << in_loc << " bytes: " << length - << llendl; + << LL_ENDL; unlockData(); return length; } else if (location > block->mLength) { - llwarns << "VFS: Attempt to write to location " << location + LL_WARNS() << "VFS: Attempt to write to location " << location << " in file " << file_id << " type " << S32(file_type) << " of size " << block->mSize << " block length " << block->mLength - << llendl; + << LL_ENDL; unlockData(); return length; } @@ -1177,7 +1172,7 @@ S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, { if (length > block->mLength - location ) { - llwarns << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << llendl; + LL_WARNS() << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << LL_ENDL; length = block->mLength - location; } U32 file_location = location + block->mLocation; @@ -1186,7 +1181,7 @@ S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP); if (write_len != length) { - llwarns << llformat("VFS Write Error: %d != %d",write_len,length) << llendl; + LL_WARNS() << llformat("VFS Write Error: %d != %d",write_len,length) << LL_ENDL; } // fflush(mDataFP); @@ -1249,7 +1244,7 @@ void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, E } else { - llwarns << "VFS: Decrementing zero-value lock " << lock << llendl; + LL_WARNS() << "VFS: Decrementing zero-value lock " << lock << LL_ENDL; } mLockCounts[lock]--; } @@ -1301,7 +1296,7 @@ void LLVFS::eraseBlockLength(LLVFSBlock *block) } if(!found_block) { - llerrs << "eraseBlock could not find block" << llendl; + LL_ERRS() << "eraseBlock could not find block" << LL_ENDL; } } @@ -1324,7 +1319,7 @@ void LLVFS::addFreeBlock(LLVFSBlock *block) size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation); if(dbgcount > 0) { - llerrs << "addFreeBlock called with block already in list" << llendl; + LL_ERRS() << "addFreeBlock called with block already in list" << LL_ENDL; } #endif @@ -1353,7 +1348,7 @@ void LLVFS::addFreeBlock(LLVFSBlock *block) if (merge_prev && merge_next) { - // llinfos << "VFS merge BOTH" << llendl; + // LL_INFOS() << "VFS merge BOTH" << LL_ENDL; // Previous block is changing length (a lot), so only need to update length map. // Next block is going away completely. JC eraseBlockLength(prev_block); @@ -1367,7 +1362,7 @@ void LLVFS::addFreeBlock(LLVFSBlock *block) } else if (merge_prev) { - // llinfos << "VFS merge previous" << llendl; + // LL_INFOS() << "VFS merge previous" << LL_ENDL; // Previous block is maintaining location, only changing length, // therefore only need to update the length map. JC eraseBlockLength(prev_block); @@ -1378,7 +1373,7 @@ void LLVFS::addFreeBlock(LLVFSBlock *block) } else if (merge_next) { - // llinfos << "VFS merge next" << llendl; + // LL_INFOS() << "VFS merge next" << LL_ENDL; // Next block is changing both location and length, // so both free lists must update. JC eraseBlock(next_block); @@ -1405,7 +1400,7 @@ void LLVFS::addFreeBlock(LLVFSBlock *block) //{ // if (!isValid()) // { -// llerrs << "Attempting to use invalid VFS!" << llendl; +// LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; // } // // TODO: could we optimize this with hints from the calling code? // blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(); @@ -1464,11 +1459,11 @@ void LLVFS::sync(LLVFSFileBlock *block, BOOL remove) { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } if (mReadOnly) { - llwarns << "Attempt to sync read-only VFS" << llendl; + LL_WARNS() << "Attempt to sync read-only VFS" << LL_ENDL; return; } if (block->mLength == BLOCK_LENGTH_INVALID) @@ -1478,7 +1473,7 @@ void LLVFS::sync(LLVFSFileBlock *block, BOOL remove) } if (block->mLength == 0) { - llerrs << "VFS syncing zero-length block" << llendl; + LL_ERRS() << "VFS syncing zero-length block" << LL_ENDL; } BOOL set_index_to_end = FALSE; @@ -1530,7 +1525,7 @@ void LLVFS::sync(LLVFSFileBlock *block, BOOL remove) if (fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) { - llwarns << "Short write" << llendl; + LL_WARNS() << "Short write" << LL_ENDL; } // *NOTE: Why was this commented out? @@ -1546,7 +1541,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } LLVFSBlock *block = NULL; @@ -1591,7 +1586,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) if (lru_list.size() == 0) { // No more files to delete, and still not enough room! - llwarns << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << llendl; + LL_WARNS() << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << LL_ENDL; break; } @@ -1602,7 +1597,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) { // ditch this file and look again for a free block - should find it // TODO: it'll be faster just to assign the free block and break - llinfos << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << llendl; + LL_INFOS() << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << LL_ENDL; lru_list.erase(it); removeFileBlock(file_block); file_block = NULL; @@ -1610,7 +1605,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) } - llinfos << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << llendl; + LL_INFOS() << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << LL_ENDL; dumpLockCounts(); // Now it's time to aggressively make more space @@ -1625,7 +1620,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) file_block = *it; // TODO: it would be great to be able to batch all these sync() calls - // llinfos << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << llendl; + // LL_INFOS() << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << LL_ENDL; cleaned_up += file_block->mLength; lru_list.erase(it++); @@ -1639,7 +1634,7 @@ LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) F32 time = timer.getElapsedTimeF32(); if (time > 0.5f) { - llwarns << "VFS: Spent " << time << " seconds in findFreeBlock!" << llendl; + LL_WARNS() << "VFS: Spent " << time << " seconds in findFreeBlock!" << LL_ENDL; } return block; @@ -1653,7 +1648,7 @@ void LLVFS::pokeFiles() { if (!isValid()) { - llerrs << "Attempting to use invalid VFS!" << llendl; + LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; } U32 word; @@ -1665,7 +1660,7 @@ void LLVFS::pokeFiles() fseek(mDataFP, 0, SEEK_SET); if (fwrite(&word, sizeof(word), 1, mDataFP) != 1) { - llwarns << "Could not write to data file" << llendl; + LL_WARNS() << "Could not write to data file" << LL_ENDL; } fflush(mDataFP); } @@ -1676,7 +1671,7 @@ void LLVFS::pokeFiles() fseek(mIndexFP, 0, SEEK_SET); if (fwrite(&word, sizeof(word), 1, mIndexFP) != 1) { - llwarns << "Could not write to index file" << llendl; + LL_WARNS() << "Could not write to index file" << LL_ENDL; } fflush(mIndexFP); } @@ -1685,20 +1680,20 @@ void LLVFS::pokeFiles() void LLVFS::dumpMap() { - llinfos << "Files:" << llendl; + LL_INFOS() << "Files:" << LL_ENDL; for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) { LLVFSFileBlock *file_block = (*it).second; - llinfos << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl; + LL_INFOS() << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; } - llinfos << "Free Blocks:" << llendl; + LL_INFOS() << "Free Blocks:" << LL_ENDL; for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(), end = mFreeBlocksByLocation.end(); iter != end; iter++) { LLVFSBlock *free_block = iter->second; - llinfos << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl; + LL_INFOS() << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; } } @@ -1717,11 +1712,12 @@ void LLVFS::audit() BOOL vfs_corrupt = FALSE; - std::vector<U8> buffer(index_size); + // since we take the address of element 0, we need to have at least one element. + std::vector<U8> buffer(llmax<size_t>(index_size,1U)); if (fread(&buffer[0], 1, index_size, mIndexFP) != index_size) { - llwarns << "Index truncated" << llendl; + LL_WARNS() << "Index truncated" << LL_ENDL; vfs_corrupt = TRUE; } @@ -1750,7 +1746,7 @@ void LLVFS::audit() { if (mFileBlocks.find(*block) == mFileBlocks.end()) { - llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << llendl; + LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << LL_ENDL; } else if (found_files.find(*block) != found_files.end()) { @@ -1762,22 +1758,22 @@ void LLVFS::audit() mIndexFP = NULL; unlockAndClose(mDataFP); mDataFP = NULL; - llwarns << "VFS: Original block index " << block->mIndexLocation + LL_WARNS() << "VFS: Original block index " << block->mIndexLocation << " location " << block->mLocation << " length " << block->mLength << " size " << block->mSize << " id " << block->mFileID << " type " << block->mFileType - << llendl; - llwarns << "VFS: Duplicate block index " << dupe->mIndexLocation + << LL_ENDL; + LL_WARNS() << "VFS: Duplicate block index " << dupe->mIndexLocation << " location " << dupe->mLocation << " length " << dupe->mLength << " size " << dupe->mSize << " id " << dupe->mFileID << " type " << dupe->mFileType - << llendl; - llwarns << "VFS: Index size " << index_size << llendl; - llwarns << "VFS: INDEX CORRUPT" << llendl; + << LL_ENDL; + LL_WARNS() << "VFS: Index size " << index_size << LL_ENDL; + LL_WARNS() << "VFS: INDEX CORRUPT" << LL_ENDL; vfs_corrupt = TRUE; break; } @@ -1790,7 +1786,7 @@ void LLVFS::audit() { if (block->mLength) { - llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << llendl; + LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << LL_ENDL; } // else this is just a hole } @@ -1806,19 +1802,19 @@ void LLVFS::audit() { if (! found_files.count(*block)) { - llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< llendl; + LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< LL_ENDL; fseek(mIndexFP, block->mIndexLocation, SEEK_SET); U8 buf[LLVFSFileBlock::SERIAL_SIZE]; if (fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) { - llwarns << "VFile " << block->mFileID - << " gave short read" << llendl; + LL_WARNS() << "VFile " << block->mFileID + << " gave short read" << LL_ENDL; } LLVFSFileBlock disk_block; disk_block.deserialize(buf, block->mIndexLocation); - llwarns << "Instead found " << disk_block.mFileID << ":" << block->mFileType << llendl; + LL_WARNS() << "Instead found " << disk_block.mFileID << ":" << block->mFileType << LL_ENDL; } else { @@ -1832,14 +1828,15 @@ void LLVFS::audit() iter != found_files.end(); iter++) { LLVFSFileBlock* block = iter->second; - llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << llendl; + LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << LL_ENDL; } - llinfos << "VFS: audit OK" << llendl; + LL_INFOS() << "VFS: audit OK" << LL_ENDL; // mutex released by LLMutexLock() destructor. } for_each(audit_blocks.begin(), audit_blocks.end(), DeletePointer()); + audit_blocks.clear(); } @@ -1862,12 +1859,12 @@ void LLVFS::checkMem() S32 index_loc = *iter; if (index_loc == block->mIndexLocation) { - llwarns << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << llendl; + LL_WARNS() << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << LL_ENDL; } } } - llinfos << "VFS: mem check OK" << llendl; + LL_INFOS() << "VFS: mem check OK" << LL_ENDL; unlockData(); } @@ -1877,7 +1874,7 @@ void LLVFS::dumpLockCounts() S32 i; for (i = 0; i < VFSLOCK_COUNT; i++) { - llinfos << "LockType: " << i << ": " << mLockCounts[i] << llendl; + LL_INFOS() << "LockType: " << i << ": " << mLockCounts[i] << LL_ENDL; } } @@ -1902,7 +1899,7 @@ void LLVFS::dumpStatistics() } else if (file_block->mLength <= 0) { - llinfos << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl; + LL_INFOS() << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; size_counts[file_block->mLength]++; location_counts[file_block->mLocation]++; } @@ -1924,13 +1921,13 @@ void LLVFS::dumpStatistics() { S32 size = it->first; S32 size_count = it->second; - llinfos << "Bad files size " << size << " count " << size_count << llendl; + LL_INFOS() << "Bad files size " << size << " count " << size_count << LL_ENDL; } for (std::map<U32,S32>::iterator it = location_counts.begin(); it != location_counts.end(); ++it) { U32 location = it->first; S32 location_count = it->second; - llinfos << "Bad files location " << location << " count " << location_count << llendl; + LL_INFOS() << "Bad files location " << location << " count " << location_count << LL_ENDL; } // Investigate free list. @@ -1944,14 +1941,14 @@ void LLVFS::dumpStatistics() LLVFSBlock *free_block = iter->second; if (free_block->mLength <= 0) { - llinfos << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl; + LL_INFOS() << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; } else { - llinfos << "Block: " << free_block->mLocation + LL_INFOS() << "Block: " << free_block->mLocation << "\tLength: " << free_block->mLength << "\tEnd: " << free_block->mLocation + free_block->mLength - << llendl; + << LL_ENDL; total_free_size += free_block->mLength; } @@ -1966,38 +1963,38 @@ void LLVFS::dumpStatistics() // Dump histogram of free block sizes for (std::map<S32,S32>::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it) { - llinfos << "Free length " << it->first << " count " << it->second << llendl; + LL_INFOS() << "Free length " << it->first << " count " << it->second << LL_ENDL; } - llinfos << "Invalid blocks: " << invalid_file_count << llendl; - llinfos << "File blocks: " << mFileBlocks.size() << llendl; + LL_INFOS() << "Invalid blocks: " << invalid_file_count << LL_ENDL; + LL_INFOS() << "File blocks: " << mFileBlocks.size() << LL_ENDL; S32 length_list_count = (S32)mFreeBlocksByLength.size(); S32 location_list_count = (S32)mFreeBlocksByLocation.size(); if (length_list_count == location_list_count) { - llinfos << "Free list lengths match, free blocks: " << location_list_count << llendl; + LL_INFOS() << "Free list lengths match, free blocks: " << location_list_count << LL_ENDL; } else { - llwarns << "Free list lengths do not match!" << llendl; - llwarns << "By length: " << length_list_count << llendl; - llwarns << "By location: " << location_list_count << llendl; + LL_WARNS() << "Free list lengths do not match!" << LL_ENDL; + LL_WARNS() << "By length: " << length_list_count << LL_ENDL; + LL_WARNS() << "By location: " << location_list_count << LL_ENDL; } - llinfos << "Max file: " << max_file_size/1024 << "K" << llendl; - llinfos << "Max free: " << max_free_size/1024 << "K" << llendl; - llinfos << "Total file size: " << total_file_size/1024 << "K" << llendl; - llinfos << "Total free size: " << total_free_size/1024 << "K" << llendl; - llinfos << "Sum: " << (total_file_size + total_free_size) << " bytes" << llendl; - llinfos << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << llendl; + LL_INFOS() << "Max file: " << max_file_size/1024 << "K" << LL_ENDL; + LL_INFOS() << "Max free: " << max_free_size/1024 << "K" << LL_ENDL; + LL_INFOS() << "Total file size: " << total_file_size/1024 << "K" << LL_ENDL; + LL_INFOS() << "Total free size: " << total_free_size/1024 << "K" << LL_ENDL; + LL_INFOS() << "Sum: " << (total_file_size + total_free_size) << " bytes" << LL_ENDL; + LL_INFOS() << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << LL_ENDL; - llinfos << " " << llendl; + LL_INFOS() << " " << LL_ENDL; for (std::map<LLAssetType::EType, std::pair<S32,S32> >::iterator iter = filetype_counts.begin(); iter != filetype_counts.end(); ++iter) { - llinfos << "Type: " << LLAssetType::getDesc(iter->first) + LL_INFOS() << "Type: " << LLAssetType::getDesc(iter->first) << " Count: " << iter->second.first - << " Bytes: " << (iter->second.second>>20) << " MB" << llendl; + << " Bytes: " << (iter->second.second>>20) << " MB" << LL_ENDL; } // Look for potential merges @@ -2012,7 +2009,7 @@ void LLVFS::dumpStatistics() LLVFSBlock *second_block = iter->second; if (first_block->mLocation + first_block->mLength == second_block->mLocation) { - llinfos << "Potential merge at " << first_block->mLocation << llendl; + LL_INFOS() << "Potential merge at " << first_block->mLocation << LL_ENDL; } first_block = second_block; } @@ -2041,6 +2038,9 @@ std::string get_extension(LLAssetType::EType type) case LLAssetType::AT_ANIMATION: extension = ".lla"; break; + case LLAssetType::AT_MESH: + extension = ".slm"; + break; default: // Just use the asset server filename extension in most cases extension += "."; @@ -2064,10 +2064,10 @@ void LLVFS::listFiles() { LLUUID id = file_spec.mFileID; std::string extension = get_extension(file_spec.mFileType); - llinfos << " File: " << id + LL_INFOS() << " File: " << id << " Type: " << LLAssetType::getDesc(file_spec.mFileType) << " Size: " << size - << llendl; + << LL_ENDL; } } @@ -2098,7 +2098,7 @@ void LLVFS::dumpFiles() std::string extension = get_extension(type); std::string filename = id.asString() + extension; - llinfos << " Writing " << filename << llendl; + LL_INFOS() << " Writing " << filename << LL_ENDL; LLAPRFile outfile; outfile.open(filename, LL_APR_WB); @@ -2111,7 +2111,7 @@ void LLVFS::dumpFiles() unlockData(); - llinfos << "Extracted " << files_extracted << " files out of " << mFileBlocks.size() << llendl; + LL_INFOS() << "Extracted " << files_extracted << " files out of " << mFileBlocks.size() << LL_ENDL; } //============================================================================ diff --git a/indra/llvfs/llvfs.h b/indra/llvfs/llvfs.h index 2acda7ae29..39f31a221b 100644..100755 --- a/indra/llvfs/llvfs.h +++ b/indra/llvfs/llvfs.h @@ -2,31 +2,25 @@ * @file llvfs.h * @brief Definition of virtual file system * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,7 +29,6 @@ #include <deque> #include "lluuid.h" -#include "linked_lists.h" #include "llassettype.h" #include "llthread.h" diff --git a/indra/llvfs/llvfs_objc.h b/indra/llvfs/llvfs_objc.h new file mode 100755 index 0000000000..56cdbebfc5 --- /dev/null +++ b/indra/llvfs/llvfs_objc.h @@ -0,0 +1,43 @@ +/** + * @file llvfs_objc.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_LLVFS_OBJC_H +#define LL_LLVFS_OBJC_H + +#include <iostream> + +std::string* getSystemTempFolder(); +std::string* getSystemCacheFolder(); +std::string* getSystemApplicationSupportFolder(); +std::string* getSystemResourceFolder(); +std::string* getSystemExecutableFolder(); + + +#endif // LL_LLVFS_OBJC_H diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm new file mode 100755 index 0000000000..282ea41339 --- /dev/null +++ b/indra/llvfs/llvfs_objc.mm @@ -0,0 +1,108 @@ +/** + * @file llvfs_objc.cpp + * @brief Cocoa 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 + +//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL + +#include "llvfs_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/llvfs/llvfsthread.cpp b/indra/llvfs/llvfsthread.cpp index 69da4b75e5..8cd85929e2 100644..100755 --- a/indra/llvfs/llvfsthread.cpp +++ b/indra/llvfs/llvfsthread.cpp @@ -2,31 +2,25 @@ * @file llvfsthread.cpp * @brief LLVFSThread implementation * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -52,7 +46,7 @@ void LLVFSThread::initClass(bool local_is_threaded) //static S32 LLVFSThread::updateClass(U32 ms_elapsed) { - sLocal->update(ms_elapsed); + sLocal->update((F32)ms_elapsed); return sLocal->getPending(); } @@ -94,7 +88,7 @@ LLVFSThread::handle_t LLVFSThread::read(LLVFS* vfs, const LLUUID &file_id, const bool res = addRequest(req); if (!res) { - llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; req->deleteRequest(); handle = nullHandle(); } @@ -113,7 +107,7 @@ S32 LLVFSThread::readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetT S32 res = addRequest(req) ? 1 : 0; if (res == 0) { - llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; req->deleteRequest(); } else @@ -136,7 +130,7 @@ LLVFSThread::handle_t LLVFSThread::write(LLVFS* vfs, const LLUUID &file_id, cons bool res = addRequest(req); if (!res) { - llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; req->deleteRequest(); handle = nullHandle(); } @@ -155,7 +149,7 @@ S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAsset S32 res = addRequest(req) ? 1 : 0; if (res == 0) { - llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl; + LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; req->deleteRequest(); } else @@ -181,7 +175,7 @@ S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAsset // bool res = addRequest(req); // if (!res) // { -// llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl; +// LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; // req->deleteRequest(); // handle = nullHandle(); // } @@ -209,17 +203,17 @@ LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags, if (numbytes <= 0 && mOperation != FILE_RENAME) { - llwarns << "LLVFSThread: Request with numbytes = " << numbytes + LL_WARNS() << "LLVFSThread: Request with numbytes = " << numbytes << " operation = " << op << " offset " << offset - << " file_type " << file_type << llendl; + << " file_type " << file_type << LL_ENDL; } if (mOperation == FILE_WRITE) { S32 blocksize = mVFS->getMaxSize(mFileID, mFileType); if (blocksize < 0) { - llwarns << "VFS write to temporary block (shouldn't happen)" << llendl; + LL_WARNS() << "VFS write to temporary block (shouldn't happen)" << LL_ENDL; } mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND); } @@ -254,7 +248,7 @@ void LLVFSThread::Request::deleteRequest() { if (getStatus() == STATUS_QUEUED) { - llerrs << "Attempt to delete a queued LLVFSThread::Request!" << llendl; + LL_ERRS() << "Attempt to delete a queued LLVFSThread::Request!" << LL_ENDL; } if (mOperation == FILE_WRITE) { @@ -279,13 +273,13 @@ bool LLVFSThread::Request::processRequest() llassert(mOffset >= 0); mBytesRead = mVFS->getData(mFileID, mFileType, mBuffer, mOffset, mBytes); complete = true; - //llinfos << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl; + //LL_INFOS() << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; } else if (mOperation == FILE_WRITE) { mBytesRead = mVFS->storeData(mFileID, mFileType, mBuffer, mOffset, mBytes); complete = true; - //llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl; + //LL_INFOS() << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; } else if (mOperation == FILE_RENAME) { @@ -294,11 +288,11 @@ bool LLVFSThread::Request::processRequest() mVFS->renameFile(mFileID, mFileType, *new_idp, new_type); mFileID = *new_idp; complete = true; - //llinfos << llformat("LLVFSThread::RENAME '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl; + //LL_INFOS() << llformat("LLVFSThread::RENAME '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; } else { - llerrs << llformat("LLVFSThread::unknown operation: %d", mOperation) << llendl; + LL_ERRS() << llformat("LLVFSThread::unknown operation: %d", mOperation) << LL_ENDL; } return complete; } diff --git a/indra/llvfs/llvfsthread.h b/indra/llvfs/llvfsthread.h index 0a9786e9e1..95f3c857c6 100644..100755 --- a/indra/llvfs/llvfsthread.h +++ b/indra/llvfs/llvfsthread.h @@ -2,31 +2,25 @@ * @file llvfsthread.h * @brief LLVFSThread definition * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index 9befa59477..3cff622a4b 100644..100755 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -3,40 +3,191 @@ * @date 2008-05 * @brief LLDir test cases. * - * $LicenseInfo:firstyear=2008&license=viewergpl$ - * - * Copyright (c) 2008-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * 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. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * 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. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * 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 * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 { @@ -262,5 +413,355 @@ namespace tut 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/llvfs/tests/lldiriterator_test.cpp b/indra/llvfs/tests/lldiriterator_test.cpp new file mode 100755 index 0000000000..a65e3dada5 --- /dev/null +++ b/indra/llvfs/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(); + } + +} |