From 3653727e7f84f10caefb6ea7dc33859455ebfa0b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 10 Oct 2012 14:57:43 -0400 Subject: Introduce new LLDir::findSkinnedFilenames() method. Use as needed. In a number of different places, for different reasons, the viewer wants to load a UI-related file that might be overridden by a non-default skin; and within that skin, might further be overridden by a non-default language. Apparently, for each of those use cases, every individual developer approached it as an entirely new problem, solving it idiosyncratically for that one case. Not only is this a maintenance problem, but it rubs one's nose in the fact that most such solutions consider only a subset of the relevant skin directories. Richard and I evolved an API intended to address all such cases: a central LLDir method returning a list of relevant pathnames, from most general to most localized, filtered to present only existing files; plus a couple of convenience methods to specifically obtain the most general and most localized available file. There were several load-skinned-file methods (LLFloater::buildFromFile(), LLPanel::buildFromFile() and LLUICtrlFactory::createFromFile() -- apparently cloned-and-modified from each other) that contained funky bolted-on logic to output the loaded data to an optional passed LLXMLNodePtr param. The trouble is that passing that param forced each of these methods to subvert its normal search: specifically for that case, it needed to find the baseline XML file instead of the localized one. Richard agreed that for the intended usage (reformatting XML files) we should use XML schema instead, and that the hacky functionality should be removed. Remove it. Also remove LLUICtrlFactory::getLocalizedXMLNode(), only used for those three special cases. Some callers explicitly passed the optional LLXMLNodePtr param as NULL. Remove that. Remove LLFloaterUIPreview::displayFloater(save) param, which relied on the optional output LLXMLNodePtr param. Make onClickSaveFloater() and onClickSaveAll() emit popupAndPrintWarning() about discontinued functionality. Recast LLFloater::buildFromFile(), LLPanel::buildFromFile(), LLUICtrlFactory::createFromFile(), LLNotifications::loadTemplates(), LLUI::locateSkin(), LLFontRegistry::parseFontInfo(), LLUIColorTable::loadFromSettings(), LLUICtrlFactory::loadWidgetTemplate(), LLUICtrlFactory::getLayeredXMLNode(), LLUIImageList::initFromFile(), LLAppViewer::launchUpdater() and LLMediaCtrl::navigateToLocalPage() to use findSkinnedFilenames(). (Is LLAppViewer::launchUpdater() ever called any more? Apparently so -- though the linux-updater.bin logic to process the relevant command-line switch has been disabled. Shrug.) (Is LLMediaCtrl::navigateToLocalPage() ever used?? If so, why?) Remove LLUI::setupPaths(), getXUIPaths(), getSkinPath() and getLocalizedSkinPath(). Remove the skins/paths.xml file read by setupPaths(). The only configuration it contained was the pair of partial paths "xui/en" and "xui/[LANGUAGE]" -- hardly likely to change. getSkinPath() specifically returned the first of these, while getLocalizedSkinPath() specifically returned the second. This knowledge is now embedded in findSkinnedFilenames(). Also remove paths.xml from viewer_manifest.py. Remove injected xui_paths from LLFontGL::initClass() and LLFontRegistry::LLFontRegistry(). These are no longer needed since LLFontRegistry can now directly consult LLDir for its path search. Stop passing LLUI::getXUIPaths() to LLFontGL::initClass() in LLViewerWindow's constructor and initFonts() method. Add LLDir::append() and add() methods for the simple task of combining two path components separated by getDirDelimiter() -- but only if they're both non-empty. Amazing how often that logic is replicated. Replace some existing concatenations with add() or append(). New LLDir::findSkinnedFilenames() method must know current language. Allow injecting current language by adding an LLDir::setSkinFolder(language) param, and pass it where LLAppViewer::init() and initConfiguration() currently call setSkinFolder(). Also add LLDir::getSkinFolder() and getLanguage() methods. Change LLFLoaterUIPreview's LLLocalizationResetForcer helper to "forcibly reset language" using LLDir::setSkinFolder() instead of LLUI::setupPaths(). Update LLDir stubs in lldir_stub.cpp and llupdaterservice_test.cpp. Add LLDir::getUserDefaultSkinDir() to obtain often-overlooked possible skin directory -- like getUserSkinDir() but with "default" in place of the current skin name as the last path component. (However, we hope findSkinnedFilenames() obviates most explicit use of such individual skin directory pathnames.) Add LLDir unit tests for new findSkinnedFilenames() and add() methods -- the latter exercises append() as well. Tweak indra/integration_tests/llui_libtest/llui_libtest.cpp for all the above. Notably, comment out its export_test_floaters() function, since the essential LLFloater::buildFromFile(optional LLXMLNodePtr) functionality has been removed. This may mean that llui_libtest.cpp has little remaining value, not sure. --- indra/llvfs/lldir.cpp | 458 ++++++++++++++++++++++++++++++--------- indra/llvfs/lldir.h | 91 +++++++- indra/llvfs/tests/lldir_test.cpp | 339 ++++++++++++++++++++++++++++- 3 files changed, 771 insertions(+), 117 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 32d081d552..a7d12476a4 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -41,6 +41,12 @@ #include "lluuid.h" #include "lldiriterator.h" +#include "stringize.h" +#include +#include +#include +#include +#include #if LL_WINDOWS #include "lldir_win32.h" @@ -58,6 +64,14 @@ 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 = ""; + LLDir::LLDir() : mAppName(""), mExecutablePathAndName(""), @@ -70,7 +84,8 @@ LLDir::LLDir() mOSCacheDir(""), mCAFile(""), mTempDir(""), - mDirDelimiter("/") // fallback to forward slash if not overridden + mDirDelimiter("/"), // fallback to forward slash if not overridden + mLanguage("en") { } @@ -96,9 +111,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) LLDirIterator iter(dirname, mask); while (iter.next(filename)) { - fullpath = dirname; - fullpath += getDirDelimiter(); - fullpath += filename; + fullpath = add(dirname, filename); if(LLFile::isdir(fullpath)) { @@ -270,12 +283,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; } @@ -298,19 +311,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 @@ -323,6 +341,41 @@ const std::string &LLDir::getLLPluginDir() const return mLLPluginDir; } +static std::string ELLPathToString(ELLPath location) +{ + typedef std::map ELLPathMap; +#define ENT(symbol) ELLPathMap::value_type(symbol, #symbol) + static ELLPathMap::value_type init[] = + { + 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 + static const ELLPathMap sMap(boost::begin(init), boost::end(init)); + + 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); @@ -343,15 +396,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: @@ -363,16 +412,22 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd 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; @@ -386,9 +441,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: @@ -412,9 +465,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: @@ -422,56 +473,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; - } - - 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; + llwarns << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "': prefix is empty, possible bad filename" << llendl; } - 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; } @@ -511,31 +542,168 @@ 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, + bool merge) const { - return findSkinnedFilename("", "", filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, merge)); + 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, + bool merge) const { - return findSkinnedFilename("", subdir, filename); + // This implementation is basically just as described in the declaration comments. + std::vector found(findSkinnedFilenames(subdir, filename, merge)); + if (found.empty()) + { + return ""; + } + return found.back(); } -std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const +std::vector LLDir::findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + bool merge) const { - // generate subdirectory path fragment, e.g. "/foo/bar", "/foo", "" - std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1) - + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2); + // Recognize subdirs that have no localization. + static const char* sUnlocalizedData[] = + { + "", // top-level directory not localized + "textures" // textures not localized + }; + static const std::set sUnlocalized(boost::begin(sUnlocalizedData), + boost::end(sUnlocalizedData)); + + LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename + << "', merge " << std::boolalpha << merge << 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." + typedef std::map LocalizedMap; + static LocalizedMap sLocalized; + + // Check whether we've already discovered if this subdir is localized. + LocalizedMap::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(LocalizedMap::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(LocalizedMap::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(LocalizedMap::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(LocalizedMap::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 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); + } + } + // Code below relies on subsubdirs not being empty: more specifically, on + // front() being valid. There may or may not be additional entries, but we + // have at least one. For an unlocalized subdir, it's the only one; for a + // localized subdir, it's the default one. + llassert(! subsubdirs.empty()); + + // Build results vector. + std::vector results; + BOOST_FOREACH(std::string skindir, mSearchSkinDirs) + { + std::string subdir_path(add(skindir, subdir)); + // Does subdir_path/subsubdirs[0]/filename exist? If there's more than + // one entry in subsubdirs, the first is the default language ("en"), + // the second is the current language. A skin that contains + // subdir/language/filename without also containing subdir/en/filename + // is ill-formed: skip any such skin. So to decide whether to keep + // this skin dir or skip it, we need only check for the existence of + // the first subsubdir entry ("en" or only). + std::string subsubdir_path(add(add(subdir_path, subsubdirs.front()), filename)); + if (! fileExists(subsubdir_path)) + continue; - std::vector 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 + // Here the desired filename exists in the first subsubdir. That means + // this is a skindir we want to record in results. But if the caller + // passed merge=false, we must discard all previous skindirs. + if (! merge) + { + results.clear(); + } + + // Now add every subsubdir in which filename exists. We already know + // it exists in the first one. + results.push_back(subsubdir_path); + + // Append all remaining subsubdirs in which filename exists. + for (std::vector::const_iterator ssdi(subsubdirs.begin() + 1), ssdend(subsubdirs.end()); + ssdi != ssdend; ++ssdi) + { + subsubdir_path = add(add(subdir_path, *ssdi), filename); + if (fileExists(subsubdir_path)) + { + results.push_back(subsubdir_path); + } + } + } - std::string found_file = findFile(filename, search_paths); - return found_file; + 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 @@ -546,12 +714,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 @@ -587,9 +750,7 @@ void LLDir::setLindenUserDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += userlower; + mLindenUserDir = add(getOSUserAppDir(), userlower); } else { @@ -621,9 +782,7 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += userlower; + mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); } else { @@ -632,25 +791,59 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) } -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) @@ -664,7 +857,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) { @@ -697,6 +890,57 @@ void LLDir::dumpCurrentDirectories() 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) { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 5ee8bdb542..a242802979 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -56,7 +56,7 @@ typedef enum ELLPath LL_PATH_LAST } ELLPath; - +/// Directory operations class LLDir { public: @@ -100,9 +100,10 @@ class LLDir 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 @@ -117,10 +118,59 @@ 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() + /** + * 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 merge 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 merge=false. The returned + * vector will contain only + * ".../current_skin/xui/en/filename", + * ".../current_skin/xui/current_language/filename". + * 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 merge=true. The returned vector will contain at + * least ".../default/xui/en/strings.xml", + * ".../default/xui/current_language/strings.xml", + * ".../current_skin/xui/en/strings.xml", + * ".../current_skin/xui/current_language/strings.xml". + */ + std::vector findSkinnedFilenames(const std::string& subdir, + const std::string& filename, + bool merge=false) 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, + bool merge=false) 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, + bool merge=false) const; // random filename in common temporary directory std::string getTempFilename() const; @@ -132,15 +182,30 @@ class LLDir virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir virtual void setPerAccountChatLogsDir(const std::string &username); // Set the per user chat log directory. virtual void setLindenUserDir(const std::string &username); // Set the linden user dir to this user's dir - virtual void setSkinFolder(const std::string &skin_folder); + 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 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 SepOff; + SepOff needSep(const std::string& path, const std::string& name) const; + // build mSearchSkinDirs without adding duplicates + void addSearchSkinDir(const std::string& skindir); + std::string mAppName; // install directory under progams/ ie "SecondLife" std::string mExecutablePathAndName; // full path + Filename of .exe std::string mExecutableFilename; // Filename of .exe @@ -158,10 +223,18 @@ 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 mSearchSkinDirs; + std::string mLanguage; // Current viewer language std::string mLLPluginDir; // Location for plugins and plugin shell }; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index ea321c5ae9..a00fc8684c 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -27,11 +27,161 @@ #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 +#include + +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[] = + { + "install/skins/default/colors.xml", + "install/skins/default/xui/en/strings.xml", + "install/skins/default/xui/fr/strings.xml", + "install/skins/default/xui/en/floater.xml", + "install/skins/default/xui/fr/floater.xml", + "install/skins/default/xui/en/newfile.xml", + "install/skins/default/xui/fr/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/default/future/somefile.txt", + "install/skins/steam/colors.xml", + "install/skins/steam/xui/en/strings.xml", + "install/skins/steam/xui/fr/strings.xml", + "install/skins/steam/textures/only_steam.jpeg", + "user/skins/default/colors.xml", + "user/skins/default/xui/en/strings.xml", + "user/skins/default/xui/fr/strings.xml", + // This is an attempted override that doesn't work: for a + // localized subdir, a skin must have subdir/en/filename as well + // as subdir/language/filename. + "user/skins/default/xui/fr/floater.xml", + // This is an override that only specifies the "en" version + "user/skins/default/xui/en/newfile.xml", + "user/skins/default/textures/only_user_default.jpeg", + "user/skins/steam/colors.xml", + "user/skins/steam/xui/en/strings.xml", + "user/skins/steam/xui/fr/strings.xml", + "user/skins/steam/textures/only_user_steam.jpeg" + }; + 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 mFilesystem; + mutable std::set mChecked; +}; namespace tut { @@ -419,5 +569,192 @@ namespace tut 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", true), + 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 to be unlocalized. + 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", true), + 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", true), + 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", true), + 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"))); + + // The most specific skin for our dummy floater.xml is the installed + // default. Although we have a user xui/fr/floater.xml, we would also + // need a xui/en/floater.xml file to consider the user skin for this. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"), + vec(list_of + ("install/skins/default/xui/en/floater.xml") + ("install/skins/default/xui/fr/floater.xml"))); + + // The user override for the default skin does define newfile.xml, but + // only an "en" version, not a "fr" version as well. Nonetheless + // that's the most specific skin we have, regardless of the existence + // of a "fr" version in the installed default skin. + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"), + vec(list_of("user/skins/default/xui/en/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 the user default skins strings.xml has only a "fr" override + // but not a "zh" override, 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", true), + 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"))); + + // merge=false + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), + vec(list_of("user/skins/steam/xui/en/strings.xml"))); + + // pass merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + 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 merge=true to request this filename in all relevant skins + 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 merge=true to request this filename in all relevant skins + ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + 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"); + } +} -- cgit v1.2.3 From 730d13a76a4b06e6dbdd4bd5829a90bcb98b52b3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 11 Oct 2012 19:51:07 -0400 Subject: Change LLDir::findSkinnedFilenames() to use enum instead of bool. At Richard's suggestion, changed the bool merge parameter to new enum ESkinConstraint with values CURRENT_SKIN and ALL_SKINS. This clarifies what we're requesting at the point of the call. --- indra/llvfs/lldir.cpp | 19 ++++++++++--------- indra/llvfs/lldir.h | 22 ++++++++++++---------- indra/llvfs/tests/lldir_test.cpp | 14 +++++++------- 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index a7d12476a4..2d0adf14e6 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #if LL_WINDOWS #include "lldir_win32.h" @@ -544,10 +543,10 @@ std::string LLDir::getExtension(const std::string& filepath) const std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, const std::string &filename, - bool merge) const + ESkinConstraint constraint) const { // This implementation is basically just as described in the declaration comments. - std::vector found(findSkinnedFilenames(subdir, filename, merge)); + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); if (found.empty()) { return ""; @@ -557,10 +556,10 @@ std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename, - bool merge) const + ESkinConstraint constraint) const { // This implementation is basically just as described in the declaration comments. - std::vector found(findSkinnedFilenames(subdir, filename, merge)); + std::vector found(findSkinnedFilenames(subdir, filename, constraint)); if (found.empty()) { return ""; @@ -570,7 +569,7 @@ std::string LLDir::findSkinnedFilename(const std::string &subdir, std::vector LLDir::findSkinnedFilenames(const std::string& subdir, const std::string& filename, - bool merge) const + ESkinConstraint constraint) const { // Recognize subdirs that have no localization. static const char* sUnlocalizedData[] = @@ -582,7 +581,9 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, boost::end(sUnlocalizedData)); LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename - << "', merge " << std::boolalpha << merge << LL_ENDL; + << "', 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, @@ -672,8 +673,8 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, // Here the desired filename exists in the first subsubdir. That means // this is a skindir we want to record in results. But if the caller - // passed merge=false, we must discard all previous skindirs. - if (! merge) + // passed constraint=CURRENT_SKIN, we must discard all previous skindirs. + if (constraint == CURRENT_SKIN) { results.clear(); } diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index a242802979..ffa2676838 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -119,6 +119,8 @@ class LLDir // these methods search the various skin paths for the specified file in the following order: // getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir() + /// param value for findSkinnedFilenames(), explained below + enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS }; /** * Given a filename within skin, return an ordered sequence of paths to * search. Nonexistent files will be filtered out -- which means that the @@ -130,25 +132,25 @@ class LLDir * 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 merge 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 merge=false. The returned - * vector will contain only + * @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 * ".../current_skin/xui/en/filename", * ".../current_skin/xui/current_language/filename". * 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 merge=true. The returned vector will contain at - * least ".../default/xui/en/strings.xml", + * that case, pass @a constraint=ALL_SKINS. The returned vector will + * contain at least ".../default/xui/en/strings.xml", * ".../default/xui/current_language/strings.xml", * ".../current_skin/xui/en/strings.xml", * ".../current_skin/xui/current_language/strings.xml". */ std::vector findSkinnedFilenames(const std::string& subdir, const std::string& filename, - bool merge=false) const; + ESkinConstraint constraint=CURRENT_SKIN) const; /// Values for findSkinnedFilenames(subdir) parameter static const char *XUI, *TEXTURES, *SKINBASE; /** @@ -160,7 +162,7 @@ class LLDir */ std::string findSkinnedFilenameBaseLang(const std::string &subdir, const std::string &filename, - bool merge=false) const; + 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 @@ -170,7 +172,7 @@ class LLDir */ std::string findSkinnedFilename(const std::string &subdir, const std::string &filename, - bool merge=false) const; + ESkinConstraint constraint=CURRENT_SKIN) const; // random filename in common temporary directory std::string getTempFilename() const; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index a00fc8684c..15a5b6d4f3 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -584,7 +584,7 @@ namespace tut ensure_equals(lldir.getLanguage(), "en"); // top-level directory of a skin isn't localized - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + 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 @@ -599,13 +599,13 @@ namespace tut 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", true), + 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", true), + 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"); @@ -648,7 +648,7 @@ namespace tut ensure_equals(lldir.getLanguage(), "fr"); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + 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") @@ -691,7 +691,7 @@ namespace tut /*------------------------- "steam", "en" --------------------------*/ lldir.setSkinFolder("steam", "en"); - ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true), + ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), vec(list_of ("install/skins/default/colors.xml") ("install/skins/steam/colors.xml") @@ -715,7 +715,7 @@ namespace tut vec(list_of("user/skins/steam/xui/en/strings.xml"))); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + 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") @@ -732,7 +732,7 @@ namespace tut ("user/skins/steam/xui/fr/strings.xml"))); // pass merge=true to request this filename in all relevant skins - ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true), + 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") -- cgit v1.2.3 From 543b7ee9f7eb104497e971961fc0c39ed32ef54d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 12 Oct 2012 11:09:25 -0400 Subject: Fix LLUI::locateSkin() failure case; clarify lldir.cpp static init. Per code review: Previous refactoring of LLUI::locateSkin() preserved odd failure behavior: it would return last-considered pathname, whether or not it exists. Changed to emit LL_WARNS log message and return empty string. Use Boost.Assign to simplify initialization of a couple static containers in lldir.cpp. --- indra/llvfs/lldir.cpp | 71 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 2d0adf14e6..4c0978b39e 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -45,8 +45,12 @@ #include #include #include +#include #include +using boost::assign::list_of; +using boost::assign::map_list_of; + #if LL_WINDOWS #include "lldir_win32.h" LLDir_Win32 gDirUtil; @@ -342,37 +346,35 @@ const std::string &LLDir::getLLPluginDir() const static std::string ELLPathToString(ELLPath location) { - typedef std::map ELLPathMap; -#define ENT(symbol) ELLPathMap::value_type(symbol, #symbol) - static ELLPathMap::value_type init[] = - { - 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) - }; + typedef std::map 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 - static const ELLPathMap sMap(boost::begin(init), boost::end(init)); - ELLPathMap::const_iterator found = sMap.find(location); - if (found != sMap.end()) - return found->second; - return STRINGIZE("Invalid ELLPath value " << location); + 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 @@ -572,13 +574,10 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, ESkinConstraint constraint) const { // Recognize subdirs that have no localization. - static const char* sUnlocalizedData[] = - { - "", // top-level directory not localized - "textures" // textures not localized - }; - static const std::set sUnlocalized(boost::begin(sUnlocalizedData), - boost::end(sUnlocalizedData)); + static const std::set sUnlocalized = list_of + ("") // top-level directory not localized + ("textures") // textures not localized + ; LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename << "', constraint " -- cgit v1.2.3 From a9ea41003587e2411c7247f84ea04df2a686552b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 23 Oct 2012 16:06:03 -0400 Subject: Allow user to override either English, or localized, skinned file. Original LLDir::findSkinnedFilenames() implementation used a tricky rule: a given skin directory was only considered if it provided a default-language override for the sought filename, regardless of whether it also provided localizations for that filename. Discussion with Richard clarifies that we want to allow the user to override neither, either or both. Change findSkinnedFilenames() accordingly; update unit tests to verify new semantics. --- indra/llvfs/lldir.cpp | 132 ++++++++++++++++++++++++++------------- indra/llvfs/lldir.h | 7 +++ indra/llvfs/tests/lldir_test.cpp | 73 ++++++++++++---------- 3 files changed, 134 insertions(+), 78 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 4c0978b39e..5e5aeefba1 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -46,6 +46,8 @@ #include #include #include +#include +#include #include using boost::assign::list_of; @@ -569,6 +571,43 @@ std::string LLDir::findSkinnedFilename(const std::string &subdir, return found.back(); } +// This method exists because the two code paths for +// findSkinnedFilenames(ALL_SKINS) and findSkinnedFilenames(CURRENT_SKIN) must +// generate the list of candidate pathnames in identical ways. The only +// difference is in the body of the inner loop. +template +void LLDir::walkSearchSkinDirs(const std::string& subdir, + const std::vector& subsubdirs, + const std::string& filename, + const FUNCTION& function) const +{ + BOOST_FOREACH(std::string skindir, mSearchSkinDirs) + { + std::string subdir_path(add(skindir, subdir)); + BOOST_FOREACH(std::string subsubdir, subsubdirs) + { + std::string full_path(add(add(subdir_path, subsubdir), filename)); + if (fileExists(full_path)) + { + function(subsubdir, full_path); + } + } + } +} + +// ridiculous little helper function that should go away when we can use lambda +inline void push_back(std::vector& vector, const std::string& value) +{ + vector.push_back(value); +} + +typedef std::map 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 LLDir::findSkinnedFilenames(const std::string& subdir, const std::string& filename, ESkinConstraint constraint) const @@ -587,11 +626,10 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, // 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." - typedef std::map LocalizedMap; - static LocalizedMap sLocalized; + static StringMap sLocalized; // Check whether we've already discovered if this subdir is localized. - LocalizedMap::const_iterator found = sLocalized.find(subdir); + StringMap::const_iterator found = sLocalized.find(subdir); if (found == sLocalized.end()) { // We have not yet determined that. Is it one of the subdirs "known" @@ -599,7 +637,7 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, if (sUnlocalized.find(subdir) != sUnlocalized.end()) { // This subdir is known to be unlocalized. Remember that. - found = sLocalized.insert(LocalizedMap::value_type(subdir, "")).first; + found = sLocalized.insert(StringMap::value_type(subdir, "")).first; } else { @@ -608,20 +646,20 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, if (fileExists(add(subdir_path, "en"))) { // defaultSkinDir/subdir contains subdir "en". That's our - // default language; this subdir is localized. - found = sLocalized.insert(LocalizedMap::value_type(subdir, "en")).first; + // 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(LocalizedMap::value_type(subdir, "en-us")).first; + 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(LocalizedMap::value_type(subdir, "")).first; + found = sLocalized.insert(StringMap::value_type(subdir, "")).first; } } } @@ -648,48 +686,52 @@ std::vector LLDir::findSkinnedFilenames(const std::string& subdir, subsubdirs.push_back(mLanguage); } } - // Code below relies on subsubdirs not being empty: more specifically, on - // front() being valid. There may or may not be additional entries, but we - // have at least one. For an unlocalized subdir, it's the only one; for a - // localized subdir, it's the default one. - llassert(! subsubdirs.empty()); // Build results vector. std::vector results; - BOOST_FOREACH(std::string skindir, mSearchSkinDirs) - { - std::string subdir_path(add(skindir, subdir)); - // Does subdir_path/subsubdirs[0]/filename exist? If there's more than - // one entry in subsubdirs, the first is the default language ("en"), - // the second is the current language. A skin that contains - // subdir/language/filename without also containing subdir/en/filename - // is ill-formed: skip any such skin. So to decide whether to keep - // this skin dir or skip it, we need only check for the existence of - // the first subsubdir entry ("en" or only). - std::string subsubdir_path(add(add(subdir_path, subsubdirs.front()), filename)); - if (! fileExists(subsubdir_path)) - continue; - - // Here the desired filename exists in the first subsubdir. That means - // this is a skindir we want to record in results. But if the caller - // passed constraint=CURRENT_SKIN, we must discard all previous skindirs. - if (constraint == CURRENT_SKIN) - { - results.clear(); - } - - // Now add every subsubdir in which filename exists. We already know - // it exists in the first one. - results.push_back(subsubdir_path); - - // Append all remaining subsubdirs in which filename exists. - for (std::vector::const_iterator ssdi(subsubdirs.begin() + 1), ssdend(subsubdirs.end()); - ssdi != ssdend; ++ssdi) + // 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) { - subsubdir_path = add(add(subdir_path, *ssdi), filename); - if (fileExists(subsubdir_path)) + StringMap::const_iterator found(path_for.find(subsubdir)); + if (found != path_for.end()) { - results.push_back(subsubdir_path); + results.push_back(found->second); } } } diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index ffa2676838..eb36faa085 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -208,6 +208,13 @@ protected: // build mSearchSkinDirs without adding duplicates void addSearchSkinDir(const std::string& skindir); + // Internal to findSkinnedFilenames() + template + void walkSearchSkinDirs(const std::string& subdir, + const std::vector& 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 diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index 15a5b6d4f3..323f876c12 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -73,35 +73,41 @@ struct LLDir_Dummy: public LLDir // 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/default/future/somefile.txt", - "install/skins/steam/colors.xml", - "install/skins/steam/xui/en/strings.xml", - "install/skins/steam/xui/fr/strings.xml", "install/skins/steam/textures/only_steam.jpeg", - "user/skins/default/colors.xml", - "user/skins/default/xui/en/strings.xml", - "user/skins/default/xui/fr/strings.xml", - // This is an attempted override that doesn't work: for a - // localized subdir, a skin must have subdir/en/filename as well - // as subdir/language/filename. - "user/skins/default/xui/fr/floater.xml", - // This is an override that only specifies the "en" version - "user/skins/default/xui/en/newfile.xml", "user/skins/default/textures/only_user_default.jpeg", - "user/skins/steam/colors.xml", - "user/skins/steam/xui/en/strings.xml", - "user/skins/steam/xui/fr/strings.xml", - "user/skins/steam/textures/only_user_steam.jpeg" + "user/skins/steam/textures/only_user_steam.jpeg", + + "install/skins/default/future/somefile.txt" }; BOOST_FOREACH(const char* path, preload) { @@ -594,7 +600,7 @@ namespace tut 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 to be unlocalized. + // 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") @@ -661,20 +667,21 @@ namespace tut ("user/skins/default/xui/en/strings.xml") ("user/skins/default/xui/fr/strings.xml"))); - // The most specific skin for our dummy floater.xml is the installed - // default. Although we have a user xui/fr/floater.xml, we would also - // need a xui/en/floater.xml file to consider the user skin for this. + // 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") - ("install/skins/default/xui/fr/floater.xml"))); + ("user/skins/default/xui/fr/floater.xml"))); - // The user override for the default skin does define newfile.xml, but - // only an "en" version, not a "fr" version as well. Nonetheless - // that's the most specific skin we have, regardless of the existence - // of a "fr" version in the installed default skin. + // 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"))); + 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 @@ -683,8 +690,8 @@ namespace tut /*------------------------ "default", "zh" -------------------------*/ lldir.setSkinFolder("default", "zh"); - // Because the user default skins strings.xml has only a "fr" override - // but not a "zh" override, the most localized version we can find is "en". + // 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"))); @@ -710,11 +717,11 @@ namespace tut ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"), vec(list_of("user/skins/steam/textures/only_user_steam.jpeg"))); - // merge=false + // CURRENT_SKIN ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), vec(list_of("user/skins/steam/xui/en/strings.xml"))); - // pass merge=true to request this filename in all relevant skins + // 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") @@ -725,13 +732,13 @@ namespace tut /*------------------------- "steam", "fr" --------------------------*/ lldir.setSkinFolder("steam", "fr"); - // pass merge=true to request this filename in all relevant skins + // 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 merge=true to request this filename in all relevant skins + // 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") -- cgit v1.2.3 From 586d878ef5c415ebf711e5b3a5ad220e9671e50b Mon Sep 17 00:00:00 2001 From: callum_linden Date: Mon, 12 Nov 2012 16:55:20 -0800 Subject: First round of fixes to make viewer work with Boost 1.52 --- indra/llvfs/lldiriterator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp index ff92cbb7fd..fa806bf23e 100644 --- a/indra/llvfs/lldiriterator.cpp +++ b/indra/llvfs/lldiriterator.cpp @@ -59,7 +59,7 @@ LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask) { is_dir = fs::is_directory(dir_path); } - catch (fs::basic_filesystem_error& e) + catch (const fs::filesystem_error& e) { llwarns << e.what() << llendl; return; @@ -76,7 +76,7 @@ LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask) { mIter = fs::directory_iterator(dir_path); } - catch (fs::basic_filesystem_error& e) + catch (const fs::filesystem_error& e) { llwarns << e.what() << llendl; return; @@ -121,7 +121,7 @@ bool LLDirIterator::Impl::next(std::string &fname) while (mIter != end_itr && !found) { boost::smatch match; - std::string name = mIter->path().filename(); + std::string name = mIter->path().filename().string(); if (found = boost::regex_match(name, match, mFilterExp)) { fname = name; -- cgit v1.2.3 From eca619985206462d39dc9ae665857e97948562da Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Mon, 12 Nov 2012 18:06:32 -0800 Subject: Ported slplugin and some other missing components for cocoa. --- indra/llvfs/CMakeLists.txt | 6 +- indra/llvfs/lldir.h | 6 +- indra/llvfs/lldir_linux.cpp | 2 +- indra/llvfs/lldir_linux.h | 2 +- indra/llvfs/lldir_mac.cpp | 237 +++++++++++++----------------------------- indra/llvfs/lldir_mac.h | 10 +- indra/llvfs/lldir_solaris.cpp | 2 +- indra/llvfs/lldir_solaris.h | 2 +- indra/llvfs/lldir_win32.cpp | 2 +- indra/llvfs/lldir_win32.h | 2 +- 10 files changed, 84 insertions(+), 187 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt index a819d12861..3c68b279f7 100644 --- a/indra/llvfs/CMakeLists.txt +++ b/indra/llvfs/CMakeLists.txt @@ -36,6 +36,8 @@ 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) @@ -73,8 +75,8 @@ target_link_libraries(llvfs 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) diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 5ee8bdb542..35b2654ae7 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -1,4 +1,4 @@ -/** +/** * @file lldir.h * @brief Definition of directory utilities class * @@ -73,10 +73,8 @@ class LLDir virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask); // pure virtual functions - virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 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 filenames) const; const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const; diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index 407f3b93fb..4edd078640 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -254,7 +254,7 @@ std::string LLDir_Linux::getCurPath() } -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 7603239867..e83a020ba4 100644 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -47,7 +47,7 @@ public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*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); diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 489bc3e4a7..75b3d56ebc 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -22,7 +22,7 @@ * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ - */ + */ #if LL_DARWIN @@ -35,73 +35,27 @@ #include #include #include - -#include - -// -------------------------------------------------------------------------------- - -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 +#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; } // -------------------------------------------------------------------------------- @@ -109,35 +63,26 @@ 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); + + mExecutableFilename = executablepath.filename(); + 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 @@ -146,10 +91,7 @@ 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 build_dir_pos = mExecutableDir.rfind("/build-darwin-"); if (build_dir_pos != std::string::npos) { @@ -166,55 +108,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; + //SPATTERS 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; } } @@ -235,52 +172,18 @@ 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); } 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); } diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index d190d70be4..558727ebbc 100644 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -22,7 +22,7 @@ * * 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. @@ -45,16 +45,10 @@ public: const std::string& app_read_only_data_dir); virtual std::string getCurPath(); - virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - virtual BOOL fileExists(const std::string &filename) const; + virtual 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 21f8c3acdb..a97d72d539 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -272,7 +272,7 @@ std::string LLDir_Solaris::getCurPath() } -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 0b58a45b15..c6dac57e14 100644 --- a/indra/llvfs/lldir_solaris.h +++ b/indra/llvfs/lldir_solaris.h @@ -47,7 +47,7 @@ public: virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ bool fileExists(const std::string &filename) const; private: DIR *mDirp; diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index 7709945123..462d1cce06 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -249,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 62fb4713ab..450efaf9da 100644 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -44,7 +44,7 @@ public: /*virtual*/ std::string getCurPath(); /*virtual*/ U32 countFilesInDir(const std::string &dirname, const std::string &mask); - /*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); -- cgit v1.2.3 From aaf96507a15196145d903764dc905d8d6e7df7b0 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 16 Nov 2012 18:08:40 -0500 Subject: Some Mac header #defines macros like check and equivalent -- gack!! Given that third-party libraries (such as Boost) can and do use those names, properly namespace-scoped, it's unpardonable to break any such innocent usage with a macro. Given the pervasiveness of the need, introduce a header file with the requisite #undef directives. --- indra/llvfs/lldiriterator.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldiriterator.cpp b/indra/llvfs/lldiriterator.cpp index fa806bf23e..460d2a8b4f 100644 --- a/indra/llvfs/lldiriterator.cpp +++ b/indra/llvfs/lldiriterator.cpp @@ -26,6 +26,7 @@ #include "lldiriterator.h" +#include "fix_macros.h" #include #include -- cgit v1.2.3 From acc53a1c226999878d26b0b4bd58ac8df3256c06 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Tue, 27 Nov 2012 14:27:52 -0800 Subject: New VFS objects included in every bite! --- indra/llvfs/llvfs_objc.h | 43 ++++++++++++++++++++++ indra/llvfs/llvfs_objc.mm | 93 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 indra/llvfs/llvfs_objc.h create mode 100644 indra/llvfs/llvfs_objc.mm (limited to 'indra/llvfs') diff --git a/indra/llvfs/llvfs_objc.h b/indra/llvfs/llvfs_objc.h new file mode 100644 index 0000000000..90101eb2e9 --- /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 + +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 100644 index 0000000000..4d7cd3a93e --- /dev/null +++ b/indra/llvfs/llvfs_objc.mm @@ -0,0 +1,93 @@ +/** + * @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 + +std::string* getSystemTempFolder() +{ + NSString * tempDir = NSTemporaryDirectory(); + if (tempDir == nil) + tempDir = @"/tmp"; + return ( new std::string([tempDir UTF8String]) ); +} + +//findSystemDirectory scoped exclusively to this file. +std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, + NSSearchPathDomainMask domainMask) +{ + std::string *result; + NSString *path = nil; + + // Search for the path + NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory, + domainMask, + YES); + if ([paths count]) + { + NSString *bundleName = + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; + path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName]; + + //SPATTERS 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]); + } + return result; +} + +std::string* getSystemExecutableFolder() +{ + NSString *bundlePath = [[NSBundle mainBundle] executablePath]; + return (new std::string([bundlePath UTF8String])); +} + +std::string* getSystemResourceFolder() +{ + NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; + return (new std::string([bundlePath UTF8String])); +} + +std::string* getSystemCacheFolder() +{ + return findSystemDirectory (NSCachesDirectory, + NSUserDomainMask); +} + +std::string* getSystemApplicationSupportFolder() +{ + return findSystemDirectory (NSApplicationSupportDirectory, + NSUserDomainMask); +} + +#endif // LL_DARWIN -- cgit v1.2.3 From 1149c94c5b5008f6c076ce4b072f54e48c4783ae Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Thu, 29 Nov 2012 23:33:31 -0800 Subject: Crashlogger only runs when it comes from teamcity so I must checkin debug and wait. What fun. --- indra/llvfs/lldir.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 32d081d552..bd5ce4acac 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -431,6 +431,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd llassert(0); } + llinfos << "SPATTERS got " << prefix << " for location " << location << llendl; std::string filename = in_filename; if (!subdir2.empty()) { @@ -471,7 +472,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd expanded_filename.assign(""); } - //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; + llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; return expanded_filename; } -- cgit v1.2.3 From 9085dc3e84b66e9f3802ebaf23dedb5891c39065 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Fri, 30 Nov 2012 00:46:02 -0800 Subject: Removed logging. --- indra/llvfs/lldir.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index bd5ce4acac..32d081d552 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -431,7 +431,6 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd llassert(0); } - llinfos << "SPATTERS got " << prefix << " for location " << location << llendl; std::string filename = in_filename; if (!subdir2.empty()) { @@ -472,7 +471,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd expanded_filename.assign(""); } - llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; + //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; return expanded_filename; } -- cgit v1.2.3 From 53ea30af06f94b5462fada7c4bfea3122b50ff95 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Tue, 11 Dec 2012 06:56:14 -0800 Subject: Interim debug checkin to test from teamcity --- indra/llvfs/llvfs_objc.mm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm index 4d7cd3a93e..61cfad99d6 100644 --- a/indra/llvfs/llvfs_objc.mm +++ b/indra/llvfs/llvfs_objc.mm @@ -51,10 +51,10 @@ std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, YES); if ([paths count]) { - NSString *bundleName = - [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; - path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName]; - + //NSString *bundleName = @"SecondLife"; + //[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; + //path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName]; + path = [paths objectAtIndex:0]; //SPATTERS HACK: Always attempt to create directory, ignore errors. NSError *error = nil; @@ -88,6 +88,7 @@ std::string* getSystemApplicationSupportFolder() { return findSystemDirectory (NSApplicationSupportDirectory, NSUserDomainMask); + } #endif // LL_DARWIN -- cgit v1.2.3 From 6b059d183b9516f45e775bde00f255cef8aab7c0 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Tue, 11 Dec 2012 07:40:26 -0800 Subject: Crash reporter fixed, debugging removed. --- indra/llvfs/llvfs_objc.mm | 3 --- 1 file changed, 3 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm index 61cfad99d6..16cfefda26 100644 --- a/indra/llvfs/llvfs_objc.mm +++ b/indra/llvfs/llvfs_objc.mm @@ -51,9 +51,6 @@ std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, YES); if ([paths count]) { - //NSString *bundleName = @"SecondLife"; - //[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; - //path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName]; path = [paths objectAtIndex:0]; //SPATTERS HACK: Always attempt to create directory, ignore errors. NSError *error = nil; -- cgit v1.2.3 From 3a72577f397a3cd3ebcb2d3e80000716bf0befec Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Thu, 13 Dec 2012 18:27:32 -0800 Subject: Fixes from Oz's merge. --- indra/llvfs/lldir_mac.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 75b3d56ebc..c5041d434c 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -77,7 +77,9 @@ LLDir_Mac::LLDir_Mac() boost::filesystem::path executablepath(*executablepathstr); - mExecutableFilename = executablepath.filename(); +# ifndef BOOST_SYSTEM_NO_DEPRECATED +#endif + mExecutableFilename = executablepath.filename().string(); mExecutableDir = executablepath.parent_path().string(); // mAppRODataDir -- cgit v1.2.3 From 5c9dd94e197a5db10fdf794ec1632e5586053df8 Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Fri, 14 Dec 2012 21:41:02 +0000 Subject: BOOL vs bool FIGHT --- indra/llvfs/tests/lldir_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index 323f876c12..3cff622a4b 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -134,7 +134,7 @@ struct LLDir_Dummy: public LLDir return 0; } - virtual BOOL fileExists(const std::string& pathname) const + 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 -- cgit v1.2.3 From c1db961ab9b7aa9c0dd43cadf850b7527b77e95b Mon Sep 17 00:00:00 2001 From: Aura Linden Date: Sun, 16 Dec 2012 17:45:55 -0800 Subject: Fixed small one-time memory leaks. --- indra/llvfs/llvfs_objc.mm | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm index 16cfefda26..4f9e2f81e9 100644 --- a/indra/llvfs/llvfs_objc.mm +++ b/indra/llvfs/llvfs_objc.mm @@ -32,16 +32,22 @@ std::string* getSystemTempFolder() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString * tempDir = NSTemporaryDirectory(); if (tempDir == nil) tempDir = @"/tmp"; - return ( new std::string([tempDir UTF8String]) ); + 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; NSString *path = nil; @@ -60,19 +66,30 @@ std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, result = new std::string([path UTF8String]); } + [pool release]; return result; } std::string* getSystemExecutableFolder() { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString *bundlePath = [[NSBundle mainBundle] executablePath]; - return (new std::string([bundlePath UTF8String])); + 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]; - return (new std::string([bundlePath UTF8String])); + std::string *result = (new std::string([bundlePath UTF8String])); + [pool release]; + + return result; } std::string* getSystemCacheFolder() -- cgit v1.2.3 From 2ac99f55f9e562e4ff8ebde4cba8270f1048c28d Mon Sep 17 00:00:00 2001 From: maksymsproductengine Date: Thu, 24 Jan 2013 19:56:13 +0200 Subject: CHUI-578 FIXED Move preferences for conversation logs/transcripts to chat tab --- indra/llvfs/lldir.cpp | 13 ++++++++++--- indra/llvfs/lldir.h | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 5e5aeefba1..f7bc19574a 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -90,7 +90,8 @@ LLDir::LLDir() mCAFile(""), mTempDir(""), mDirDelimiter("/"), // fallback to forward slash if not overridden - mLanguage("en") + mLanguage("en"), + mUserName("undefined") { } @@ -814,6 +815,11 @@ void LLDir::setChatLogsDir(const std::string &path) } } +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 @@ -824,13 +830,14 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) std::string userlower(username); LLStringUtil::toLower(userlower); LLStringUtil::replaceChar(userlower, ' ', '_'); - mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); + + mUserName = userlower; + updatePerAccountChatLogsDir(); } else { llerrs << "NULL name for LLDir::setPerAccountChatLogsDir" << llendl; } - } void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 300ff1eef6..95cab65149 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -186,6 +186,7 @@ class LLDir virtual std::string getSkinFolder() const; virtual std::string getLanguage() const; virtual bool setCacheDir(const std::string &path); + virtual void updatePerAccountChatLogsDir(); virtual void dumpCurrentDirectories(); @@ -243,6 +244,7 @@ protected: std::vector mSearchSkinDirs; std::string mLanguage; // Current viewer language std::string mLLPluginDir; // Location for plugins and plugin shell + std::string mUserName; // Current user name }; void dir_exists_or_crash(const std::string &dir_name); -- cgit v1.2.3 From 05f4e8a10517b3b341359a210aeb0af06c44d43a Mon Sep 17 00:00:00 2001 From: Gilbert Gonzales Date: Fri, 22 Feb 2013 18:53:40 -0800 Subject: CHUI-778 (Saving preferences updates text in all open message panels in conversation floater to show as old messages) Now changing the log/transcripts file location actually moves the files to the new location. Prior behavior just started a new history at that location. Also a fix was made so that if the user changed the log/transcripts path then after pressing the Preferences 'OK' button the new location will be saved to the corresponding .xml file. --- indra/llvfs/lldir.cpp | 5 +++++ indra/llvfs/lldir.h | 1 + 2 files changed, 6 insertions(+) (limited to 'indra/llvfs') diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index f7bc19574a..6899e9a44a 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -347,6 +347,11 @@ 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 ELLPathMap; diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 95cab65149..cc10ed5bbd 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -104,6 +104,7 @@ class LLDir const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin const std::string getSkinBaseDir() const; // folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins const std::string &getLLPluginDir() const; // Directory containing plugins and plugin shell + const std::string &getUserName() const; // Expanded filename std::string getExpandedFilename(ELLPath location, const std::string &filename) const; -- cgit v1.2.3