2 * Copyright 2013, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold <ingo_weinhold@gmx.de>
10 #include <MergedDirectory.h>
18 #include <Directory.h>
21 #include <AutoDeleter.h>
23 #include "storage_support.h"
26 struct BMergedDirectory::EntryNameSet
: std::set
<std::string
> {
30 BMergedDirectory::BMergedDirectory(BPolicy policy
)
33 fDirectories(10, true),
41 BMergedDirectory::~BMergedDirectory()
43 delete fVisitedEntries
;
48 BMergedDirectory::Init()
50 delete fVisitedEntries
;
51 fDirectories
.MakeEmpty(true);
53 fVisitedEntries
= new(std::nothrow
) EntryNameSet
;
54 return fVisitedEntries
!= NULL
? B_OK
: B_NO_MEMORY
;
58 BMergedDirectory::BPolicy
59 BMergedDirectory::Policy() const
66 BMergedDirectory::SetPolicy(BPolicy policy
)
73 BMergedDirectory::AddDirectory(BDirectory
* directory
)
75 return fDirectories
.AddItem(directory
) ? B_OK
: B_NO_MEMORY
;
80 BMergedDirectory::AddDirectory(const char* path
)
82 BDirectory
* directory
= new(std::nothrow
) BDirectory(path
);
83 if (directory
== NULL
)
85 ObjectDeleter
<BDirectory
> directoryDeleter(directory
);
87 if (directory
->InitCheck() != B_OK
)
88 return directory
->InitCheck();
90 status_t error
= AddDirectory(directory
);
93 directoryDeleter
.Detach();
100 BMergedDirectory::GetNextEntry(BEntry
* entry
, bool traverse
)
103 status_t error
= GetNextRef(&ref
);
107 return entry
->SetTo(&ref
, traverse
);
112 BMergedDirectory::GetNextRef(entry_ref
* ref
)
114 BPrivate::Storage::LongDirEntry entry
;
115 int32 result
= GetNextDirents(&entry
, sizeof(entry
), 1);
119 return B_ENTRY_NOT_FOUND
;
121 ref
->device
= entry
.d_pdev
;
122 ref
->directory
= entry
.d_pino
;
123 return ref
->set_name(entry
.d_name
);
128 BMergedDirectory::GetNextDirents(struct dirent
* direntBuffer
, size_t bufferSize
,
134 while (fDirectoryIndex
< fDirectories
.CountItems()) {
135 int32 count
= fDirectories
.ItemAt(fDirectoryIndex
)->GetNextDirents(
136 direntBuffer
, bufferSize
, 1);
144 if (strcmp(direntBuffer
->d_name
, ".") == 0
145 || strcmp(direntBuffer
->d_name
, "..") == 0) {
150 case B_ALLOW_DUPLICATES
:
155 if (fVisitedEntries
!= NULL
156 && fVisitedEntries
->find(direntBuffer
->d_name
)
157 != fVisitedEntries
->end()) {
161 if (fVisitedEntries
!= NULL
) {
163 fVisitedEntries
->insert(direntBuffer
->d_name
);
164 } catch (std::bad_alloc
&) {
169 if (fPolicy
== B_COMPARE
)
170 _FindBestEntry(direntBuffer
);
180 BMergedDirectory::Rewind()
182 for (int32 i
= 0; BDirectory
* directory
= fDirectories
.ItemAt(i
); i
++)
185 if (fVisitedEntries
!= NULL
)
186 fVisitedEntries
->clear();
194 BMergedDirectory::CountEntries()
197 char buffer
[sizeof(dirent
) + B_FILE_NAME_LENGTH
];
198 while (GetNextDirents((dirent
*)&buffer
, sizeof(buffer
), 1) == 1)
205 BMergedDirectory::ShallPreferFirstEntry(const entry_ref
& entry1
, int32 index1
,
206 const entry_ref
& entry2
, int32 index2
)
208 // That's basically B_ALWAYS_FIRST semantics. A derived class will implement
209 // the desired semantics.
215 BMergedDirectory::_FindBestEntry(dirent
* direntBuffer
)
217 entry_ref
bestEntry(direntBuffer
->d_pdev
, direntBuffer
->d_pino
,
218 direntBuffer
->d_name
);
219 if (bestEntry
.name
== NULL
)
221 int32 bestIndex
= fDirectoryIndex
;
223 int32 directoryCount
= fDirectories
.CountItems();
224 for (int32 i
= fDirectoryIndex
+ 1; i
< directoryCount
; i
++) {
225 BEntry
entry(fDirectories
.ItemAt(i
), bestEntry
.name
);
228 if (entry
.GetStat(&st
) == B_OK
&& entry
.GetRef(&ref
) == B_OK
229 && !ShallPreferFirstEntry(bestEntry
, bestIndex
, ref
, i
)) {
230 direntBuffer
->d_pdev
= ref
.device
;
231 direntBuffer
->d_pino
= ref
.directory
;
232 direntBuffer
->d_dev
= st
.st_dev
;
233 direntBuffer
->d_ino
= st
.st_ino
;
234 bestEntry
.device
= ref
.device
;
235 bestEntry
.directory
= ref
.directory
;