Files
haiku-beta6/src/kits/debugger/files/FileManager.cpp
T
Rene Gollent 584fd9619c libdebugger: Fix #12944.
LocatableFile:
- If there is no parent path, don't insert a path separator between parent
  and filename. This may be the case depending on how the source file was
  specified during compilation.

FileManager:
- When constructing an EntryPath from a LocatableEntry, ensure that the
  parent folder actually has a path string that isn't simply empty to ensure
  consistency with the raw dir/file case. Otherwise, hash lookups that are
  dependent on the parent dir being NULL if not specified will fail, causing
  us to not locate the file successfully. This was preventing us from updating
  source location information for make 4.2's main.c, as the latter was
  specified in such a way that the above combination of conditions would
  occur, and consequently when asking the FileManager to update the source
  location with the actual file, the entry couldn't be found in the table,
  and no information would be updated.
2016-09-06 22:56:52 -04:00

806 lines
17 KiB
C++

/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Copyright 2011-2016, Rene Gollent, [email protected].
* 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()
{
}
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);
else {
DeadEntryList::Iterator iterator = fDeadEntries.GetIterator();
while (iterator.HasNext()) {
if (iterator.Next() == entry) {
fDeadEntries.Remove(entry);
break;
}
}
}
}
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)
{
// if already known return the file
LocatableEntry* entry = _LookupEntry(EntryPath(directoryPath, name));
if (entry != NULL) {
LocatableFile* file = dynamic_cast<LocatableFile*>(entry);
if (file == NULL)
return NULL;
if (file->AcquireReference() == 0) {
fEntries.Remove(file);
fDeadEntries.Insert(file);
} else
return file;
}
// no such file yet -- create it
BString normalizedDirPath;
_NormalizePath(directoryPath, normalizedDirPath);
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;
DeadEntryList fDeadEntries;
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;
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;
}