diff --git a/headers/private/storage/MergedDirectory.h b/headers/private/storage/MergedDirectory.h new file mode 100644 index 0000000000..cf020bae60 --- /dev/null +++ b/headers/private/storage/MergedDirectory.h @@ -0,0 +1,72 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ +#ifndef _MERGED_DIRECTORY_H +#define _MERGED_DIRECTORY_H + + +#include +#include + + +class BDirectory; + + +class BMergedDirectory : public BEntryList { +public: + // policy how to handle equally named entries in different + // directories + enum BPolicy { + B_ALLOW_DUPLICATES, + B_ALWAYS_FIRST, + B_COMPARE + }; + +public: + BMergedDirectory( + BPolicy policy = B_ALWAYS_FIRST); + virtual ~BMergedDirectory(); + + status_t Init(); + + BPolicy Policy() const; + void SetPolicy(BPolicy policy); + + status_t AddDirectory(BDirectory* directory); + status_t AddDirectory(const char* path); + + virtual status_t GetNextEntry(BEntry* entry, + bool traverse = false); + virtual status_t GetNextRef(entry_ref* ref); + virtual int32 GetNextDirents(struct dirent* direntBuffer, + size_t bufferSize, + int32 maxEntries = INT_MAX); + virtual status_t Rewind(); + virtual int32 CountEntries(); + +protected: + virtual bool ShallPreferFirstEntry(const entry_ref& entry1, + int32 index1, const entry_ref& entry2, + int32 index2); + // always invoked with index1 < index2 + +private: + typedef BObjectList DirectoryList; + struct EntryNameSet; + +private: + void _FindBestEntry(dirent* direntBuffer); + +private: + DirectoryList fDirectories; + BPolicy fPolicy; + int32 fDirectoryIndex; + EntryNameSet* fVisitedEntries; +}; + + +#endif // _MERGED_DIRECTORY_H diff --git a/src/kits/storage/Jamfile b/src/kits/storage/Jamfile index ea3410946c..41c7f7e44e 100644 --- a/src/kits/storage/Jamfile +++ b/src/kits/storage/Jamfile @@ -22,6 +22,7 @@ MergeObject storage_kit.o : FileDescriptorIO.cpp FileIO.cpp FindDirectory.cpp + MergedDirectory.cpp Mime.cpp MimeType.cpp Node.cpp diff --git a/src/kits/storage/MergedDirectory.cpp b/src/kits/storage/MergedDirectory.cpp new file mode 100644 index 0000000000..2ec338f9b9 --- /dev/null +++ b/src/kits/storage/MergedDirectory.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "storage_support.h" + + +struct BMergedDirectory::EntryNameSet : std::set { +}; + + +BMergedDirectory::BMergedDirectory(BPolicy policy) + : + BEntryList(), + fDirectories(10, true), + fPolicy(policy), + fDirectoryIndex(0), + fVisitedEntries(NULL) +{ +} + + +BMergedDirectory::~BMergedDirectory() +{ + delete fVisitedEntries; +} + + +status_t +BMergedDirectory::Init() +{ + delete fVisitedEntries; + fDirectories.MakeEmpty(true); + + fVisitedEntries = new(std::nothrow) EntryNameSet; + return fVisitedEntries != NULL ? B_OK : B_NO_MEMORY; +} + + +BMergedDirectory::BPolicy +BMergedDirectory::Policy() const +{ + return fPolicy; +} + + +void +BMergedDirectory::SetPolicy(BPolicy policy) +{ + fPolicy = policy; +} + + +status_t +BMergedDirectory::AddDirectory(BDirectory* directory) +{ + return fDirectories.AddItem(directory) ? B_OK : B_NO_MEMORY; +} + + +status_t +BMergedDirectory::AddDirectory(const char* path) +{ + BDirectory* directory = new(std::nothrow) BDirectory(path); + if (directory == NULL) + return B_NO_MEMORY; + ObjectDeleter directoryDeleter(directory); + + if (directory->InitCheck() != B_OK) + return directory->InitCheck(); + + status_t error = AddDirectory(directory); + if (error != B_OK) + return error; + directoryDeleter.Detach(); + + return B_OK; +} + + +status_t +BMergedDirectory::GetNextEntry(BEntry* entry, bool traverse) +{ + entry_ref ref; + status_t error = GetNextRef(&ref); + if (error != B_OK) + return error; + + return entry->SetTo(&ref, traverse); +} + + +status_t +BMergedDirectory::GetNextRef(entry_ref* ref) +{ + BPrivate::Storage::LongDirEntry entry; + int32 result = GetNextDirents(&entry, sizeof(entry), 1); + if (result < 0) + return result; + if (result == 0) + return B_ENTRY_NOT_FOUND; + + ref->device = entry.d_pdev; + ref->directory = entry.d_pino; + return ref->set_name(entry.d_name); +} + + +int32 +BMergedDirectory::GetNextDirents(struct dirent* direntBuffer, size_t bufferSize, + int32 maxEntries) +{ + if (maxEntries <= 0) + return B_BAD_VALUE; + + while (fDirectoryIndex < fDirectories.CountItems()) { + int32 count = fDirectories.ItemAt(fDirectoryIndex)->GetNextDirents( + direntBuffer, bufferSize, 1); + if (count < 0) + return count; + if (count == 0) { + fDirectoryIndex++; + continue; + } + + if (strcmp(direntBuffer->d_name, ".") == 0 + || strcmp(direntBuffer->d_name, "..") == 0) { + continue; + } + + switch (fPolicy) { + case B_ALLOW_DUPLICATES: + return count; + + case B_ALWAYS_FIRST: + case B_COMPARE: + if (fVisitedEntries != NULL + && fVisitedEntries->find(direntBuffer->d_name) + != fVisitedEntries->end()) { + continue; + } + + if (fVisitedEntries != NULL) { + try { + fVisitedEntries->insert(direntBuffer->d_name); + } catch (std::bad_alloc&) { + return B_NO_MEMORY; + } + } + + if (fPolicy == B_COMPARE) + _FindBestEntry(direntBuffer); + return 1; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +status_t +BMergedDirectory::Rewind() +{ + for (int32 i = 0; BDirectory* directory = fDirectories.ItemAt(i); i++) + directory->Rewind(); + + if (fVisitedEntries != NULL) + fVisitedEntries->clear(); + + fDirectoryIndex = 0; + return B_OK; +} + + +int32 +BMergedDirectory::CountEntries() +{ + int32 count = 0; + char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; + while (GetNextDirents((dirent*)&buffer, sizeof(buffer), 1) == 1) + count++; + return count; +} + + +bool +BMergedDirectory::ShallPreferFirstEntry(const entry_ref& entry1, int32 index1, + const entry_ref& entry2, int32 index2) +{ + // That's basically B_ALWAYS_FIRST semantics. A derived class will implement + // the desired semantics. + return true; +} + + +void +BMergedDirectory::_FindBestEntry(dirent* direntBuffer) +{ + entry_ref bestEntry(direntBuffer->d_pdev, direntBuffer->d_pino, + direntBuffer->d_name); + if (bestEntry.name == NULL) + return; + int32 bestIndex = fDirectoryIndex; + + int32 directoryCount = fDirectories.CountItems(); + for (int32 i = fDirectoryIndex + 1; i < directoryCount; i++) { + BEntry entry(fDirectories.ItemAt(i), bestEntry.name); + struct stat st; + entry_ref ref; + if (entry.GetStat(&st) == B_OK && entry.GetRef(&ref) == B_OK + && !ShallPreferFirstEntry(bestEntry, bestIndex, ref, i)) { + direntBuffer->d_pdev = ref.device; + direntBuffer->d_pino = ref.directory; + direntBuffer->d_dev = st.st_dev; + direntBuffer->d_ino = st.st_ino; + bestEntry.device = ref.device; + bestEntry.directory = ref.directory; + bestIndex = i; + } + } +}