2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2017, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
7 #include "FileManager.h"
11 #include <AutoDeleter.h>
12 #include <AutoLocker.h>
14 #include "LocatableDirectory.h"
15 #include "LocatableFile.h"
16 #include "SourceFile.h"
17 #include "StringUtils.h"
18 #include "TeamFileManagerSettings.h"
21 // #pragma mark - EntryPath
24 struct FileManager::EntryPath
{
25 const char* directory
;
28 EntryPath(const char* directory
, const char* name
)
35 EntryPath(const BString
& directory
, const BString
& name
)
37 directory(directory
.Length() > 0 ? directory
.String() : NULL
),
42 EntryPath(const LocatableEntry
* entry
)
47 LocatableDirectory
* parent
= entry
->Parent();
48 if (parent
!= NULL
&& strlen(parent
->Path()) > 0)
49 directory
= parent
->Path();
52 EntryPath(const EntryPath
& other
)
54 directory(other
.directory
),
59 size_t HashValue() const
61 return StringUtils::HashValue(directory
)
62 ^ StringUtils::HashValue(name
);
65 bool operator==(const EntryPath
& other
) const
67 if (directory
!= other
.directory
68 && (directory
== NULL
|| other
.directory
== NULL
69 || strcmp(directory
, other
.directory
) != 0)) {
73 return strcmp(name
, other
.name
) == 0;
78 // #pragma mark - EntryHashDefinition
81 struct FileManager::EntryHashDefinition
{
82 typedef EntryPath KeyType
;
83 typedef LocatableEntry ValueType
;
85 size_t HashKey(const EntryPath
& key
) const
87 return key
.HashValue();
90 size_t Hash(const LocatableEntry
* value
) const
92 return HashKey(EntryPath(value
));
95 bool Compare(const EntryPath
& key
, const LocatableEntry
* value
) const
97 return EntryPath(value
) == key
;
100 LocatableEntry
*& GetLink(LocatableEntry
* value
) const
107 // #pragma mark - Domain
110 class FileManager::Domain
: private LocatableEntryOwner
{
112 Domain(FileManager
* manager
, bool isLocal
)
121 LocatableEntry
* entry
= fEntries
.Clear(true);
122 while (entry
!= NULL
) {
123 LocatableEntry
* next
= entry
->fNext
;
124 entry
->ReleaseReference();
128 while ((entry
= fDeadEntries
.RemoveHead()) != NULL
)
129 entry
->ReleaseReference();
134 status_t error
= fEntries
.Init();
141 LocatableFile
* GetFile(const BString
& directoryPath
,
142 const BString
& relativePath
)
144 if (directoryPath
.Length() == 0 || relativePath
[0] == '/')
145 return GetFile(relativePath
);
146 return GetFile(BString(directoryPath
) << '/' << relativePath
);
149 LocatableFile
* GetFile(const BString
& path
)
151 BString directoryPath
;
153 _SplitPath(path
, directoryPath
, name
);
154 LocatableFile
* file
= _GetFile(directoryPath
, name
);
158 // try to auto-locate the file
159 if (LocatableDirectory
* directory
= file
->Parent()) {
160 if (directory
->State() == LOCATABLE_ENTRY_UNLOCATED
) {
161 // parent not yet located -- try locate with the entry's path
164 _LocateEntry(file
, path
, true, true);
166 // parent already located -- locate the entry in the parent
167 BString locatedDirectoryPath
;
168 if (directory
->GetLocatedPath(locatedDirectoryPath
))
169 _LocateEntryInParentDir(file
, locatedDirectoryPath
, true);
176 void EntryLocated(const BString
& path
, const BString
& locatedPath
)
180 _SplitPath(path
, directory
, name
);
182 LocatableEntry
* entry
= _LookupEntry(EntryPath(directory
, name
));
186 _LocateEntry(entry
, locatedPath
, false, true);
192 return fManager
->Lock();
195 virtual void Unlock()
200 virtual void LocatableEntryUnused(LocatableEntry
* entry
)
202 AutoLocker
<FileManager
> lock(fManager
);
203 if (fEntries
.Lookup(EntryPath(entry
)) == entry
)
204 fEntries
.Remove(entry
);
206 DeadEntryList::Iterator iterator
= fDeadEntries
.GetIterator();
207 while (iterator
.HasNext()) {
208 if (iterator
.Next() == entry
) {
209 fDeadEntries
.Remove(entry
);
215 LocatableDirectory
* parent
= entry
->Parent();
217 parent
->RemoveEntry(entry
);
222 bool _LocateDirectory(LocatableDirectory
* directory
,
223 const BString
& locatedPath
, bool implicit
)
225 if (directory
== NULL
226 || directory
->State() != LOCATABLE_ENTRY_UNLOCATED
) {
230 if (!_LocateEntry(directory
, locatedPath
, implicit
, true))
233 _LocateEntries(directory
, locatedPath
, implicit
);
238 bool _LocateEntry(LocatableEntry
* entry
, const BString
& locatedPath
,
239 bool implicit
, bool locateAncestors
)
241 if (implicit
&& entry
->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY
)
245 if (stat(locatedPath
, &st
) != 0)
248 if (S_ISDIR(st
.st_mode
)) {
249 LocatableDirectory
* directory
250 = dynamic_cast<LocatableDirectory
*>(entry
);
251 if (directory
== NULL
)
253 entry
->SetLocatedPath(locatedPath
, implicit
);
254 } else if (S_ISREG(st
.st_mode
)) {
255 LocatableFile
* file
= dynamic_cast<LocatableFile
*>(entry
);
258 entry
->SetLocatedPath(locatedPath
, implicit
);
261 // locate the ancestor directories, if requested
262 if (locateAncestors
) {
263 BString locatedDirectory
;
265 _SplitPath(locatedPath
, locatedDirectory
, locatedName
);
266 if (locatedName
== entry
->Name())
267 _LocateDirectory(entry
->Parent(), locatedDirectory
, implicit
);
273 bool _LocateEntryInParentDir(LocatableEntry
* entry
,
274 const BString
& locatedDirectoryPath
, bool implicit
)
276 // construct the located entry path
277 BString
locatedEntryPath(locatedDirectoryPath
);
278 int32 pathLength
= locatedEntryPath
.Length();
279 if (pathLength
>= 1 && locatedEntryPath
[pathLength
- 1] != '/')
280 locatedEntryPath
<< '/';
281 locatedEntryPath
<< entry
->Name();
283 return _LocateEntry(entry
, locatedEntryPath
, implicit
, false);
286 void _LocateEntries(LocatableDirectory
* directory
,
287 const BString
& locatedPath
, bool implicit
)
289 for (LocatableEntryList::ConstIterator it
290 = directory
->Entries().GetIterator();
291 LocatableEntry
* entry
= it
.Next();) {
292 if (entry
->State() == LOCATABLE_ENTRY_LOCATED_EXPLICITLY
)
295 if (_LocateEntryInParentDir(entry
, locatedPath
, implicit
)) {
296 // recurse for directories
297 if (LocatableDirectory
* subDir
298 = dynamic_cast<LocatableDirectory
*>(entry
)) {
299 BString locatedEntryPath
;
300 if (subDir
->GetLocatedPath(locatedEntryPath
))
301 _LocateEntries(subDir
, locatedEntryPath
, implicit
);
307 LocatableFile
* _GetFile(const BString
& directoryPath
, const BString
& name
)
309 // if already known return the file
310 LocatableEntry
* entry
= _LookupEntry(EntryPath(directoryPath
, name
));
312 LocatableFile
* file
= dynamic_cast<LocatableFile
*>(entry
);
316 if (file
->AcquireReference() == 0) {
317 fEntries
.Remove(file
);
318 fDeadEntries
.Insert(file
);
323 // no such file yet -- create it
324 BString normalizedDirPath
;
325 _NormalizePath(directoryPath
, normalizedDirPath
);
326 LocatableDirectory
* directory
= _GetDirectory(normalizedDirPath
);
327 if (directory
== NULL
)
330 LocatableFile
* file
= new(std::nothrow
) LocatableFile(this, directory
,
333 directory
->ReleaseReference();
337 directory
->AddEntry(file
);
339 fEntries
.Insert(file
);
343 LocatableDirectory
* _GetDirectory(const BString
& path
)
345 BString directoryPath
;
347 _SplitNormalizedPath(path
, directoryPath
, fileName
);
349 // if already know return the directory
350 LocatableEntry
* entry
351 = _LookupEntry(EntryPath(directoryPath
, fileName
));
353 LocatableDirectory
* directory
354 = dynamic_cast<LocatableDirectory
*>(entry
);
355 if (directory
== NULL
)
357 directory
->AcquireReference();
361 // get the parent directory
362 LocatableDirectory
* parentDirectory
= NULL
;
363 if (directoryPath
.Length() > 0) {
364 parentDirectory
= _GetDirectory(directoryPath
);
365 if (parentDirectory
== NULL
)
369 // create a new directory
370 LocatableDirectory
* directory
= new(std::nothrow
) LocatableDirectory(
371 this, parentDirectory
, path
);
372 if (directory
== NULL
) {
373 parentDirectory
->ReleaseReference();
377 // auto-locate, if possible
380 directory
->GetPath(dirPath
);
381 directory
->SetLocatedPath(dirPath
, false);
382 } else if (parentDirectory
!= NULL
383 && parentDirectory
->State() != LOCATABLE_ENTRY_UNLOCATED
) {
384 BString locatedDirectoryPath
;
385 if (parentDirectory
->GetLocatedPath(locatedDirectoryPath
))
386 _LocateEntryInParentDir(directory
, locatedDirectoryPath
, true);
389 if (parentDirectory
!= NULL
)
390 parentDirectory
->AddEntry(directory
);
392 fEntries
.Insert(directory
);
396 LocatableEntry
* _LookupEntry(const EntryPath
& entryPath
)
398 LocatableEntry
* entry
= fEntries
.Lookup(entryPath
);
402 // if already unreferenced, remove it
403 if (entry
->CountReferences() == 0) {
404 fEntries
.Remove(entry
);
411 void _NormalizePath(const BString
& path
, BString
& _normalizedPath
)
413 BString normalizedPath
;
414 char* buffer
= normalizedPath
.LockBuffer(path
.Length());
416 const char* remaining
= path
.String();
418 while (*remaining
!= '\0') {
419 // collapse repeated slashes
420 if (*remaining
== '/') {
421 buffer
[outIndex
++] = '/';
423 while (*remaining
== '/')
427 if (*remaining
== '\0') {
428 // remove trailing slash (unless it's "/" only)
434 // skip "." components
435 if (*remaining
== '.') {
436 if (remaining
[1] == '\0')
439 if (remaining
[1] == '/') {
441 while (*remaining
== '/')
447 // copy path component
448 while (*remaining
!= '\0' && *remaining
!= '/')
449 buffer
[outIndex
++] = *(remaining
++);
452 // If the path didn't change, use the original path (BString's copy on
453 // write mechanism) rather than the new string.
454 if (outIndex
== path
.Length()) {
455 _normalizedPath
= path
;
457 normalizedPath
.UnlockBuffer(outIndex
);
458 _normalizedPath
= normalizedPath
;
462 void _SplitPath(const BString
& path
, BString
& _directory
, BString
& _name
)
465 _NormalizePath(path
, normalized
);
466 _SplitNormalizedPath(normalized
, _directory
, _name
);
469 void _SplitNormalizedPath(const BString
& path
, BString
& _directory
,
472 // handle single component (including root dir) cases
473 int32 lastSlash
= path
.FindLast('/');
474 if (lastSlash
< 0 || path
.Length() == 1) {
475 _directory
= (const char*)NULL
;
480 // handle root dir + one component and multi component cases
484 _directory
.SetTo(path
, lastSlash
);
485 _name
= path
.String() + (lastSlash
+ 1);
489 FileManager
* fManager
;
490 LocatableEntryTable fEntries
;
491 DeadEntryList fDeadEntries
;
496 // #pragma mark - SourceFileEntry
499 struct FileManager::SourceFileEntry
: public SourceFileOwner
{
501 FileManager
* manager
;
504 SourceFileEntry
* next
;
506 SourceFileEntry(FileManager
* manager
, const BString
& path
)
514 virtual void SourceFileUnused(SourceFile
* sourceFile
)
516 manager
->_SourceFileUnused(this);
519 virtual void SourceFileDeleted(SourceFile
* sourceFile
)
521 // We have already been removed from the table, so commit suicide.
527 // #pragma mark - SourceFileHashDefinition
530 struct FileManager::SourceFileHashDefinition
{
531 typedef BString KeyType
;
532 typedef SourceFileEntry ValueType
;
534 size_t HashKey(const BString
& key
) const
536 return StringUtils::HashValue(key
);
539 size_t Hash(const SourceFileEntry
* value
) const
541 return HashKey(value
->path
);
544 bool Compare(const BString
& key
, const SourceFileEntry
* value
) const
546 return value
->path
== key
;
549 SourceFileEntry
*& GetLink(SourceFileEntry
* value
) const
556 // #pragma mark - FileManager
559 FileManager::FileManager()
561 fLock("file manager"),
569 FileManager::~FileManager()
571 delete fTargetDomain
;
572 delete fSourceDomain
;
574 SourceFileEntry
* entry
= fSourceFiles
->Clear();
575 while (entry
!= NULL
) {
576 SourceFileEntry
* next
= entry
->next
;
585 FileManager::Init(bool targetIsLocal
)
587 status_t error
= fLock
.InitCheck();
591 // create target domain
592 fTargetDomain
= new(std::nothrow
) Domain(this, targetIsLocal
);
593 if (fTargetDomain
== NULL
)
596 error
= fTargetDomain
->Init();
600 // create source domain
601 fSourceDomain
= new(std::nothrow
) Domain(this, false);
602 if (fSourceDomain
== NULL
)
605 error
= fSourceDomain
->Init();
609 // create source file table
610 fSourceFiles
= new(std::nothrow
) SourceFileTable
;
611 if (fSourceFiles
== NULL
)
614 error
= fSourceFiles
->Init();
623 FileManager::GetTargetFile(const BString
& directory
,
624 const BString
& relativePath
)
626 AutoLocker
<FileManager
> locker(this);
627 return fTargetDomain
->GetFile(directory
, relativePath
);
632 FileManager::GetTargetFile(const BString
& path
)
634 AutoLocker
<FileManager
> locker(this);
635 return fTargetDomain
->GetFile(path
);
640 FileManager::TargetEntryLocated(const BString
& path
,
641 const BString
& locatedPath
)
643 AutoLocker
<FileManager
> locker(this);
644 fTargetDomain
->EntryLocated(path
, locatedPath
);
649 FileManager::GetSourceFile(const BString
& directory
,
650 const BString
& relativePath
)
652 AutoLocker
<FileManager
> locker(this);
653 LocatableFile
* file
= fSourceDomain
->GetFile(directory
, relativePath
);
660 FileManager::GetSourceFile(const BString
& path
)
662 AutoLocker
<FileManager
> locker(this);
663 LocatableFile
* file
= fSourceDomain
->GetFile(path
);
670 FileManager::SourceEntryLocated(const BString
& path
,
671 const BString
& locatedPath
)
673 AutoLocker
<FileManager
> locker(this);
675 // check if we already have this path mapped. If so,
676 // first clear the mapping, as the user may be attempting
677 // to correct an existing entry.
678 SourceFileEntry
* entry
= _LookupSourceFile(path
);
680 _SourceFileUnused(entry
);
682 fSourceDomain
->EntryLocated(path
, locatedPath
);
685 fSourceLocationMappings
[path
] = locatedPath
;
695 FileManager::LoadSourceFile(LocatableFile
* file
, SourceFile
*& _sourceFile
)
697 AutoLocker
<FileManager
> locker(this);
701 BString originalPath
;
702 file
->GetPath(originalPath
);
703 if (!file
->GetLocatedPath(path
)) {
704 // see if this is a file we have a lazy mapping for.
705 if (!_LocateFileIfMapped(originalPath
, file
)
706 || !file
->GetLocatedPath(path
)) {
707 return B_ENTRY_NOT_FOUND
;
711 // we might already know the source file
712 SourceFileEntry
* entry
= _LookupSourceFile(originalPath
);
714 entry
->file
->AcquireReference();
715 _sourceFile
= entry
->file
;
719 // create the hash table entry
720 entry
= new(std::nothrow
) SourceFileEntry(this, originalPath
);
725 SourceFile
* sourceFile
= new(std::nothrow
) SourceFile(entry
);
726 if (sourceFile
== NULL
) {
730 ObjectDeleter
<SourceFile
> sourceFileDeleter(sourceFile
);
732 entry
->file
= sourceFile
;
734 status_t error
= sourceFile
->Init(path
);
738 fSourceFiles
->Insert(entry
);
740 _sourceFile
= sourceFileDeleter
.Detach();
746 FileManager::LoadLocationMappings(TeamFileManagerSettings
* settings
)
748 AutoLocker
<FileManager
> locker(this);
749 for (int32 i
= 0; i
< settings
->CountSourceMappings(); i
++) {
753 if (settings
->GetSourceMappingAt(i
, sourcePath
, locatedPath
) != B_OK
)
757 fSourceLocationMappings
[sourcePath
] = locatedPath
;
768 FileManager::SaveLocationMappings(TeamFileManagerSettings
* settings
)
770 AutoLocker
<FileManager
> locker(this);
772 for (LocatedFileMap::const_iterator it
= fSourceLocationMappings
.begin();
773 it
!= fSourceLocationMappings
.end(); ++it
) {
774 status_t error
= settings
->AddSourceMapping(it
->first
, it
->second
);
783 FileManager::SourceFileEntry
*
784 FileManager::_LookupSourceFile(const BString
& path
)
786 SourceFileEntry
* entry
= fSourceFiles
->Lookup(path
);
790 // the entry might be unused already -- in that case remove it
791 if (entry
->file
->CountReferences() == 0) {
792 fSourceFiles
->Remove(entry
);
801 FileManager::_SourceFileUnused(SourceFileEntry
* entry
)
803 AutoLocker
<FileManager
> locker(this);
805 SourceFileEntry
* otherEntry
= fSourceFiles
->Lookup(entry
->path
);
806 if (otherEntry
== entry
)
807 fSourceFiles
->Remove(entry
);
812 FileManager::_LocateFileIfMapped(const BString
& sourcePath
,
815 // called with lock held
817 LocatedFileMap::const_iterator it
= fSourceLocationMappings
.find(
819 if (it
!= fSourceLocationMappings
.end()
820 && file
->State() != LOCATABLE_ENTRY_LOCATED_EXPLICITLY
821 && file
->State() != LOCATABLE_ENTRY_LOCATED_IMPLICITLY
) {
822 fSourceDomain
->EntryLocated(it
->first
, it
->second
);