/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011-2017, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "FileManager.h"
#include <new>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "LocatableDirectory.h"
#include "LocatableFile.h"
#include "SourceFile.h"
#include "StringUtils.h"
#include "TeamFileManagerSettings.h"
// #pragma mark - EntryPath
struct FileManager::EntryPath {
const char* directory;
const char* name;
EntryPath(const char* directory, const char* name)
:
directory(directory),
name(name)
{
}
EntryPath(const BString& directory, const BString& name)
:
directory(directory.Length() > 0 ? directory.String() : NULL),
name(name.String())
{
}
EntryPath(const LocatableEntry* entry)
:
directory(NULL),
name(entry->Name())
{
LocatableDirectory* parent = entry->Parent();
if (parent != NULL && strlen(parent->Path()) > 0)
directory = parent->Path();
}
EntryPath(const EntryPath& other)
:
directory(other.directory),
name(other.name)
{
}
size_t HashValue() const
{
return StringUtils::HashValue(directory)
^ StringUtils::HashValue(name);
}
bool operator==(const EntryPath& other) const
{
if (directory != other.directory
&& (directory == NULL || other.directory == NULL
|| strcmp(directory, other.directory) != 0)) {
return false;
}
return strcmp(name, other.name) == 0;
}
};
// #pragma mark - EntryHashDefinition
struct FileManager::EntryHashDefinition {
typedef EntryPath KeyType;
typedef LocatableEntry ValueType;
size_t HashKey(const EntryPath& key) const
{
return key.HashValue();
}
size_t Hash(const LocatableEntry* value) const
{
return HashKey(EntryPath(value));
}
bool Compare(const EntryPath& key, const LocatableEntry* value) const
{
return EntryPath(value) == key;
}
LocatableEntry*& GetLink(LocatableEntry* value) const
{
return value->fNext;
}
};
// #pragma mark - Domain
class FileManager::Domain : private LocatableEntryOwner {
public:
Domain(FileManager* manager, bool isLocal)
:
fManager(manager),
fIsLocal(isLocal)
{
}
~Domain()
{
LocatableEntry* entry = fEntries.Clear(true);
while (entry != NULL) {
LocatableEntry* next = entry->fNext;
entry->ReleaseReference();
entry = next;
}
}
status_t Init()
{
status_t error = fEntries.Init();
if (error != B_OK)
return error;
return B_OK;
}
LocatableFile* GetFile(const BString& directoryPath,
const BString& relativePath)
{
if (directoryPath.Length() == 0 || relativePath[0] == '/')
return GetFile(relativePath);
return GetFile(BString(directoryPath) << '/' << relativePath);
}
LocatableFile* GetFile(const BString& path)
{
BString directoryPath;
BString name;
_SplitPath(path, directoryPath, name);
LocatableFile* file = _GetFile(directoryPath, name);
if (file == NULL)
return NULL;
// try to auto-locate the file
if (LocatableDirectory* directory = file->Parent()) {
if (directory->State() == LOCATABLE_ENTRY_UNLOCATED) {
// parent not yet located -- try locate with the entry's path
BString path;
file->GetPath(path);
_LocateEntry(file, path, true, true);
} else {
// parent already located -- locate the entry in the parent
BString locatedDirectoryPath;
if (directory->GetLocatedPath(locatedDirectoryPath))
_LocateEntryInParentDir(file, locatedDirectoryPath, true);
}
}
return file;
}
void EntryLocated(const BString& path, const BString& locatedPath)
{
BString directory;
BString name;
_SplitPath(path, directory, name);
LocatableEntry* entry = _LookupEntry(EntryPath(directory, name));
if (entry == NULL)
return;
_LocateEntry(entry, locatedPath, false, true);
}
private:
virtual bool Lock()
{
return fManager->Lock();
}
virtual void Unlock()
{
fManager->Unlock();
}
virtual void LocatableEntryUnused(LocatableEntry* entry)
{
AutoLocker<FileManager> lock(fManager);
if (fEntries.Lookup(EntryPath(entry)) == entry)
fEntries.Remove(entry);
LocatableDirectory* parent = entry->Parent();
if (parent != NULL)
parent->RemoveEntry(entry);
}
bool _LocateDirectory(LocatableDirectory* directory,
const BString& locatedPath, bool implicit)
{
if (directory == NULL
|| directory->State() != LOCATABLE_ENTRY_UNLOCATED) {
return false;
}
if (!_LocateEntry(directory, locatedPath, implicit, true))
return false;
_LocateEntries(directory, locatedPath, implicit);
return true;
}
bool _LocateEntry(LocatableEntry* entry, const BString& locatedPath,
bool implicit, bool locateAncestors)
{
if (implicit && entry->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY)
return false;
struct stat st;
if (stat(locatedPath, &st) != 0)
return false;
if (S_ISDIR(st.st_mode)) {
LocatableDirectory* directory
= dynamic_cast<LocatableDirectory*>(entry);
if (directory == NULL)
return false;
entry->SetLocatedPath(locatedPath, implicit);
} else if (S_ISREG(st.st_mode)) {
LocatableFile* file = dynamic_cast<LocatableFile*>(entry);
if (file == NULL)
return false;
entry->SetLocatedPath(locatedPath, implicit);
}
// locate the ancestor directories, if requested
if (locateAncestors) {
BString locatedDirectory;
BString locatedName;
_SplitPath(locatedPath, locatedDirectory, locatedName);
if (locatedName == entry->Name())
_LocateDirectory(entry->Parent(), locatedDirectory, implicit);
}
return true;
}
bool _LocateEntryInParentDir(LocatableEntry* entry,
const BString& locatedDirectoryPath, bool implicit)
{
// construct the located entry path
BString locatedEntryPath(locatedDirectoryPath);
int32 pathLength = locatedEntryPath.Length();
if (pathLength >= 1 && locatedEntryPath[pathLength - 1] != '/')
locatedEntryPath << '/';
locatedEntryPath << entry->Name();
return _LocateEntry(entry, locatedEntryPath, implicit, false);
}
void _LocateEntries(LocatableDirectory* directory,
const BString& locatedPath, bool implicit)
{
for (LocatableEntryList::ConstIterator it
= directory->Entries().GetIterator();
LocatableEntry* entry = it.Next();) {
if (entry->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY)
continue;
if (_LocateEntryInParentDir(entry, locatedPath, implicit)) {
// recurse for directories
if (LocatableDirectory* subDir
= dynamic_cast<LocatableDirectory*>(entry)) {
BString locatedEntryPath;
if (subDir->GetLocatedPath(locatedEntryPath))
_LocateEntries(subDir, locatedEntryPath, implicit);
}
}
}
}
LocatableFile* _GetFile(const BString& directoryPath, const BString& name)
{
BString normalizedDirPath;
_NormalizePath(directoryPath, normalizedDirPath);
// if already known return the file
LocatableEntry* entry = _LookupEntry(EntryPath(normalizedDirPath, name));
if (entry != NULL) {
LocatableFile* file = dynamic_cast<LocatableFile*>(entry);
if (file == NULL)
return NULL;
if (file->AcquireReference() == 0)
fEntries.Remove(file);
else
return file;
}
// no such file yet -- create it
LocatableDirectory* directory = _GetDirectory(normalizedDirPath);
if (directory == NULL)
return NULL;
LocatableFile* file = new(std::nothrow) LocatableFile(this, directory,
name);
if (file == NULL) {
directory->ReleaseReference();
return NULL;
}
directory->AddEntry(file);
fEntries.Insert(file);
return file;
}
LocatableDirectory* _GetDirectory(const BString& path)
{
BString directoryPath;
BString fileName;
_SplitNormalizedPath(path, directoryPath, fileName);
// if already know return the directory
LocatableEntry* entry
= _LookupEntry(EntryPath(directoryPath, fileName));
if (entry != NULL) {
LocatableDirectory* directory
= dynamic_cast<LocatableDirectory*>(entry);
if (directory == NULL)
return NULL;
directory->AcquireReference();
return directory;
}
// get the parent directory
LocatableDirectory* parentDirectory = NULL;
if (directoryPath.Length() > 0) {
parentDirectory = _GetDirectory(directoryPath);
if (parentDirectory == NULL)
return NULL;
}
// create a new directory
LocatableDirectory* directory = new(std::nothrow) LocatableDirectory(
this, parentDirectory, path);
if (directory == NULL) {
parentDirectory->ReleaseReference();
return NULL;
}
// auto-locate, if possible
if (fIsLocal) {
BString dirPath;
directory->GetPath(dirPath);
directory->SetLocatedPath(dirPath, false);
} else if (parentDirectory != NULL
&& parentDirectory->State() != LOCATABLE_ENTRY_UNLOCATED) {
BString locatedDirectoryPath;
if (parentDirectory->GetLocatedPath(locatedDirectoryPath))
_LocateEntryInParentDir(directory, locatedDirectoryPath, true);
}
if (parentDirectory != NULL)
parentDirectory->AddEntry(directory);
fEntries.Insert(directory);
return directory;
}
LocatableEntry* _LookupEntry(const EntryPath& entryPath)
{
LocatableEntry* entry = fEntries.Lookup(entryPath);
if (entry == NULL)
return NULL;
// if already unreferenced, remove it
if (entry->CountReferences() == 0) {
fEntries.Remove(entry);
return NULL;
}
return entry;
}
void _NormalizePath(const BString& path, BString& _normalizedPath)
{
BString normalizedPath;
char* buffer = normalizedPath.LockBuffer(path.Length());
int32 outIndex = 0;
const char* remaining = path.String();
while (*remaining != '\0') {
// collapse repeated slashes
if (*remaining == '/') {
buffer[outIndex++] = '/';
remaining++;
while (*remaining == '/')
remaining++;
}
if (*remaining == '\0') {
// remove trailing slash (unless it's "/" only)
if (outIndex > 1)
outIndex--;
break;
}
// skip "." components
if (*remaining == '.') {
if (remaining[1] == '\0')
break;
if (remaining[1] == '/') {
remaining += 2;
while (*remaining == '/')
remaining++;
continue;
}
}
// copy path component
while (*remaining != '\0' && *remaining != '/')
buffer[outIndex++] = *(remaining++);
}
// If the path didn't change, use the original path (BString's copy on
// write mechanism) rather than the new string.
if (outIndex == path.Length()) {
_normalizedPath = path;
} else {
normalizedPath.UnlockBuffer(outIndex);
_normalizedPath = normalizedPath;
}
}
void _SplitPath(const BString& path, BString& _directory, BString& _name)
{
BString normalized;
_NormalizePath(path, normalized);
_SplitNormalizedPath(normalized, _directory, _name);
}
void _SplitNormalizedPath(const BString& path, BString& _directory,
BString& _name)
{
// handle single component (including root dir) cases
int32 lastSlash = path.FindLast('/');
if (lastSlash < 0 || path.Length() == 1) {
_directory = (const char*)NULL;
_name = path;
return;
}
// handle root dir + one component and multi component cases
if (lastSlash == 0)
_directory = "/";
else
_directory.SetTo(path, lastSlash);
_name = path.String() + (lastSlash + 1);
}
private:
FileManager* fManager;
LocatableEntryTable fEntries;
bool fIsLocal;
};
// #pragma mark - SourceFileEntry
struct FileManager::SourceFileEntry : public SourceFileOwner {
FileManager* manager;
BString path;
SourceFile* file;
SourceFileEntry* next;
SourceFileEntry(FileManager* manager, const BString& path)
:
manager(manager),
path(path),
file(NULL)
{
}
virtual void SourceFileUnused(SourceFile* sourceFile)
{
manager->_SourceFileUnused(this);
}
virtual void SourceFileDeleted(SourceFile* sourceFile)
{
// We have already been removed from the table, so commit suicide.
delete this;
}
};
// #pragma mark - SourceFileHashDefinition
struct FileManager::SourceFileHashDefinition {
typedef BString KeyType;
typedef SourceFileEntry ValueType;
size_t HashKey(const BString& key) const
{
return StringUtils::HashValue(key);
}
size_t Hash(const SourceFileEntry* value) const
{
return HashKey(value->path);
}
bool Compare(const BString& key, const SourceFileEntry* value) const
{
return value->path == key;
}
SourceFileEntry*& GetLink(SourceFileEntry* value) const
{
return value->next;
}
};
// #pragma mark - FileManager
FileManager::FileManager()
:
fLock("file manager"),
fTargetDomain(NULL),
fSourceDomain(NULL),
fSourceFiles(NULL)
{
}
FileManager::~FileManager()
{
delete fTargetDomain;
delete fSourceDomain;
SourceFileEntry* entry = fSourceFiles->Clear();
while (entry != NULL) {
SourceFileEntry* next = entry->next;
delete entry;
entry = next;
}
delete fSourceFiles;
}
status_t
FileManager::Init(bool targetIsLocal)
{
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
// create target domain
fTargetDomain = new(std::nothrow) Domain(this, targetIsLocal);
if (fTargetDomain == NULL)
return B_NO_MEMORY;
error = fTargetDomain->Init();
if (error != B_OK)
return error;
// create source domain
fSourceDomain = new(std::nothrow) Domain(this, false);
if (fSourceDomain == NULL)
return B_NO_MEMORY;
error = fSourceDomain->Init();
if (error != B_OK)
return error;
// create source file table
fSourceFiles = new(std::nothrow) SourceFileTable;
if (fSourceFiles == NULL)
return B_NO_MEMORY;
error = fSourceFiles->Init();
if (error != B_OK)
return error;
return B_OK;
}
LocatableFile*
FileManager::GetTargetFile(const BString& directory,
const BString& relativePath)
{
AutoLocker<FileManager> locker(this);
return fTargetDomain->GetFile(directory, relativePath);
}
LocatableFile*
FileManager::GetTargetFile(const BString& path)
{
AutoLocker<FileManager> locker(this);
return fTargetDomain->GetFile(path);
}
void
FileManager::TargetEntryLocated(const BString& path,
const BString& locatedPath)
{
AutoLocker<FileManager> locker(this);
fTargetDomain->EntryLocated(path, locatedPath);
}
LocatableFile*
FileManager::GetSourceFile(const BString& directory,
const BString& relativePath)
{
AutoLocker<FileManager> locker(this);
LocatableFile* file = fSourceDomain->GetFile(directory, relativePath);
return file;
}
LocatableFile*
FileManager::GetSourceFile(const BString& path)
{
AutoLocker<FileManager> locker(this);
LocatableFile* file = fSourceDomain->GetFile(path);
return file;
}
status_t
FileManager::SourceEntryLocated(const BString& path,
const BString& locatedPath)
{
AutoLocker<FileManager> locker(this);
// check if we already have this path mapped. If so,
// first clear the mapping, as the user may be attempting
// to correct an existing entry.
SourceFileEntry* entry = _LookupSourceFile(path);
if (entry != NULL)
_SourceFileUnused(entry);
fSourceDomain->EntryLocated(path, locatedPath);
try {
fSourceLocationMappings[path] = locatedPath;
} catch (...) {
return B_NO_MEMORY;
}
return B_OK;
}
status_t
FileManager::LoadSourceFile(LocatableFile* file, SourceFile*& _sourceFile)
{
AutoLocker<FileManager> locker(this);
// get the path
BString path;
BString originalPath;
file->GetPath(originalPath);
if (!file->GetLocatedPath(path)) {
// see if this is a file we have a lazy mapping for.
if (!_LocateFileIfMapped(originalPath, file)
|| !file->GetLocatedPath(path)) {
return B_ENTRY_NOT_FOUND;
}
}
// we might already know the source file
SourceFileEntry* entry = _LookupSourceFile(originalPath);
if (entry != NULL) {
entry->file->AcquireReference();
_sourceFile = entry->file;
return B_OK;
}
// create the hash table entry
entry = new(std::nothrow) SourceFileEntry(this, originalPath);
if (entry == NULL)
return B_NO_MEMORY;
// load the file
SourceFile* sourceFile = new(std::nothrow) SourceFile(entry);
if (sourceFile == NULL) {
delete entry;
return B_NO_MEMORY;
}
ObjectDeleter<SourceFile> sourceFileDeleter(sourceFile);
entry->file = sourceFile;
status_t error = sourceFile->Init(path);
if (error != B_OK)
return error;
fSourceFiles->Insert(entry);
_sourceFile = sourceFileDeleter.Detach();
return B_OK;
}
status_t
FileManager::LoadLocationMappings(TeamFileManagerSettings* settings)
{
AutoLocker<FileManager> locker(this);
for (int32 i = 0; i < settings->CountSourceMappings(); i++) {
BString sourcePath;
BString locatedPath;
if (settings->GetSourceMappingAt(i, sourcePath, locatedPath) != B_OK)
return B_NO_MEMORY;
try {
fSourceLocationMappings[sourcePath] = locatedPath;
} catch (...) {
return B_NO_MEMORY;
}
}
return B_OK;
}
status_t
FileManager::SaveLocationMappings(TeamFileManagerSettings* settings)
{
AutoLocker<FileManager> locker(this);
for (LocatedFileMap::const_iterator it = fSourceLocationMappings.begin();
it != fSourceLocationMappings.end(); ++it) {
status_t error = settings->AddSourceMapping(it->first, it->second);
if (error != B_OK)
return error;
}
return B_OK;
}
FileManager::SourceFileEntry*
FileManager::_LookupSourceFile(const BString& path)
{
SourceFileEntry* entry = fSourceFiles->Lookup(path);
if (entry == NULL)
return NULL;
// the entry might be unused already -- in that case remove it
if (entry->file->CountReferences() == 0) {
fSourceFiles->Remove(entry);
return NULL;
}
return entry;
}
void
FileManager::_SourceFileUnused(SourceFileEntry* entry)
{
AutoLocker<FileManager> locker(this);
SourceFileEntry* otherEntry = fSourceFiles->Lookup(entry->path);
if (otherEntry == entry)
fSourceFiles->Remove(entry);
}
bool
FileManager::_LocateFileIfMapped(const BString& sourcePath,
LocatableFile* file)
{
// called with lock held
LocatedFileMap::const_iterator it = fSourceLocationMappings.find(
sourcePath);
if (it != fSourceLocationMappings.end()
&& file->State() != LOCATABLE_ENTRY_LOCATED_EXPLICITLY
&& file->State() != LOCATABLE_ENTRY_LOCATED_IMPLICITLY) {
fSourceDomain->EntryLocated(it->first, it->second);
return true;
}
return false;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: next.
↑ V595 The 'parentDirectory' pointer was utilized before it was verified against nullptr. Check lines: 360, 369.